Hacker News new | ask | show | jobs
by pauldirac137 3709 days ago
If you've been away from Ocaml for a while, this would be a great time to check out what's changed. A package manager, a new optimizer, and we'll soon have modular implicits (think Haskell-style type classes, but better) and multicore. Ocaml is really a first-rate programming language and I wish more people knew about it.
3 comments

If I'm not mistaken, this release is not the multicore release[0]. Also, if anyone's keen on reading the paper behind modular implicits in OCaml, its a fun read - http://arxiv.org/abs/1512.01895

[0] - https://blogs.janestreet.com/flambda/#comment-39958

Correct. Multicore _may_ come in 4.04 if we're lucky. No ETA for modular implicits AFAICT, but I really want them ASAP; they will be a game-changer.
Any time I want to look further into OCaml, it's because the purist in me wants a better language than the ones I'm already using. And it always comes down to Haskell vs OCaml for me, and Haskell always wins out because OCaml has lots of edge cases built into the language and the standard library that they justify and excuse rather than deprecating and cleaning up. Then again, Haskell also has Prelude. But it feels like OCaml has way more legacy cruft.
I was prompted to try an OCaml tutorial by this HN post. I soon saw that strings and numbers each have a different print function. Is this part of the cruft you mention? Why have two print functions?
When it comes to functions, there's ad hoc polymorphism, and there's parametric polymorphism.

Parametric polymorphism is when you can define a function without knowing the precise type of its parameters. However, this limits what you can actually do with the parameters.

For instance, a function concat([a], [a]) -> [a] which takes two lists containing items of type "a" knows enough about the type of its parameters (they're lists) to do its job, but it doesn't know everything about them. This is the type of polymorphism OCaml supports. Think of it as "one implementation, many types". That's why OCaml has more than one print function, and why it has integer and floating point versions of operators like + and *.

Ad hoc polymorphism is when you can define implementations for a single function for whatever types you like. So you can implement add(Int, Int) -> Int, as well as add(Float, Float) -> Float. So this is "many implementations, many types." Haskell supports a version of this through typeclasses. OCaml doesn't currently support it, but the new "modular implicits" feature, as I understand it, will provide largely the same functionality.

This is incorrect. Haskell and OCaml both support parametric polymorphism, but Haskell also supports ad-hoc polymorphism, whereas OCaml does not. Modular implicits, it seems, are OCaml's solution to the problem of reducing the set of valid types in a polymorphic function.

Haskell: concat :: [a] -> [a] -> [a]

OCaml: val concat : 'a list -> 'a list -> 'a list = <fun>

Both are parametrically polymorphic. However, in Haskell, I can use a type class to support ad-hoc polymorphism:

concat :: (Num a) => [a] -> [a] -> [a]

Now concat works forall a, as long as a is an instance of Num. This is important for arithmetic operators:

(+) :: (Num a) => a -> a -> a

Now I can have (+) be defined in a meaningful way, since Int, Integer, Float, etc. are all instances of Num, and implement (+). An easy way to think of this (since it's all it is, really) is operator/method overloading. In Haskell, (+) is overloaded for every type that you would want it to work for (the details of this are actually probably different, but are not important for learning).

In OCaml, if I have

val (+) : 'a -> 'a -> 'a = <fun>

There is actually only two implementations of that function:

let (+) a b = a

or

let (+) a b = b

Since I cannot restrict the set of types to the function, I can't do anything with the arguments. I don't know what the types are! Ad-hoc polymorphism allows for restricting the set of types, which allows for Haskell to use + for any Num-like thing.

I also don't see the disagreement between your comment and the parent.
I appreciate the clearer explanation, but I'm not sure what I said that was incorrect. Can you explain?
Hm, after re-reading a couple times, I think I merely misinterpreted how you said "OCaml supports parametric polymorphism" and "Haskell supports ad-hoc polymorphism." Based on your wording I thought you were saying that Haskell only supports ad-hoc polymorphism and OCaml only supports parametric polymorphism. Surely there was something in my head at 2am that made me think you were off in your description, but I don't know what it was. Sorry! I would edit it if I could.

EDIT: I think the line that stood out to me as being odd was "That's why OCaml has more than one print function, and why it has integer and floating point versions of operators like + and *." which was after you explained what parametric polymorphism was. So my interpretation was "OCaml supports parametric polymorphism, which is why there are multiple addition functions for different types." Of course, the real reason isn't due to supporting parametric polymorphism, but specifically because OCaml lacks support for ad-hoc polymorphism.

Interestingly, OCaml does have sort of pseudo-ad-hoc polymorphic implementations for comparison operators.

val (<) : 'a -> 'a -> bool = <fun>

But this is a wonderfully hacky implementation.

It's annoying I guess, but you get over it in a few hours. Interestingly it's only annoying from an ideological standpoint—it's never bothered me when reading or writing code.

Anyway, the reason, along with having separate sets of arithmetic operators, is basically because Ocaml ‘generics’ can't be specialized like C++ templates can. Doing it this way keeps the whole language fully type-inferable.

It is actually annoying from a very practical point of view. You cannot, for example, write an algorithm that will work an any sequence type, and then apply it to a list. This is why, for instance, you have List.map and so on
You can, that's precisely why we have functors and for this kind of use cases (abstracting a module over another), they are much nicer than type classes.
As far as I understand, this would only work if standard sequence types were defined inside a functor, which is not the case.
The biggest reason is because of type inference. Ocaml generally stays away from type coercions or overloading.

That said, Ocaml does have some voodoo magic for type-safe printf, so in the specific case of printing you can use a single function if you want

    let name = "Buttons840" in
    Printf.printf "Hello, %s!\n" name
This is exactly the problem that modular implicits will solve. See http://arxiv.org/pdf/1512.01895.pdf
Oh shit. I came here to ask if this was the release with multicore. Have you tested it for real-world performance enhancement?
Multicore will take more time before it's stable, but it will be available via opam as another alternative OCaml toolchain until then. It's basically the result of KC's research: http://kcsrk.info/
Note that multicore includes two separate "features": the algebraic effects abstractions, and the multi-core runtime (domain heaps). From what I understand, OCaml's multi-core effort will include both of these as well as some improvements to garbage collection.
It's hard to say which is more exciting for me, but it probably comes down to effect handlers in a production-capable (if not production-ready) language that can work across different compilation target. Lots of interesting tooling that can be built on top of that!