Hacker News new | past | comments | ask | show | jobs | submit login

Nice. But it should explain float arithmetic, i.e., why can I do 1+1 but not 1.0+1.0.



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.


> promotion from int to float is not free!

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.


> What type should this function have:

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.

Seems sensible to me. No problem.


Isn't it because "constrained" polymorphism goes through functors? And that wouldn't be very convenient for such basic operations?


Does this mean, it has a special function/operator for adding ints to floats?


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.


It has a separate operator for adding two floats: +.


(+) is an operator that takes 2 ints and produces an int

(+.) is the equivalent for floats: 1.0 +. 1.0

In short: if you need any operator for floats, and you know how to write it for ints, add a dot.

The reasoning is that it's hard to define the semantics of mixed expressions and explicit conversions clarify what needs to happen.


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.


Not allowing mixed expressions is completely orthogonal to using the same symbols for float or integer (or bignum or fixed or...) addition.


The non-obnoxious solution for that not-so-big problem is explicit conversions, not a whole new set of ugly multi-character operators.


Good question. It's a different operator, +. (plus dot).




Consider applying for YC's W25 batch! Applications are open till Nov 12.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: