3

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?

Community
  • 1
  • 1
Bellarmine Head
  • 3,397
  • 2
  • 22
  • 31
  • It's worth noting that `let`s in a type are always private, and so cannot be used externally like `let`s in a module. – ildjarn Nov 20 '14 at 20:00

1 Answers1

5

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.

kvb
  • 54,864
  • 2
  • 91
  • 133
  • Yes, but the distinction between A -> B and (A -> B) has helped me understand Marc Sigrist's answer to my related question wrt the difference between true syntactic functions and lambda-computing functions (with closures etc.) That distinction breaks down in the class case, where the two type signatures are identical even though the behaviour is very different. It's all quite confusing. – Bellarmine Head Nov 20 '14 at 21:27
  • 1
    @AndrewWebb - The difference in signatures doesn't explain the difference in behavior. Given your module, you could create an F# signature file which uses the more general `(string -> int)` signature for *both* `getValFromMapPartial` and `getValFromMapPartialAndImplicit`, but that wouldn't change the behavior at all. – kvb Nov 20 '14 at 21:48
  • Ok, that clarifies things even better. – Bellarmine Head Nov 21 '14 at 08:05
  • For the sake of completion, here are two more cases where the distinction between A -> B and (A -> B) matters: When implementing interface members or overriding abstract base class members. However, in practice, it's not really necessary to be aware of this. – Marc Sigrist Nov 21 '14 at 13:02