I don't the author really gave Ocaml a chance. He argues that every language needs to have interfaces. I agree somewhat with that statement, but I think the truer statement is to say that every language needs to have some way of defining composition at a structural level.<p>An interface allows you to pass different "structures" to the same function so long as they adhere to the same spec. In Ocaml this is accomplished through modules, functors, and module signatures. Ocaml's module signature can play the same exact role as interfaces in Haskell, Rust or F#.<p>The module system is arguably more expressive than what can be accomplished to interfaces. To give an example, Ocaml suffers from the same problem as rust does with having two standard implementations of a async runtime. Just like rusts: tokio and async-std, ocaml has Lwt, Async, (and newly added to the mix Eio).<p>Whereas in rust most libraries just implement one of these systems, and you'll have to use compiler directives to support both. Ocaml's module system means that you can describe the async runtime as a signature and make your entire library generic to the async runtime it runs on top of. Most of the well-used libraries do this, and so you don't have to worry too much about which runtime you decide to use.<p>Clearly, a language that can do that must have in some place a system that can replace interfaces.
Related: <a href="https://twitter.com/zetalyrae/status/1639474931086901248" rel="nofollow">https://twitter.com/zetalyrae/status/1639474931086901248</a><p>My OCaml:<p><pre><code> let foo (x: bar): baz =
(\* Is this quuxable? *)
match is_quuxable x with
| Yes ->
(* Then, we have to ... \*)
</code></pre>
All the OCaml I see in the wild:<p><pre><code> qlet%bind f = 's 't 'a 'b (fun ꙮ -> ꙮ ((<_<')) ꙮ)
[@@ppx_gov_backdoor (~b~e~p~i~s)]</code></pre>
I think you should give F# a shot instead.<p>1. No standard and easy way of implementing interfaces<p>No problem in F#, you have interfaces, abstract classed, ...<p>2. Bad standard library<p>In F# you have access to the full .NET standard library and ecosystem. There are also quite a lot of libraries that are especially designed to take advantage of F# (SQL libs for example).<p>3. Syntax problems<p>3.1 OCaml doesn’t have a single-line comment syntax.<p>It's `//` for single line and `(* comment *)` for n line comments in F#.<p>3.2 It has for and while, but no break and continue. So you use exceptions with a try inside the loop for continue, and outside for break.<p>Same in F#, use recursion.<p>Generally F# solves some of the issues but definitely not all of them. Some are just in
the nature of the Standard ML syntax I guess.<p>4. Rest of the package is also not that good<p>- NuGet is a decent/good package manager.
- Easy to install a complete dev env.
- sane build system (MSBuild)
- Great out of the box support in JetBrains Rider/ Visual Studio (for Mac/ Windows) or via a VS Code plugin (Ionide).
> Another example is the sequencing syntax <expr> ; <expr> and productions with <expr> as the right-most symbol:<p><pre><code> let test1 b =
if b then
print_string "1"
else
print_string "2"; print_string "3"
</code></pre>
> Here print_string "3" is not a part of the if expression, so this function always prints “3”.<p>Well, yeah. Here's the equivalent Java code:<p><pre><code> void test1(bool b) {
if (b) print_string("1");
else print_string("2");
print_string("3");
}
</code></pre>
You've chosen to use a single-statement "else" form _without_ "bracketing" it with `begin` and `end`:<p><pre><code> let test1 b =
if b then
print_string "1"
else begin
print_string "2";
print_string "3"
end
</code></pre>
...which is equivalent to this Java code, where you _also_ would have to "bracket" the else-block:<p><pre><code> void test1(bool b) {
if (b) print_string("1");
else {
print_string("2");
print_string("3");
}
}
</code></pre>
You could argue that Java (and C, and C++, and Javascript, and...) has the same "ambiguous parsing problem", then -- except that it doesn't, and the author is just thrown off by `begin`/`end` as block delimiters instead of curly braces, which might be a sign that they've also never seen Ruby before, or Elixir, or Pascal, or....
1. To print values, nowadays you just derive pretty printers automatically.<p>2. Syntax? yes, it's not C-like... you'll get used to it in a few days.<p>3. Lack of interface? would be helpful to see a concrete example of what the author finds limiting. You have modules, functors, includes, objects, first-order modules... Personally, I find that plain modules are simple and go a long way.<p>It's not perfect, but I find that nowadays Core/Dune/Opam/Merlin gives you a very decent developer experience.
Coalton [1] is an OCaml-like dialect of ML: it's strictly evaluated, has imperative features, and doesn't force a purity-based development philosophy. However, Coalton has S-expression syntax and integrates into Common Lisp. It works, and is used "in production" by its developers, but it's still being developed into a 1.0 product.<p>Coalton made a lot of design decisions to work away from issues that this post describes.<p>- Coalton dispensed with ML's module system of structures, signatures, and functors. No doubt a beautiful concept, and sometimes satisfying to write, but almost always unsatisfying to use due to how explicit one tends to need to be. Like the author suggests, the "interface-style" of type classes or traits has always felt more intuitive, despite losing the ability to have multiple implementations of an interface for a single type. (However, I've come to appreciate that different interfaces <i>should require different types</i>—as opposed to one type having many interfaces.) Coalton uses type classes.<p>- Coalton has S-expression syntax. No indent rules. No precedence rules. No expression delimiters. All of the "tradition" of ML syntax is dispensed with into something that is a <i>lot</i> easier to read and write for complex programs... yes, provided you use ParEdit or similar<p>- It should be no surprise that with S-expressions, you get Lisp-style macros. Macros in Coalton are just Common Lisp macros. No separate pre-processors. No additional language semantics to deal with. Arbitrary compile-time computation and/or codegen, completely integrated.<p>- Because it's built on Common Lisp, Coalton gets something few (if any?) other ML-derivatives get: Truly incremental and interactive development. You can re-compile types, functions, etc. and try them out immediately. There's no separate "interpreter mode"; just a Lisp REPL.<p>Coalton's language features are still settling (e.g., records are being implemented) and the standard library [2] is still evolving. However, it's been used for "serious" applications, like implementing a compiler module for quantum programs [3].<p>[1] <a href="https://github.com/coalton-lang/coalton">https://github.com/coalton-lang/coalton</a><p>[2] <a href="https://coalton-lang.github.io/reference/" rel="nofollow">https://coalton-lang.github.io/reference/</a><p>[3] <a href="https://coalton-lang.github.io/20220906-quantum-compiler/" rel="nofollow">https://coalton-lang.github.io/20220906-quantum-compiler/</a>
The last paragraph show this article for what is is. That's an Haskell fan trying to discredit Ocaml and doing it very poorly.<p>It takes a special kind of writer to write an incorrect sentence about the expression syntax while quoting the actual definition from the specification which contradicts what's written just after.<p>I generally disagree about the complaint regarding the syntax. If you indent the code properly and use parentheses as you should and the language invites you to do, none of this is ambiguous. That's bad code not bad language design.
I used OCaml as my daily driver in grad school and my postdoc, and recommend my students use it. I think the main benefits of OCaml are:<p>Functional
Has mutable references
Not lazy<p>Haskell's type system is just better. But for large, complex systems that require performant code, it can be quite difficult to track the laziness, and sometimes life is just easier if I can use a mutable references.<p>The author is completely correct that there's a big failure in the lack of a type-class-like system. This is why people ask so much for modular implicits. Core is also a decent standard library (though I don't know why they require sexp_of_t and t_of_sexp on like all their data structures).<p>So I'd gladly use a different functional language with mutable references and strict evaluation. But there isn't any besides F#, and I have a Mac and don't personally want to figure out .NET on Mac.
ReasonML and ReScript are a (more or less the same) new syntax on top of OCaml. ReScript only targets JS, while ReasonML targets both JS and the native archs OCaml supports. Facebook and Bloomberg are using ReScript internally, afaik. Messenger.com is written in it. Facebook also maintains React bindings to ReScript.<p><a href="https://rescript-lang.org/" rel="nofollow">https://rescript-lang.org/</a><p><a href="https://reasonml.github.io/" rel="nofollow">https://reasonml.github.io/</a>
This piece focuses on complaints about ocaml, and the last half seems to just be syntax complaints (which I mostly ignore just because every language seems to have awkwardness in its syntax -- certainly Haskell, a language otherwise held up as an example, can be shown to accept some really weird text).<p>Good things about ocaml that I think should be mentioned are Hindley-Milner type inference (some might argue this, but it's a very effective system for inferring types), and also straightforward semantics (it's usually pretty easy to look at some code and make a reasonably accurate guess about what it will do on real hardware -- compare with Haskell).<p>I'm sympathetic toward the initial complaints here, which I also think are the most substantive. Type classes and qualified types, as in Haskell, would be very useful in ocaml/ML. The examples offered (show_of_int, show_of_double, ...) are valid, and also the related complaint that dishonestly broad types like structural equality (where runtime errors are raised for cases where the types are actually not supported) also make a compelling case for qualified types in ML.<p>I made this project ~10 years ago after making similar observations:
<a href="https://github.com/morganstanley/hobbes">https://github.com/morganstanley/hobbes</a><p>Things like structural equality and ordering are user functions here rather than magic internal definitions (e.g. equality is defined as a type class, and type class instances can deconstruct algebraic types at compile time to decide how to implement instances). But evaluation is eager by default, so it's pretty easy to reason about performance. And data structures can be persisted to files and/or shared transparently in memory with C/C++ programs without translation, also very convenient for the places where this was used. Actually we built some very big and complicated time series databases with it, used in both pre and post trade settings where ~15% of daily US equity trades happen. So I think these observations are useful and have passed through some pretty significant real tests.
The biggest problem I have with Ocaml is not the language, but the tooling and the libraries. The library ecosystem is not even remotely comparable to go or even rust. Jane street core is mentioned as a good option, but that library is the most popular undocumented library I have ever seen. There is not even some normal default structure every library follows (e.g go io.Reader).<p>Compare this to languages with supposedly "worse" type systems like Java, which has a library for everything and high quality well documented libraries like guava and apache commons. I find it fine to use ocaml for a random weekend whim project, but wouldn't touch it with a 10 feet pole for professional work.
> No standard and easy way of implementing interfaces.<p>> In Haskell, this is done with typeclasses ...<p>> In OCaml there’s no way to do this. I have to explicitly pass functions along with my values, maybe in a product type, or with a functor, or as an argument.<p>Haskell is a mixed community / paradigm language too.<p>I routinely program in "Braindead Haskell" (keep stuff simple and direct as much as possible). Funnily, in that programming style, using typeclasses for _all_ interface types is bad style.<p>Instead, the Handle pattern [0] can be used. Which really is just shoveling functions in a product type. And imagine - it works worderfully.<p>Everyone has to find their own style I guess.<p>[0]: <a href="https://jaspervdj.be/posts/2018-03-08-handle-pattern.html" rel="nofollow">https://jaspervdj.be/posts/2018-03-08-handle-pattern.html</a>
The author says: "If you have a reason to think that OCaml is the best choice for a new project please let me know your use case, I’m genuinely curious." I think it's overwhelmingly the best language for writing an interpreter or a compiler. I'm sure Haskell is also a good language for writing compilers, but whenever I tried writing interpreters in Haskell, I found myself fighting the laziness. Most of the criticisms listed strike me as things that bother people starting out in the language (syntax etc.) but which quickly become non-issues with more experience. And my experience with opam/dune has been _much_ more pleasant than with Cabal in Haskell (though ghcup has certainly improved matters). I think the infrastructure in OCaml, which admittedly used to be quite poor, is now on par with any other language, if not better. However, if your application domain is one in which few OCaml libraries exist (e.g. web programming) you probably should look elsewhere.
> It also has the “dangling else” problem:<p>God I hate this term because it's not even a <i>problem</i>. For a human reader, it's pretty obvious that an "else" should belong to the closest "if"; and it's quite difficult to write a mechanical parser that would match "else" with the furthest "if" instead. So what is, exactly, the problem? That the grammar is technically ambiguous? Who cares? The authors of LR-generators? They don't, they have been resolving shift-reduce conflicts by default in favour of shifting for half a century already.
Why would a company use ocaml?<p>I’ve worked in software now for most of my life, from small to the biggest companies. I’ve watched teams with senior developers use non mainstream languages to build services and just about every time those devs get board again, leave the team, leaving them stuck with this system that’s hard to develop and different from the rest of the orgs, eventually resulting in a complete rewrite.<p>At companies I try to be boring, not weird, and efficient. Which usually means for me, writing in a very mainstream language that is easy to hire for.
> Int.abs can return a negative number.<p>The documentation for abs[1] explains: "Warning. This may be negative if the argument is Int.min_int."<p>I think that's just the nature of two's complement and not necessarily a fault of OCaml. C++ is also like that[2].<p>[1] <a href="https://v2.ocaml.org/api/Int.html" rel="nofollow">https://v2.ocaml.org/api/Int.html</a><p>[2] <a href="https://gcc.godbolt.org/z/34b3GbvvT" rel="nofollow">https://gcc.godbolt.org/z/34b3GbvvT</a>
An alternative to OCaml is Flix (<a href="https://flix.dev/" rel="nofollow">https://flix.dev/</a>) which attempts to address some of the critiques in the post. For example:<p>> Bad standard library<p>A particular goal of Flix is to have a consistent standard library based on type classes.<p>> Standard types are sometimes persistent, sometimes mutable. List, Map, and Set are persistent. Stack and Hashtbl are mutable.<p>In Flix immutable types are named List, Set, Map, etc. and mutable collections are prefixed with Mut, e.g. MutSet, MutMap, etc. Mutable data types also carry a region.<p>> Also, OCaml has no tracking of side-effects (like in Haskell)<p>Flix has an effect system that tracks purity/impurity.<p>(I am one of the developers of Flix)
Knowing the virtually dead Standard ML is a curse when it comes to appreciating the aesthetics of OCaml. The two languages have similar syntax, but in just about every way they differ, OCaml syntax took the ugly road :(
Simply put, OCaml is very principled. There is a good reason for pretty much every design decision.<p>The only thing one could argue is the lack of type classes, i.e., overloading. (Calling it "interfaces" is somewhat wrong.)
Ocaml5 is multithreaded. MLdonkey would run much better.
Compared to the classical Amule, a monstruosity writen in C++, at least it doesn't leak memory like some youngster with a constipation.
I learned OCaml two decades ago. It was great to learning algorithmics but I already had the feeling the language was old and awkward. Today there is Scala, Haskell, Rust, F# etc.
Everyone I’ve met, including on the steering committee, has been funded by high-frequency trading.<p>Heck, I almost took a job on a handshake in manhattan on this premise.<p>But it just seems, even now, archaic for the task. As the author mentions, the community is small, the integrations are weak. You will be working on legacy systems if anyone pays you well.<p>Same with certain parts of national defense, but we’ll make fun of PDP-11’s and FORTRAN another day.<p>Proactive thought: why don’t we fund a B-team to say, try using Julia? Even ERLANG has some superior qualities for these real-time audited tasks. (And hey, if you want employees for some reason, just use python).
I don't think this list is fair at all.<p>> No standard and easy way of implementing interfaces<p>Firstly another commenter has mentioned [1] modules as interfaces. There is also the object system [2] which I've seen used to great effect in similar scenarios.<p>Typeclasses and modular implicits are fantastic - being a heavy user of Scala and Rust; but I've always found that they tend to be best when used as an alternative to macros, for derivation purposes.<p>The interface and code split in OCaml is also really nice for value types and comes with no limitations; for years, we had issues in Scala because of the host platform, and in Rust exposing functions for your newtype can be oddly boilerplate heavy.<p>> Bad standard library<p>The OCaml standard library is (infamously) anemic and obviously is not as fully-featured as something like Java or Python, but since alternative libraries tend to be compatible with the bundled StdLib (both use the same `option` and `list` type), it means that having libraries dependent on different ones is not a huge issue. Learning resources, can be confusing though.<p>I'm not sure why it's a problem certain data structures are mutable and some are persistent; the documentation is usually fairly clear and you usually have a performance characteristic in mind when selecting your data structure.<p>Universal equality and hashes are indeed a little annoying (here an `Eq` typeclass would be great!) but I do understand the original decision to have them inside. At the very least, it's not pointer based equality and since records and most types don't have subtypes, you don't run into as many issues. You can of course use the equality methods on the modules themselves and since `Hashtbl` and co have functors, you aren't penalized for it.<p>Strings are a problem-ish (what older language doesn't have string issues ha!), but there is utf-8 support now with [3].<p>> but just two years ago it was common to use Makefiles to build OCaml projects<p>It is true that OCaml has a unix-y flavour, but honestly I don't really understand the snipe at make. Sure there are alternatives, but make is bundled on most distros, has a quick startup time and a lot of people are familiar with it.<p>More of the community is moving towards dune as well, which is pretty good; the docs could do with better indexing but there are some features such as promotion which are really nice.<p>> was no standard way of doing compile-time metaprogramming<p>I haven't used ppx much so I can't really comment here.<p>Finally they've missed the biggest reasons to /use/ OCaml.<p>[1] <a href="https://news.ycombinator.com/user?id=4ad" rel="nofollow">https://news.ycombinator.com/user?id=4ad</a>
[2] <a href="https://ocaml.org/docs/objects" rel="nofollow">https://ocaml.org/docs/objects</a>
[3] <a href="https://v2.ocaml.org/api/Stdlib.String.html#utf_8" rel="nofollow">https://v2.ocaml.org/api/Stdlib.String.html#utf_8</a>
My friends. Algebraic effects in ocaml 5! I did protohackers and AOC with effects and it was a blast! Im not a daily ocaml driver so my code can be a bit clunky, but i love the patterns effects unlock, especially around module coupling.
OCaml is one of those languages (like Raku and Nim) that I really want to learn and do something in at some point but usually end up not bothering because it's easy for me to just start up new personal projects in Python like I'm used to.
Almost all modern statically typed languages have closures, higher-order functions/methods, lazy streams, and combinators that run efficiently. Persistent/immutable data structures can be implemented even in C.<p>Also, OCaml has no tracking of side-effects (like in Haskell), and the language and the standard library have lots of features and functions with mutation, such as the array update syntax, mutable record fields, Hashtbl, and the regex module.<p>The only thing that makes OCaml more “functional” than e.g. Dart, Java, or Rust is that it supports tail calls. While having tail calls is important for functional programming, I would happily give up on tail calls if that means not having the problems listed above.<p>When you mix imperative and functional styles tail calls become less important. For example, I don’t have to implement a stream map function in Dart with a tail call to map the rest of the stream, I can just use a while or for loop.<p>In my opinion there is no reason to use OCaml in a new project in 2023.