Reputation: 166
I do not understand why the following code does not compile
module GenericsTest =
open System
type Dog = {
name:string
}
type Apple = {
size:int
}
let get<'a> (id:string) =
Activator.CreateInstance<'a>()
let creatorInferred getAsParam =
let apple = {
name = getAsParam "some-apple"
}
let dog = {
size = getAsParam "some-dog"
}
(apple, dog)
let creatorWithTypeAnnotation (getAsParam:string->'a) =
let apple = {
name = getAsParam "some-apple"
}
let dog = {
size = getAsParam "some-dog"
}
(apple, dog)
If you look at the 2 "creator..." function - both of them give the compile error..
this expression was expected to have the type int... but here has the type string
I can see that F# is infering the return type of the getAsParam method to be int, because it is the first one that it encounters. However, why does it not then decide to use a generic return type?
As you can see, i have tried to for the function signature in the creatorWithTypeAnnotation method - but this has no affect.
I'm stumped! How do i force this to recognise that the getAsParam function should return a generic?
Upvotes: 0
Views: 320
Reputation: 43036
As Carsten implies, the type variables are all resolved at compile time. If you define getAsParam
as string -> 'a
, that doesn't stop the 'a
from being resolved at compile time. Because it cannot resolve at compile time to two different types, compilation fails.
Consider your example:
let creatorWithTypeAnnotation (getAsParam:string->'a) =
let apple = {
name = getAsParam "some-apple"
}
let dog = {
size = getAsParam "some-dog"
}
This could also be declared thus (I'll use 'A
instead of 'a
, since the intended convention is that lower-case type variables are for compiler-inferred types):
let creatorWithTypeAnnotation<'A> (getAsParam:string->'A) =
let apple = {
name = getAsParam "some-apple"
}
let dog = {
size = getAsParam "some-dog"
}
Now it should be clear why the code does not compile.
Upvotes: 1
Reputation: 52270
here is a quick F#-interactive session on what I meant with cheat by using an interface (it just have to be some sort of member
- so it can be a method on a class too of course):
> type IParam = abstract getParam : string -> 'a;;
type IParam =
interface
abstract member getParam : string -> 'a
end
> let createWith (p : IParam) : int*bool = (p.getParam "a", p.getParam "b");;
val createWith : p:IParam -> int * bool
> let test = { new IParam with member __.getParam s = Unchecked.defaultof<_> };;
val test : IParam
> createWith test;;
val it : int * bool = (0, false)
you might find it not exactly easy to implement some saner instances of IParam
though ;)
Upvotes: 2