It was a surprising language decision to find out that OCaml didn't do implement polymorphic behaviour for common operations and opted to stress static types for things like print_int or 1.0 +. 1.0...
One reason is that promotion from int to float is not free! Therefore maybe it should be explicit that your code is doing that especially in a language like OCaml that cares a lot about performance and is used in a few performance-sensitive areas (notably HFT).
You can have overloaded functions without having type promotions. `1 + 2` would call `addInt`, `1.0 + 2.0` would call `addDouble` and `1 + 2.0` would be a type error, requiring manual conversion in order to fix it.
Nor is it safe to allow implicit conversion in general, but F# shows that you don’t need separate operators for floating point. You just need a compiler that refuses to implicitly convert data types, as F# does, so you must use explicit conversions and type suffixes if you want to mix data types. If you manually convert, then on your head be the consequences.
This is not legal F#:
let x = 1.0 + 1
But both of these are legal:
let x = 1.0 + 1.0
let y = 1.0 + float 1
F# doesn’t even let you mix single- and double-precision FP:
let x = 1.0f + 1.0 // ILLEGAL
let x = 1.0f + float32 1 // legal
And both F# and sml have a problem with that. What type should this function have:
let add x y = x + y
The proper solution for ad-hoc polymorphism is either dynamic dispatching (aka oop solution) or type-classes or OCaml's modular implicits, not ad-hoc monkey patches like that. And I find (+.) fine since float addition is not an arithmetic addition.
If you don't call it, F# simply guesses "int -> int -> int". If you pass a float, you get back a curried function taking float and returning float. Now that the "add" function's type is known, you now can't pass an int: that's now known to be an error.
It doesn't have any way to add ints to floats directly. You have to convert one to the other then perform the addition.
Haskell or Rust are the same incidentally: while the same "lexical" operator is used all the time (`+`), numeric additions are defined for two values of the exact same concrete type e.g. in Haskell you can add two Int or to Float or two Integer, but not an Int and an Integer, or an Int and a Float:
Prelude> (1::Int) + (1::Int)
2
Prelude> (1::Integer) + (1::Integer)
2
Prelude> (1::Float) + (1::Float)
2.0
<interactive>:6:15: error:
• Couldn't match expected type ‘Float’ with actual type ‘Int’
• In the second argument of ‘(+)’, namely ‘(1 :: Int)’
In the expression: (1 :: Float) + (1 :: Int)
In an equation for ‘it’: it = (1 :: Float) + (1 :: Int)
While true, that issue arose because you restricted their types. Without the type annotations it would have worked just fine, because the (+) function is polymorphic in the Num typeclass, and because literals are overloaded such that the literal 1 will have type Fractional a when type inference calls for it.
So in ghci:
Prelude> let x = 1 + 1.0
Prelude> :t x
x :: Fractional a => a
Prelude> x
2.0
> While true, that issue arose because you restricted their types.
What I was intending to show occurred because I engineered the example to show it, yes, that was the entire point.
> Without the type annotations it would have worked just fine
Without the type annotation it would have been in an actual codebase rather than the REPL, and that codebase would have been typed.
> because the (+) function is polymorphic in the Num typeclass, and because literals are overloaded such that the literal 1 will have type Fractional a when type inference calls for it.
Which does not contradict my point at all.
> So in ghci:
Which works because (drumroll) both operands are of the same Fractional type. Which is the entire point of my comment: Haskell does not allow adding numbers of different types with one another any more than OCaml does.
I initially hated the separate operators for floats and integers.
I've since grown to like it, because the only times I've been caught out by it is when I've made a mistake or I've forgotten I was working with floats.
Don't let the syntax put you off Ocaml, because there is some reasoning behind it. It's a multi-paradigm language that has a lot of powerful features.