I just learned that OCAML have to have a .
postfix for doing float arithmetic. An example would be 3. +. 4.
which equals 7.
(float). However, F# handles float and integer arithmetic in the same way, so both 3 + 4
(int) and 3. + 4.
(float) works.
F# have +
naturally assigned to int so let add a b = a + b
is of type int -> int -> int
. And indeed (+)
gives me val it : (int -> int -> int) = <fun:it@6-1>
.
That leads to the following sequence which I think quite counter-intuitive:
> 3. + 4.;;
val it : float = 7.0
> (+);;
val it : (int -> int -> int) = <fun:it@8-2>
So my question is: Is the "overloading" done by a special mechanism/case in the compiler or is this a language-wide thing so I potentially can define a function called add
(or anything else) which have a one definition for integers and one for floats (or any other type.)
Briefly, F# has an ad-hoc-overloading mechanism via the inline
keyword and "static member constraints". There is some further magic specific to the built-in math operators, which magically assumes type int
the absence of other constraints. (+)
is just about the most special/magical thing in all of F#, so it does not make for a nice introduction to the language/type system.
In general, "overloading" is difficult for statically-typed, type-inferred languages. F#'s choices here are very pragmatic. OCaml does a different, simple, pragmatic thing (no overloading). Haskell does a different, complex-but-elegant thing (type classes). They're all somewhat reasonable points in the language/library design space.
Overloaded functions (and operators) must be marked inline
in F#. This is because they depend on explicit member constraints. Those constraints are resolved at compile time. A function let inline add a b = a + b
has the type 'a -> 'b -> 'c (requires member (+))
where +
is a static function/operator. You can't do this in C#; it doesn't have static member constraints.
let inline add a b = a + b
add 1 2 //works
add 1.0 2.0 //also works
In addition to Brian´s answer and link:
https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/prim-types.fs
I found some definitions in the code:
let inline (+) (x:int) (y:int) = (# "add" x y : int #)
and
let inline (+) (x: ^T) (y: ^U) : ^V =
AdditionDynamic<(^T),(^U),(^V)> x y
when ^T : int32 and ^U : int32 = (# "add" x y : int32 #)
when ^T : float and ^U : float = (# "add" x y : float #)
when ^T : float32 and ^U : float32 = (# "add" x y : float32 #)
...
And the AdditionDynamic
is defined here (loads of static stuff and CIL):
https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/prim-types.fs#L2374
Fun stuff:
(# "add" 1 2 : int32 #)
works, and gives 3 as output (with a warning saying you shouldn't do this.)