3

考虑以下 F#:-

type TestClass() =
    let getValFromMap m k = Map.find k m

    let addToMap map k i = map |> Map.add k i

    let mutable someMap : Map<string,int> = Map.empty

    let getValFromMapPartial key = getValFromMap someMap key
    let getValFromMapPartialAndTacit = getValFromMap someMap

    member this.AddThenGet() =
        someMap <- addToMap someMap "A" 10

        let value = getValFromMapPartial "A"
        printfn "Value from partial = %i" value   // prints out

        let value = getValFromMapPartialAndTacit "A"  // throws
        printfn "Value from partial and tacit = %i" value

[<EntryPoint>]
let main argv = 
    let test = TestClass()
    test.AddThenGet()
    0

在我看来,功能getValFromMapPartial和是相同的。getValFromMapPartialAndTacitF# 说它们具有完全相同的类型:(string -> int). 然而它们的行为非常不同,它们的编译方式也非常不同。使用 dotPeek 进行反编译,我看到这getValFromMapPartial是一个方法,而getValFromMapPartialAndTacit它是一个在 ctor 中初始化的字段。

F# 不会抱怨getValFromMapPartialAndTacit,即使在最高警告级别(在 VS 2012 和 2013 中)。然而在我上面的示例中调用这个函数失败了,大概是因为它已经包装了初始的空版本someMap,尽管它是可变的。

为什么这两个功能有区别?F# 是否应该警告默认/无点版本可能会失败?

4

2 回答 2

3

F# 编译器区分具有参数的函数的 let 绑定和没有参数

  • 值定义:绑定likelet a = ...是一个值定义。它的主体被热切地评估,“它在哪里”,然后再评估代码后面的任何内容。

  • 函数定义:绑定likelet f x = ...是一个语法函数定义,其内容在函数被调用时进行评估。

由于someMap指的是可变变量,因此在函数定义中使用此变量意味着在调用函数时从变量中读取。但是, in 的用法是在声明getValFromMapPartialAndTacit时读取值。

此行为不会阻止值成为函数。您也可以编写let f = fun x -> ...来声明一个函数,并...再次成为函数定义的一部分。但是,如果您要在=和之间添加定义fun,它们将在 的定义处进行评估,而f不是在调用时进行评估。


在问题的评论中,someMap作为可变参考单元格也会出现同样的问题。这是同样的问题。该函数由 Andrew 为可变参考单元格重写:

let getValFromMapPartialAndTacit = getValFromMap !someMap

这里,解引用操作符(!)是在绑定值时应用的,而不是在调用函数时应用的。它相当于:

let mapRightNow = !someMap
let getValFromMapPartialAndTacit = getValFromMap mapRightNow
于 2014-11-20T17:21:20.580 回答
2

getValFromMapPartial是一个真正的句法函数。它的签名是val getValFromMapPartial : key:string -> int. 每当调用它时,它都会使用 的当前值someMap。这就是它在您的示例中起作用的原因;它访问someMap谁有条目的版本。

另一方面,getValFromMapPartialAndTacit是一个lambda 计算函数。它的签名是val getValFromMapPartialAndTacit : (string -> int)(注意括号)。lambda 有一个编译器生成的闭包,其中包含someMap计算 lambda 时的版本。这就是为什么它在您的示例中不起作用的原因;它总是访问相同的原始版本的someMap谁没有条目。

于 2014-11-20T16:10:46.847 回答