Inspired by the implementation of the plus
function (equivalent to mappend) in FSharpPlus, I decided to copy the pattern and implement a generic function incrementing numbers (it would probably be easier to use LanguagePrimitives
, but it's just an exercise).
type NumberExtensions =
static member Increment (x:float) = x + 1.0
static member Increment (x:int) = x + 1
//etc
let inline increment number =
let inline call (_:^E) (number:^N) = ((^E or ^N) : (static member Increment: ^N -> ^N) number)
call (Unchecked.defaultof<NumberExtensions>) number //!
let incrementedFloat = increment 100.0
let incrementedInt = increment 200
It works as expected:
val incrementedFloat : float = 101.0
val incrementedInt : int = 201
Unfortunately, I don't quite get why do we need or ^N
in the constraint. As far as I understand, the constraint says:
There's a static method named
Increment
, which takes an argument of some type (the same type as thenumber
's) and returns another value of the same type. This method must be defined either in^E
or in^N
.
Here I thought: We know that the Increment
method will be defined in ^E
, so we can safely change (^E or ^N)
to just ^E
. Unfortunately, the change resulted in the following error in the line marked with !
:
A unique overload for method 'Increment' could not be determined based on type information prior to this program point. A type annotation may be needed. Candidates: static member NumberExtensions.Increment: x:float->float, static member NumberExtensions.Increment: x:int->int
Why does adding or ^N
to the constraint removes this ambiguity? Does it tell the compiler something else than the possible location of the method?