Login

Advocacy

Garbage Collection

Exceptions

Comparisons

Performance

Reservations and dissatisfactions

OK. I found that my aggravation dropped a huge amount when I got past the initial stage of "why does adding ;; fix it? Should it let ... in or not, and why not?" After that, I found that it grew on me quickly. Good tooling also helps a lot - especially ocp-indent and ocamlformat and merlin, and utop, and getting comfortable with odocs after seriously pouring over ocamlgraph docs during Advent of Code.

One big reason that I won't write Lisp anymore is that I got tired of needing lots of editor help just to edit it in any way, so I get objecting to a language on a syntax level. The dots also can make math code look ugly very quickly, especially written without whitespace. ocamlformat is also hampered, seriously vs. zig, by the language not having subtle ways to guide formatting. You can't just add a trailing comma for a vertical layout of a function's parameters, for example.

Very surprisingly, it's not that bad, because code using different stdlibs actually continues to work fine together. You can have a Base module and a Stdlib module and link them into the same program. There's certainly a potential maintenance and human burden, but it's really not the deep split that you might anticipate. A win for OCaml's superior module system, perhaps.

It's not that frozen when OCaml releases keep adding language features, like immutable arrays (sharing the literal syntax!), labeled tuples, atomic record fields in 5.4, deep effect handlers in 5.3, concurrency and parallelism in 5.0 from just three years ago.

But mainly, there's a very important opposite hazard that OCaml avoids, of recklessly adding tons of ill-advised features and then struggling to cope with it all or have to walk it back, like in Perl, C++, D, Nim - even Zig which added async and then ripped it out without replacement for now 4 releases since. Languages join fads like defer, add it to say "yeah, we've got defer", and then not care that it interacts strangely with the rest of the language or results in codebases that in the aggregate are harder to maintain.

Other languages have other (worse) problems in this chore's stead, but it's fair that it feels that way. I don't think OCaml would be much different if it had some sugar for the case that you only want to make some small changes to a module's inferred type.

This is a sentiment that I'd almost forgotten I ever had, but I did have it at one point. Standard ML, Haskell, Erlang (and Prolog et al.) all have function-level pattern-matching, where function definitions can resemble mathematical definitions a little more:

fun length [] = 0
  | length (_ :: tl) = 1 + length tl
mylength([]) -> 0;
mylength([_|T]) -> 1 + mylength(T).

OCaml has some restrained sugar with function, but it really doesn't do this at all:

let rec length = function
  | [] -> 0
  | _ :: tl -> 1 + length tl

let rec length list =
  match list with
  | [] -> 0
  | _ :: tl -> 1 + length tl

SML's charming here but it's not something I miss. It gets bad, as well. Here's an example from The Little MLer:

(* book formatting *)
fun subst_anchovy_by_cheese(Crust)
    = Crust
  | subst_anchovy_by_cheese(Cheese(x))
    = Cheese(subst_anchovy_by_cheese(x))
  | subst_anchovy_by_cheese(Cheese(x))
    = Onion(subst_anchovy_by_cheese(x))
  | subst_anchovy_by_cheese(Anchovy(x))
    = Cheese(subst_anchovy_by_cheese(x))
  | subst_anchovy_by_cheese(Sausage(x))
    = Sausage(subst_anchovy_by_cheese(x))

(* smlfmt *)
fun subst_anchovy_by_cheese (Crust) = Crust
  | subst_anchovy_by_cheese (Cheese (x)) =
      Cheese (subst_anchovy_by_cheese (x))
  | subst_anchovy_by_cheese (Cheese (x)) =
      Onion (subst_anchovy_by_cheese (x))
  | subst_anchovy_by_cheese (Anchovy (x)) =
      Cheese (subst_anchovy_by_cheese (x))
  | subst_anchovy_by_cheese (Sausage (x)) =
      Sausage (subst_anchovy_by_cheese (x))

With long function name, even a function this simple becomes a wall of text. OCaml:

let rec subst_anchovy_by_cheese = function
  | Crust -> Crust
  | Cheese x -> Cheese (subst_anchovy_by_cheese x)
  | Onion x -> Onion (subst_anchovy_by_cheese x)
  | Anchovy x -> Cheese (subst_anchovy_by_cheese x)
  | Sausage x -> Sausage (subst_anchovy_by_cheese x)

Hostile reviews

Strict ordering