My own retrospective... been using OCaml in a production compiler on obscure UNICES since 3.08 (> 15 years!). Pretty seamless, simple updates, everything kept working, lights are still on. I think source changes were required ~3 times, so amazingly stable from my POV :)
It compiles a “C# class”-looking DSL to GNOME GObjects, pushing all the ugly boilerplate (I’m aware there are newer ways to do it that have appeared in the last 15 years :)) into the generated code. And nowadays I’d call it a transpiler, but...
It was written by a teammate in 2004 (Frenchman, obviously!) and I’ve owned it since. A number of people have submitted features, etc. It’s fairly straightforward and OCaml wasn’t really a barrier to entry for any of the contributors.
edit: I should have said “source changes due to OCaml upgrades” above to be more clear.
Three things left for the full glory: better Windows support (without MinGW/Cygwin hacks, or WSL), better debugging and profiling (see DWARF PRs in GitHub repository), and, of course, multicore. Looking forward to more OCaml and faster OCaml.
It's been in development for more than 5 years and looks a bit like a boondoggle though, as the goal is not just to remove the GIL and provide "native threads" but
> use algebraic effects to compose concurrency and supports parallelism through domains and incremental GC. Rather than adding a specific multicore scheduler into the runtime system, we’re providing the minimum required toolset in the form of pluggable schedulers.
They're doing lots of great heavy lifting to get effects and handlers into a production language, and I don't think it's fair to write them off like this. Yes, it's hard, but they seem to be making positive progress.
> They're doing lots of great heavy lifting to get effects and handlers into a production language
Sure, but at the end of the day what it means is 5 years into the effort OCaml still as limited threading-wise as it was at the start of the effort. And that is what most users (and non-users) care about in a world where 2/4 is your mobile baseline and where AMD's R3-3300G is expected to be 6/12 for $99.
Go didn’t get much traction until they added Windows support? I could believe that Go began to take off around the time that Windows support was added, but that seems more of a coincidence than a cause. How many deployments are running on Windows, anyway?
As a layman I would describe them as implicits like in current Scala [0] but better (easier to reason about). One thing which they allow is ad-hoc polymorphism like typeclasses.
The ML programming language is known for its advanced module system. First, you have structured, which are your traditional idea of a module. Structures define types and data. Signatures describe the interface of a module and are like the module's "type." By making types abstract or not mentioning members, signatures achieve encapsulation of modules. Finally, you have functors, which are functions from modules to modules. The parameter is constrained by a signature, and the client can pass any module that conforms to the signature to get an output module that uses the passed module in its implementation.
The module system is used when a type must support certain operations. For example, the Set module (https://caml.inria.fr/pub/docs/manual-ocaml/libref/Set.html) defines a signature called OrderedType for types equipped with a comparison function, and any module that implements OrderedType can be used to make a set.
Notably, ML modules differ from Haskell typeclasses because more than one module can exist for the same combination of types. However, you must pass ML modules explicitly, whereas typeclass instances are passed implicitly. So, ML modules have more power than typeclasses, but at the cost of convenience.
Here is an example where modules are used to make a set:
module LtInt = struct
type t = int
(* Use built-in polymorphic comparison *)
let compare = compare
end
module GtInt = struct
type t = int
let compare lhs rhs = compare rhs lhs
end
module LtIntSet = Set.Make(LtInt)
module GtIntSet = Set.Make(GtInt)
However, modules can quickly get inconvenient:
module type Monoid = sig
type t
val op : t -> t -> t
val e : t
end
module Addition = struct
type t = int
let op = (+)
let e = 0
end
module Multiplication = struct
type t = int
let op = ( * )
let e = 1
end
module FoldLeft (M : Monoid) = struct
let fold_left = List.fold_left M.op M.e
end
Here, it's probably less verbose to just pass the operation and starting value directly to List.fold_left than to use modules! Debatably, this is as equally inconvenient as Haskell's newtype solution to multiple instances, but if there is only one instance, typeclasses are much cleaner to use.
Modular implicits are a long-awaited feature that will allow ML modules to be passed implicitly.
They did a pretty good job shepherding it, although I'm still not too fond that the current trend is fueled by the Javascriptification of all the things.
Sadly, meanwhile "Standard" ML is languishing in academia...