F#: arithmetic operator and loss of polymorphism (value restriction?)

198 Views Asked by At

This code doesn't compile:

let f = fun x y -> x <<< y // bit shift
let g = fun x y -> x <<< y

[<EntryPoint>]
let main _ =
  printfn "%d" <| f 1 10
  printfn "%d" <| f 1L 10 // error
  printfn "%d" <| g 1L 10
  0
(7,21): error FS0001: This expression was expected to have type
    int
but here has type
    int64

I guess the unifier fixed the type parameters associated with f and g upon seeing their first occurrences. What governs this process? I think this is very similar to "value restriction" but f and g are already eta-expanded! This is a hard problem.

I would surely imagine there's some black magic behind typing predefined operators with ad-hoc polymorphism over integral types, but that's just my speculation. Any information appreciated.

2

There are 2 best solutions below

6
On BEST ANSWER

Generic numeric programming is done using static member constraints, which can't be represented in the .NET type system. They exist only in F# and therefore must be marked inline.

Your code could be written this way:

let inline f x y = x <<< y // bit shift
let inline g z y = z <<< y

[<EntryPoint>]
let main _ =
  printfn "%d" <| f 1 10
  printfn "%d" <| f 1L 10 // works too
  printfn "%d" <| g 1L 10
  0

More info on MSDN:
Inline Functions
Statically Resolved Type Parameters

3
On

I think it is how F# performs automatic generalization of function parameters. On first appearance it infers that the function 'f' could have type ('a -> 'a -> 'a) but the second appearance is not match this signature because it has a different signature ('b -> 'a -> 'a) because it consider int64 and int as different types.

Inlining functions could sometimes solve this problem as @Daniel mentioned

Slightly more info could be found here: http://msdn.microsoft.com/en-us/library/dd233183.aspx

Some more info on static member constraints could be found in this post by Tomas Petricek: http://tomasp.net/blog/fsharp-generic-numeric.aspx/