FSharp:使用泛型类型推断

时间:2015-05-22 16:15:57

标签: generics f# type-inference

我不明白为什么以下代码无法编译

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函数应该返回一个泛型?

2 个答案:

答案 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"
    }

现在应该清楚为什么代码不能编译。