为什么此无点F#函数的行为与非无点版本不同? [英] Why does this point-free F# function behave differently from the non-point-free version?
问题描述
请考虑以下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
和getValFromMapPartialAndTacit
是相同的. F#表示它们具有完全相同的类型:(string -> int)
.但是它们的行为截然不同,并且它们的编译也截然不同.使用dotPeek进行反编译,我看到getValFromMapPartial
是一种方法,而getValFromMapPartialAndTacit
是在ctor中初始化的字段.
Functions getValFromMapPartial
and getValFromMapPartialAndTacit
are, to my mind, identical. F# says they have the exact same type: (string -> int)
. And yet they behave very differently, and they are compiled very differently. Decompiling using dotPeek, I see that getValFromMapPartial
is a method, whereas getValFromMapPartialAndTacit
is a field that is initialized in the ctor.
F#也不会抱怨getValFromMapPartialAndTacit
.但是,在上面的示例中调用此函数失败了,可能是因为尽管它具有可变性,但它包装了someMap
的初始空白版本.
F# does not complain about getValFromMapPartialAndTacit
, even on the highest warning level (both in VS 2012 and 2013). And yet calling this function in my sample above fails, presumably because it has wrapped the initial, empty version of the someMap
, despite its mutability.
这两个功能为什么有区别? F#是否应该警告默认/无点版本可能会失败?
Why is there a difference between these two functions? Should there be a warning from F# that the tacit / point-free version might fail?
推荐答案
F#编译器区分具有参数的函数和 values 的let绑定.没有参数.
The F# compiler distinguishes between let-bindings of functions, which have parameters, and values, which do not have parameters.
-
值定义:像
let a = ...
这样的绑定是值定义.在对代码下方的任何内容进行评估之前,都会急切地对其主体进行所在位置"评估.
Value definition: A binding like
let a = ...
is a value definition. Its body is evaluated eagerly, "where it is", before the evaluation of anything further down the code.
函数定义:像let f x = ...
这样的绑定是一种语法函数定义,在调用该函数时对其内容进行评估.
Function definition: A binding like let f x = ...
is a syntactic function definition, the contents of which are evaluated when the function is called.
由于someMap
是指可变变量,因此在函数定义中使用此变量意味着在调用函数时从变量中读取.但是,getValFromMapPartialAndTacit
中的用法在声明时读取值 .
Since someMap
refers to a mutable variable, using this variable inside a function definition means reading from the variable when the function is called. However, the usage in getValFromMapPartialAndTacit
reads the value at the moment of declaration.
此行为不会阻止值成为函数.您也可以编写let f = fun x -> ...
来声明一个函数,而...
将再次成为函数定义的一部分.但是,如果要在=
和fun
之间添加定义,则将在f
定义时对它们进行评估,而不是在调用它时进行评估.
This behavior does not stop a value from being a function. You could just as well write let f = fun x -> ...
to declare a function, and ...
would again be part of a function definition. However, if you were to add definitions in between the =
and fun
, they would be evaluated at the point of the definition of f
, not when it is called.
在问题的注释中,someMap
是可变的参考单元,也会出现相同的问题.这是同样的问题.该功能,由安德鲁(Andrew)重写,用于可变的参考单元格:
In the question's comments, the same problem occurs with someMap
being a mutable reference cell. This is the same problem. The function, as rewritten by Andrew for a mutable reference cell:
let getValFromMapPartialAndTacit = getValFromMap !someMap
此处,在绑定值时(而不是在调用函数时)应用取消引用运算符(!)
.它等效于:
Here, the dereference operator (!)
is applied when the value is bound, not when the function is called. it is equivalent to:
let mapRightNow = !someMap
let getValFromMapPartialAndTacit = getValFromMap mapRightNow
这篇关于为什么此无点F#函数的行为与非无点版本不同?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!