Reputation: 128
In the following code I define two interfaces, the second of which takes the first as a type parameter. However the code gives the error "type parameter 'a' is not defined".
type IFirst<'a> =
abstract Data : 'a
type ISecond<'First when 'First :> IFirst<'a>> =
abstract First : 'First
abstract SomeData : 'a
My question is why can't f# infer what type 'a' is when ISecond is derived, since the information is embedded in 'First'? For example in the following code the compiler could infer that 'a' is a string.
type First () =
interface IFirst<string> with
member x.Data = ""
type Second () =
interface ISecond<First> with
member x.SomeData = ""
member x.First = First()
Is there any way around this or does ISecond have to take two type parameters?
EDIT: I Am aware that ISecond can take two type parameters (note the last line of my initial question). To make it clearer what I mean consider the following code
type IFirst<'a> = interface end
type ISecond<'First, 'a when 'First :> IFirst<'a>> = interface end
type First () =
interface IFirst<string>
type Second () =
interface ISecond<First, int>
It gives the error "This expression was expected to have type string but here has type int", meaning the compiler knows that 'a' is a string, yet I still have to declare it as such. I wish to know why this is the case and whether there is a workaround without specifying the second type parameter.
Upvotes: 3
Views: 365
Reputation: 55184
I think you have conflated two different questions. One is:
Do a type definition's generic parameters all need to be explicit?
The answer is yes. This has nothing to do with type inference, it's just how type definitions work in F# - the type parameter 'a
will only be in scope to be used as an argument to IFirst<_>
if it's also a parameter of ISecond
.
The other question is:
Can the compiler infer a super-type's type parameters when defining a subtype?
Here, the answer is slightly more subtle. When defining a class type, the answer is that it is a syntactic requirement that you specify all of the type parameters. If you try something like:
type Second() = interface ISecond<First,_> with ...
you'll get the error message
error FS0715: Anonymous type variables are not permitted in this declaration
However, there are other contexts where the parameters can be inferred without issue:
let second() = { new ISecond<_,_> with
member x.SomeData = ""
member x.First = First() }
Here you can see that when using an object expression both of the parameters can be inferred.
Upvotes: 3
Reputation: 80714
Your definition of ISecond
is incorrect: you're mentioning some type 'a
, but not defining it. In other words, ISecond
actually has two generic parameters - 'First
and 'a
, but you've only defined one of them.
This would work:
type ISecond<'a, 'First when 'First :> IFirst<'a>> =
abstract First : 'First
abstract SomeData : 'a
But then, of course, you'll need to amend your definition of Second
as well:
type Second () =
interface ISecond<string, First> with
member x.SomeData = ""
member x.First = First()
Upvotes: 4