Amir Vakili
Amir Vakili

Reputation: 128

Nested Generic Type Inference in f#

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

Answers (2)

kvb
kvb

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

Fyodor Soikin
Fyodor Soikin

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

Related Questions