【问题标题】:Why can't the types be inferred for nested functions为什么不能为嵌套函数推断类型
【发布时间】:2016-02-11 20:14:38
【问题描述】:

我不了解嵌套函数的 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

【问题讨论】:

标签: types f# type-inference


【解决方案1】:

基本上类型推断是从上到下从左到右进行的。有很多例外,我就不一一赘述了。

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

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

这得益于 |&gt; 的使用 失败了

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 询问了有关将多个源代码传递给 改进类型推断过程。他的回复是

"是的,可以进行多遍类型推断。还有 生成一组有限约束的单程变化。

然而,这些方法往往会给出糟糕的错误信息,而且效果不佳 智能感知生成可视化编辑器。”

【讨论】:

  • 感谢类型分解。我假设唯一可以调用我的函数printType() 的是它定义的范围内的代码。因此我不明白它不能推断出唯一的用法是MethodInfo[]
  • let t = int.GetType()printType t 位于 let methods = t.GetMethods() |&gt; Seq.map(fun m -&gt; m.Name) 之后,因此推理引擎在到达 t.GetMethods 时并不知道它们。 Type Inference 上有很多论文,如果有人谈论您的目标,我不会感到惊讶,但 F# 出于特定原因选择了 Hindley-Milner 的实现。
  • @CarloV.Dango 如果是嵌套函数或谁可以调用它,则无关。 F# 尝试尽可能通用,printType 是一个只需要一个参数t 的函数。您唯一要做的就是在其上调用方法,而仅通过方法调用无法安全地推断出类型。因为每个类都可以实现该方法。因此,您会收到一个错误,此时无法确定类型。这是一般限制,如果您将getType 提取到它自己的函数非嵌套函数中,也会发生这种情况。
  • @SidBurn 类型系统根据使用情况推断类型。因此,查看内部函数并查看它们的调用站点并从作为参数传递给它们的内容推断类型是非常有意义的。就像在第一个示例中那样内联内部函数的代码时所做的那样
  • @CarloV.Dango 类型系统不会根据其用法推断对象类型,因为这里无法推断任何内容。因为我已经解释过你不能基于方法调用来推断一个类,因为可以存在数百万个具有这种方法的类。推断对象的类型是不可能的。唯一接近的就是(静态)类型的鸭子打字。因此,每个实现被调用方法的类都将被接受。但 F# 不会自动执行类似的操作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-11
相关资源
最近更新 更多