Let's assume I'm constructing an F# generic function f with a single argument integral, and this argument by the function semantics should be constrained to any .NET integral type from System.SByte through System.Int32 to System.Numerics.BigInteger.
One approach would be implementing
let inline f (integral: 'a) =
(* function body here *) ...
and rely on compiler-deduced constraints to 'a derived from the actual contents of f's body, which may or may not coincide with the set of integral .NET
types.
Another approach might be to force explicit hand-picked a priori constraints to 'a that would really guarantee for known .NET types that only integral types pass the static
check, for example
let inline f (integral: ^a when ^a:(static member (|||): ^a * ^a-> ^a)) =
(* function body here, unit for illustration *)()
or
let inline f< ^a when ^a : (static member (|||): ^a * ^a -> ^a)> (integral: ^a) =
(* function body here, unit for illustration *)()
so f 1uy, f 1L, f 1I pass the static type check right away, but f 'a', f 1.0, f 1m do not.
What would be benefits, if any, of using second approach over the first?
Are there more idiomatic ways to reach the initial goal?
UPDATE 02/03/2014 Ironically, only today after looking at this answer managed to get a working code out of @kvb's prompt:
let inline implementation integral = ((* whatever implementation here *))
type Integral = Integral with
static member ($) (Integral, value: byte) = implementation value
static member ($) (Integral, value: sbyte) = implementation value
static member ($) (Integral, value: int16) = implementation value
static member ($) (Integral, value: uint16) = implementation value
static member ($) (Integral, value: int) = implementation value
static member ($) (Integral, value: uint32) = implementation value
static member ($) (Integral, value: int64) = implementation value
static member ($) (Integral, value: uint64) = implementation value
static member ($) (Integral, value: bigint) = implementation value
let inline doit integral = Integral $ integral
doit 1
doit 1I
doit 1.0 // does not compile
doit 1.0m // does not compile
doit '1' // does not compile