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?

+3


source to share


2 answers


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

      

+3


source


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.

+2


source







All Articles