Why the difference between type signatures of the same F# function in module vs class?

296 views Asked by At

Closely related to my question here, but actually a different question...

Consider the following F#:-

type TestClass() =
    let getValFromMap m k = Map.find k m
    let mutable someMap : Map<string,int> = Map.empty
    let getValFromMapPartial key = getValFromMap someMap key
    let getValFromMapPartialAndTacit = getValFromMap someMap

module TestModule =
    let getValFromMap m k = Map.find k m
    let mutable someMap : Map<string,int> = Map.empty
    let getValFromMapPartial key = getValFromMap someMap key
    let getValFromMapPartialAndTacit = getValFromMap someMap

In both the class case and the module case, getValFromMapPartial and getValFromMapPartialAndTacit behave in very different ways, and are compiled to IL differently. In both the class and module case, the former behaves like a true syntactic function, and the latter behaves like a lambda-computing function (I know this thanks to user Marc Sigrist).

In the module case, the type signatures seem to be correct:-

getValFromMapPartial : key:string -> int
getValFromMapPartialAndTacit : (string -> int)

But in the class case, the type signatures are identical:-

getValFromMapPartial : (string -> int)
getValFromMapPartialAndTacit : (string -> int)

Why would this be so?

Since getValFromMapPartial acts as a true syntactic function in both cases, why would it be typed as a lambda-computing function in the class case?

1

There are 1 answers

4
kvb On BEST ANSWER

I can only think of a few times you'd ever need to worry about the distinction between A -> B and (A -> B) (see the F# spec's section on signature conformance for related commentary):

  • When you want to implement a module signature, only a syntactic function can serve as the implementation of something with the signature A -> B, while either a syntactic function or any other function value could implement the signature (A -> B). That is, the latter signature is a superset of the former.
  • When you care about the way your code appears to other .NET languages, functions with signature A -> B are implemented as methods, while functions with signature (A -> B) are implemented as values of type Microsoft.FSharp.Core.FSharpFunc<A,B>.

Otherwise, the difference doesn't matter. And in this case, as @ildjarn notes, let-bound values in a type definition are private, so the above two considerations don't come into play for TestClass and it's just an implementation detail of no consequence.