为什么不能为嵌套函数推断类型

时间:2016-02-11 20:14:39

标签: types f# type-inference

我不了解嵌套函数的F#类型推理系统。当我使用简单类型之外的类型(如int,string,...

)时,它似乎特别破碎

这是一些打印一些反射信息的代码的小例子

let inferenceTest () =
    let t = int.GetType()
    let methods = t.GetMethods() |> Seq.map(fun m -> m.Name)
    printfn "%s" <| String.concat ", " methods

这很好用!不需要铸造等。现在假设打印涉及更多,因此我们希望将其分离为嵌套函数

let inferenceTestFailsToCompile () =
    let printType t =
        let methods = t.GetMethods() |> Seq.map(fun m -> m.Name)
        printfn "%s" <| String.concat ", " methods

    let t = int.GetType()
    printType t    

这失败了“基于此程序点之前的信息查找不确定类型的对象。可能需要类型注释......”

为什么类型系统的信息突然减少?如果我的printType()函数与inferenceTestFailsToCompile()

的范围相同,也许我能理解这个问题

当我改为创建一个以t作为闭包的lambda时,输入问题就消失了

let inferenceTestLambda () =
    let t = int.GetType()
    let printType =
        let methods = t.GetMethods() |> Seq.map(fun m -> m.Name)
        printfn "%s" <| String.concat ", " methods
    printType

1 个答案:

答案 0 :(得分:5)

基本上,从左到右从上到下进行类型推断。有很多例外,但我不会进入它们。

在第一个示例中,推理引擎有足够的信息来正确推断类型。

let inferenceTest () =
    let t = int.GetType()
    let methods = t.GetMethods() |> Seq.map(fun m -> m.Name)
    printfn "%s" <| String.concat ", " methods

成了

let inferenceTest () =
    let (t : type) = int.GetType()
    let methods = t.GetMethods() |> Seq.map(fun m -> m.Name)
    printfn "%s" <| String.concat ", " methods

成了

let inferenceTest () =
    let (t : type) = int.GetType()
    let methods = (t.GetMethods() : System.Reflection.MethodInfo []) |> Seq.map(fun m -> m.Name)
    printfn "%s" <| String.concat ", " methods

成了

let inferenceTest () =
    let (t : type) = int.GetType()
    let (methods : seq<string>) = (t.GetMethods() : System.Reflection.MethodInfo []) |> Seq.map(fun m -> m.Name)
    printfn "%s" <| String.concat ", " methods

使用|>帮助了

失败
let methods = Seq.map (fun m -> m.Name) (t.GetMethods())

在第二个例子中,这一行被推断为。

let printType (t : 'a) () = 

导致错误

let methods = t.GetMethods() |> Seq.map(fun m -> m.Name)

因为t的类型是通用的,并且没有提供足够的信息供t.GetMethods()使用。

要解决这些问题,我使用Visual Studio并将鼠标移到变量上以查看类型。然后,如果我发现一个不正确的类型,我开始添加类型定义。这通常会导致修复错误或揭露我的代码中的错误。

编辑:

这是来自Why is F#'s type inference so fickle?

Robert Harvey答案的一部分
  

F#使用一次传递编译,以便您只能引用类型或   您之前在文件中定义的函数   当前位于或出现在之前指定的文件中   编译顺序。

     

我最近问Don Syme有关多个源传递的问题   改进类型推断过程。他的回答是

     

“是的,可以进行多次通过类型推断。还有   产生一组有限约束的单程变化。

     

然而,这些方法倾向于提供错误的错误消息而且很差   intellisense会产生一个可视化编辑器。“