Why does this F # dot function behave differently than the dotless version?
Consider the following 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
Functions getValFromMapPartial
and getValFromMapPartialAndTacit
, in my opinion, are identical. F # say that they are of the same type: (string -> int)
. And yet they behave very differently, and they are composed very differently. Decompiling with dotPeek, I can see that getValFromMapPartial
is is a method whereas is getValFromMapPartialAndTacit
is a field that is initialized in ctor.
F # doesn't complain about getValFromMapPartialAndTacit
, even at the highest warning level (both in VS 2012 and 2013). And yet, the call to this function in my example above failed, presumably because it wrapped the original empty version someMap
despite its mutability.
Why is there a difference between these two functions? Should there be a warning from F # that the silent / dotted version might fail?
source to share
The F # compiler distinguishes between let-bindings of functions that have parameters and values ββthat have no parameters.
-
Definition of value: A binding like
let a = ...
that is a definition of value. His body is evaluated with an anxious "where is it", before evaluating anything else beyond the code. -
Function definition: Bind as
let f x = ...
is the definition of a syntactic function whose content is evaluated when the function is called.
Since it someMap
refers to a variable that is mutable, using this variable inside a function definition means reading from the variable when the function is called. However, use in getValFromMapPartialAndTacit
reads the value at the time of declaration.
This behavior does not stop the value as a function. You can also write let f = fun x -> ...
to declare a function, and ...
again will be part of the function definition. However, if you added definitions between =
and fun
, they would be evaluated at the point of the definition f
, not when it is called.
In the comments to the question, the same issue occurs when someMap
is a mutable reference cell. This is the same problem. 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. this is equivalent to:
let mapRightNow = !someMap
let getValFromMapPartialAndTacit = getValFromMap mapRightNow
source to share
getValFromMapPartial
is a true syntactic function. His signature val getValFromMapPartial : key:string -> int
. Whenever it is called, it uses the current value someMap
. This is why it works in your example; it refers to the version someMap
that has the entry.
On the other hand, it getValFromMapPartialAndTacit
is a lambda computational function. His signature val getValFromMapPartialAndTacit : (string -> int)
(note the parentheses). The lambda has a compiler generated closure that contains the version someMap
at the time the lambda is evaluated. This is why it doesn't work in your example; it always has the same original version someMap
that has no record.
source to share