我不明白为什么以下代码无法编译
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)
如果你看一下2“creator ...”函数 - 它们都会给出编译错误..
这个表达式的类型应该是int ...但是这里有 输入字符串
我可以看到F#推断getAsParam方法的返回类型是int,因为它是它遇到的第一个。但是,为什么它不会决定使用泛型返回类型?
正如你所看到的,我试图在creatorWithTypeAnnotation方法中使用函数签名 - 但这没有任何影响。
我很难过!我如何强制这一点来识别getAsParam函数应该返回一个泛型?
答案 0 :(得分:2)
这是一个快速的F#-interactive会话,通过使用接口来表示我对作弊的意思(它只需要某种member
- 所以它可以是一个方法当然也是一个班级):
> 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)
您可能会发现实现IParam
的某些 saner 实例并不容易;)
答案 1 :(得分:1)
正如Carsten所暗示的那样,类型变量都在编译时解析。如果将getAsParam
定义为string -> 'a
,则不会阻止'a
在编译时被解析。因为它无法在编译时解析为两种不同的类型,所以编译失败。
考虑你的例子:
let creatorWithTypeAnnotation (getAsParam:string->'a) =
let apple = {
name = getAsParam "some-apple"
}
let dog = {
size = getAsParam "some-dog"
}
这也可以这样声明(我将使用'A
而不是'a
,因为预期的约定是小写类型变量用于编译推断类型):
let creatorWithTypeAnnotation<'A> (getAsParam:string->'A) =
let apple = {
name = getAsParam "some-apple"
}
let dog = {
size = getAsParam "some-dog"
}
现在应该清楚为什么代码不能编译。