81 comments

[ 3.2 ms ] story [ 168 ms ] thread
Who would be cruel enough to expose the masses to OCaml?
At least it ain't Clojure.
It was cruel to expose JS to the masses.
I wouldn't mind living in the alternate universe where browsers adopted Ocaml as the built-in scripting language instead of JavaScript.
I like both JS and Ocaml. Should I seek medical attention?
It seems like Haskell minus the laziness and enforced purity. So really, far less cruel :)
One of the early programming classes I took in grad school (taught at Harvard) was focused on OCaml. It was a wild jump but gave me a newfound respect for functional languages.

Come to think of it though, I’m convinced the class was sponsored by Jane Street or something.

It's probably a great book, but I think Elixir is better for the masses.
I'm a bit surprised to see this making it to the front page. It's been available for a long time and is one of the first resources anyone with an interest in OCaml is likely to encounter. It seems a little like posting a link to cppreference.com.
True, but it appears to have been updated last year, I for one wasn't aware of it.
Even just "The lucky 10 000" can be already a reason good enough (https://xkcd.com/1053/)!
I wish someone posted that xkcd everytime someone posted a comment like GP
A more generous interpretation of your sentiment could be:

This is a fantastic resource that anyone with a passing interest in the OCaml programming language needs to be aware of.

There are constantly new programmers joining the community here. It’s good for quality things to appear regularly.
I, for one, have the attention span and knowledge organization skills of a Phineas-Gaged goldfish, so it's good to be externally reminded of the existence of good things from time to time.
I'm not a huge fan of this book, if I get a book like this I'd expect it to focus on the standard library and not third party libraries.

On the other hand it says "real world" OCaml and as far as I can tell that really is how OCaml used so It's hard to blame the author.

The standard library is quite small and not very usable for modern production applications. As an example a lot of the list manipulation functions are not tail-recursive because the tail-recursive implementation is less elegant.
For the pedagogical purposes of the book, you don't really need Jane Street Base/Core though. Plus it ignores some pretty widespread community libraries (like Lwt) and pushes its own Jane Street version, Async (which incidentally doesn't support Windows).

Anyway, this has all been hashed out before.

Yes, I’m aware it’s been hashed out. I was just explaining for the benefit of people not versed in the community why Core is so popular and used in this book, which is focused directly on practical applications of the language.
Like I said, you don't need Jane Street Core for the practical application of the language. Many people who are using OCaml practically are not using JSC.
> As an example a lot of the list manipulation functions are not tail-recursive because the tail-recursive implementation is less elegant.

What makes you say this has to do with elegance? This sounds surprising.

Well I’m not certain that’s the case, it’s my guess because the language has a strong academic background and is also used for teaching purposes, and many functions are written like they would be in a textbook chapter on recursion, with the topmost stack frame holding a pointer to the front of the list, meaning you can blow the stack on large inputs. Why else would it be done that way? Certainly possible I’m missing something.
It's not just about theoretical elegance. The list data structure is massively inefficient. Using lists large enough to blow the stack with recursion is a really good indication that something has gone really wrong and the program design needs rethinking.
The fact that lists are inefficient doesn’t mean that they should be processed in a maximally inefficient way. I would argue the opposite is true. There’s no practical justification for forcing the compiler to allocate a new stack frame when you can get it to use a jump instead.
There is actually a practical justification–simplicity of the implementation. For a data structure not meant to be used for large datasets, why optimize for that use case? In any case thanks to TRMC landing in OCaml 4.14+, it's a moot question, we'll be able to have our cake and eat it too. Doubtless someone will add the annotation to the standard library soon enough: https://v2.ocaml.org/releases/4.14/manual/tail_mod_cons.html
> There is actually a practical justification–simplicity of the implementation.

For a function that is going to be written once and called innumerable times, this is a bad justification. You should optimize for the consumers of your library, not for yourself.

> For a data structure not meant to be used for large datasets, why optimize for that use case?

Because it’s not the job of a standard library to be opinionated about how people use data structures. It’s the standard library’s job to do what is asked of it as efficiently as possible. The best implementation would be specialized for small Ns but they don’t do that either.

Good to hear about TRMC, I haven’t been a daily user of OCaml since 4.07, but it’s good to hear that they’re addressing some of these pain points. I still think Core and Base are a better starting point for real-world applications in general.

> Because it’s not the job of a standard library to be opinionated about how people use data structures.

Well, they're not being opinionated about it per se, they're just not supporting (up until now at least) highly-optimized use of lists. There are tradeoffs to consider here beyond what we have discussed in this thread. Check the OCaml forum threads on optimizing list mapping for the various considerations.

You characterized this deficiency in light of your own belief that lists should not be used, so I was responding to that part that I disagree with. It’s like Python purposefully crippling lambdas to discourage their use. If you don’t think people should use a feature, just don’t add it.

Incidentally I agree with you about lists and only meant to use it as an example of a place where the stdlib authors chose a simple implementation over one that does what you expect. As soon as I started reading the stdlib source and seeing things like that, I immediately understood why Core exists and realized I wouldn’t be able to trust the stdlib in production. It violates the principle of least surprise. It’s one thing to do stuff like this in Haskell where it makes sense because of lazy evaluation, it’s another to do it in an eager language which acknowledges the need to make concessions to practical usage.

Well, if you bring up Haskell, let's remember that String is defined as a list of Char, and in production most people seem to use a variety of other replacement string types and everyone will tell you not to use Haskell String for any serious production use.

Also,

> violates the principle of least surprise

I suppose yes if you don't read the docs which point out that it's not tail-recursive, and a few lines below a tail-recursive alternative `rev_map` is given.

> Well, if you bring up Haskell, let's remember that String is defined as a list of Char, and in production most people seem to use a variety of other replacement string types and everyone will tell you not to use Haskell String for any serious production use.

The point is that this pattern makes sense in a lazy-evaluated language, not that everything Haskell does makes sense. I think it's a less defensible choice than OCaml in most cases.

> I suppose yes if you don't read the docs which point out that it's not tail-recursive, and a few lines below a tail-recursive alternative `rev_map` is given.

I read the docs and I still disagree with the choice made here. There's no need to include two functions when one will do. This is not pragmatism, it's laziness.

> There's no need to include two functions when one will do. This is not pragmatism, it's laziness.

If providing two slightly different ways to do something is laziness, then pretty much every language is guilty of that. Also, Larry Wall: 'Laziness is a virtue' ;-)

But again that's not a justification. There's no reason for there to be two functions here, other than that it's easier for the implementers. I'm being serious when I say I don't think I've ever seen another standard library that prioritizes ease of maintenance over ease of consumption. Quite possible that I'm missing something! Maybe Common Lisp or another language nobody uses?
Well, you're extrapolating from one example where the standard library doesn't behave as you want, and characterizing the entire library as 'lazy'. I don't agree with that characterization, and as I already said, the justification has been given. You just don't agree with it, that's fine. It's still a valid justification.
> Why else would it be done that way?

My guess would be ease of maintenance. OCaml has a small team of people working on it that are already bringing improvements to the language that are multi year projects (multicore recently, modular implicits may be next and will take a long time too).

On the other hand, from what I understand, they also added to the compiler something to solve this problem for every class of problem that looks like this: https://v2.ocaml.org/manual/tail_mod_cons.html.

That leaves the question: why not fix the stdlib List.map while waiting for the tail modulo constructor? I honestly don't know. My guess would be that it was easy for everyone that wanted to replace it to either do it or use an array, but that's just a guess.

I don't know about everybody else, but I don't find the "list of language features" format that helpful. It's quite dry and it doesn't contextualize how people use these features or what patterns are common. The Guided Tour part at the beginning is the best part IMO. I wish that'd be the entire book. Just a long guided tour of OCaml through the lens of an in depth project, say an interpreter of an ML language.
I'm sure this book is an excellent resource (upon skimming the online version, it looks very thorough and well thought out). I think there is some confusion about who "the masses" are though. I don't think a subchapter titled "POLYMORPHIC VARIANT SUBTYPING" is necessarily for—what I consider—"the masses."
I don't necessarily begrudge the authors for including chapters on advanced topics, it's not like people have to read the whole book. But you raise a valid point. For languages like OCaml that are both large and also veer into PL research territory, some guidance about how many of the advanced topics one needs to understand to be a "real world" programmer could be a big help to the language's adoption.
Yeah that’s a great point. I’ve been writing front-end with reasonml (an ocaml syntax) for a couple years now and I’ve gotten a lot of benefit without writing functors at all.
(comment deleted)
An easier introductory book:

https://ocaml-book.com/

It was published in 2013, though. Much has changed in terms of tooling, right?
Opam (the CPAN/PIP) counterpart will handle the library cases fine.
Yes but it is not really relevant for that book. OCaml has an incredible backward compatibility history, and the linked book is about learning the language itself, not its ecosystem.

Things are different for RWO since it relies also on a number of extra tools and libraries which, indeed, change over time (sometimes also substantially)

The OCaml Software Foundation recently gave the author a grant to update the code in the book to the latest release of OCaml, 4.14, and release the new version of it online in HTML and PDF versions.

You can read it for free online at https://johnwhitington.net/ocamlfromtheverybeginning/

(comment deleted)
This is great live/github based book.

Other great resources are Michael R. Clarkson's (from Cornell) videos [0] and book-like format [1].

I took a lot of inspiration from it when playing with rb-trees [2] and functional, ocaml-looking typescript in general [3].

[0] https://www.youtube.com/playlist?list=PLre5AT9JnKShBOPeuiD9b...

[1] https://cs3110.github.io/textbook/cover.html

[2] https://github.com/preludejs/rb-tree

[3] https://github.com/preludejs

The design of OCaml/Reason is the sweet spot of functional programming for me but every time I must deal with Dune after working with the tooling of Go or Rust I want to stab myself in the face repeatedly and I think that maybe that might just be related to the general concept of "masses" + OCaml.
Sorry could you explain your comment some more? Genuinely curious what is bad about dune. Just started using it
We can start with that, upon starting down the road to learning a (IMO great) but very, very different syntax than most other languages in OCaml, Dune then asks you to write a lisp file to do basic configuration and I suspect 90%+ people who want to explore just say "fuck it" the second they internalize this.

I'm sure there's a crowd, especially here on HN, that views this as some sort of purity test for the language, but in my opinion it's needlessly obtuse over <insert more readable and traditional and lintable type here> used by modern build systems.

Then there's the fact that several `init` commands don't actually fully `init` a project (and up until recently, the very example in OCaml's getting started documentation was broken).

Then there's this insanity

    dune build
    dune test
    dune exec # hahaha no, no, this doesn't work.
Then you gotta manually tape together crap between opam and dune (and don't get me started on how insane the lockfile situation is -- I swear to god I complained once and someone told me I should learn to use nix!).

This is all very fun and yak shave-ey and I generally grin and bear it, but boy do I totally get why most people look at me like I'm a weirdo because I like this language.

I think there's a lot of strawmen here, many quite analogous to those used to (unfairly IMO) criticize OCaml itself.

Complaining about sexprs, when one's baseline is the completely ad-hoc nature of go.mod files, or cargo toml files, is pretty silly. Further, sexprs are a very common general-purpose serialization path for OCaml values, so dune's selection here is hardly arbitrary.

Yes, `dune build`, `dune runtest`, and `dune exec ...` do "work", i.e. each command has its semantics, which are pretty extensively documented IMO. Is the complaint that they're not the same commands that one uses with cargo or whatever?

You're right that opam and dune used to not interoperate much (or at all), but that has changed a lot in the last year or so, to the point that, _if you want_, you don't have to write opam files at all (i.e. they'll be generated from metadata in your `dune-project` file). Again, well documented, at least IMO: https://dune.readthedocs.io/en/stable/opam.html

The "lockfile situation" is pretty good AFAICT. I've been using them for a year+ with good / expected results.

You're right about `dune init` not doing the right things. I believe that's been resolved very recently IIRC, but then again, I can't recall the last language/build tool I used init-like functionality with; I almost always just copy over config/structure from another project I've worked on, and tweak from there.

There are absolutely real critiques of dune, but "it's not cargo" or whatever is silly.

Hmm, let's dive a little deeper.

Dune itself is fine, and even great once everything is working. It's just that you have to mug through tons of documentation that's arranged in pure reference format, instead of in a task-based format, before it's up and running.

Sure, dune generates opam files...except for the (some pretty important) parts it doesn't support. I've had to fall back to opam template files in two different projects to express things that dune-project format doesn't support. It's a leaky abstraction over opam format.

The lockfile situation is incredibly rudimentary compared to Go or even to npm. Check out https://go.dev/blog/supply-chain and tell me opam has 1/5th of the capabilities of go.sum checksums and their overall supply chain security. I know this was a concern for you pretty recently: https://discuss.ocaml.org/t/opam-repository-security-and-dat...

Dune init is actually the one really good piece of UX in dune. Being able to run a command and generate a valid dune component (e.g. not having to remember the `(preprocess (pps ppx_bla))` dance and just being able to do `dune init --ppx=ppx_bla` is pretty cool. I feel like 'I just endlessly copy configs from other places' is not really a point of pride. It reminds me of the bad old days of init shell scripts before systemd :-)

I'm trying not to make this just about dune. Dune is curtainly BETTER than, say Make n friend to newcomers.

But there's a market for programming languages that OCaml is objectively losing. Can we really not agree that the most popular/recommended build tool requiring you to write a lisp to do basic config probably isn't helping draw new user into the fold?

I doesn't have to be cargo. But the more it rhymes with that degree of user experience, the more the "masses" will discover the things that make ocaml great.

I agree that dune is worse in many important ways than the build systems of languages like Rust and Go (where they were developed at the same time as the language, rather than 20 years later). But dune config files aren't lisp. They're s-expressions, which have the same syntax as lisp but are just static data like json/yaml/toml (and once you get used to them, I think most people prefer them to those).
A couple of years ago I gave myself two evenings to setup and start to learn Ocaml. I gave up due to the tooling. Nothing worked, many things weren't obvious or explained. This is much better with Haskell, Rust, F#, ...
Dune is a great "window" in my experience with OCaml, it forces you to do more than with every other language but you end up with a better understanding of everything.
This is fair. It's also a reason the "masses" are going to pick up Go or Rust instead.
That's how I think things will go too. I really enjoy the time I spend with OCaml but whenever I need to do something I use JS because it gives me the frustration in lower doses if that makes sense. I learn less but sometimes I don't want to learn, I want to do.
Rust has a larger ecosystem, but it's not even on the same planet as ocaml in terms of expressiveness.

Kind of disappointed facebook didn't run with ocaml, instead of doing the whole Reason thing. They could have been contributing to and using ocaml directly, instead they decided that programmers will freak out if there's no curly braces.

> but it's not even on the same planet as ocaml in terms of expressiveness.

It's also not on the same planet with tooling pain points.

I think it is on the same planet. Maybe not wrt ecosystem, but apart from rust having a testing built into the language I find them both about the same.
I spent so much time reading the horrible documentation of Dune and I don't think I learned much about the internals of OCaml.
I didn't learn about the internals of OCaml either, I learned about dunea and how it works. I feel like I know it better than npm or cargo since I had to do a bit more manually.
What's wrong with dune? I thought it was really good.
Horrible doc, the configuration files are a made-up language with no extension, it is completely detached from managing the versions of your dependencies, or their source of origin (package manager? vendored? patched? internal?), horrible CLI, transitive dependencies are imported by default and it's hard to not do that, etc.

And then you try to use it in combination with opam and you want to blow your head.

- thought the docs were good. Same level as cargo or npm or anything else I've used.

- the 'made up langauge' is just s-expressions, not any more made up than NPM using a json file. There's no standard file extension for them though, which is fair.

- I'll take your word on the rest of them, I didn't do anything heavy duty

> - I'll take your word on the rest of them, I didn't do anything heavy duty

TBH, that's why you thought the docs were good

> - the 'made up langauge' is just s-expressions, not any more made up than NPM using a json file. There's no standard file extension for them though, which is fair.

The lack of extension is really the problem, as your editor will not format it or add syntax highlighting. It's very 90's

I concur as well that Dune is lacking.

Compared to Rust or NPM.

Shoot even dotnet/nugget is significantly ahead of Dune.

I haven't spent enough time with dune to form an opinion (go and cargo are absolutely marvelous though) but a quick search[0] reveals that dune is not the only option.

[0] <https://github.com/ocaml-community/awesome-ocaml#package-man...>

The better place to get tooling info is the OCaml Platform page: https://ocaml.org/docs/platform

There you can see that dune is the only actively maintained viable build system for OCaml. The others are all in maintenance mode or deprecated.

cool! always happy to see more of this.

i learned out of the unpublished french ora book "developing applications with ocaml". (incidentally it was so useful i had a copy shop bind me a custom "book")

Neat! I didn't know there was a 2nd edition. Does anyone know how it differs from the 1st edition?
Totally unrelated to the link/book, but sparked a question that I've had for a while (curious to hear any answers) --> when you take an initial look at a programming language that you're completely unfamiliar with, and you glance through the syntax of all the basic constructs, do you ever have a particularly strong reaction (be it negative or positive), and if so have you tried to identify why that might be the case?
such a good question! I don't think I've ever had a particularly strong guttural reaction to a language, but I'm definitively biased and either see things that I quite like or dislike in a new language I see.

As I think about why those are I think one of the biggest reasons is familiarity, it's easy to get comfortable doing one thing and it truly sucks to feel like you don't know anything again (I think somebody called this the curse of knowledge? Idk).

Lately I've been intentionally the unknown, or, those languages that I'd look at and be like, "but why!?", for example, why would I want to learn or only use recursion when a for/while has worked for pretty much my whole career? or, following the same functional languages traits, why immutable data? seems like a step backwards?

But the thing is that people, quite likely smarter than me, or at least, a lot more familiar with challenges that I'm not familiar with, came up with solutions that diverge from what I know... and I want to know why! Why they came with immutable data or recursion or a language that pretty much everything is a list (LISP)... and the answers are quite surprising and at the same time I feel that joy of discovery that has almost been erased by years in the industry.

So when you come across a language that you have strong feelings/opinions about, wearing the shoes of the people using it might be quite enlightening and enjoyable.

While I say this, I'm a complete hypocrite because I won't give such courtesy to OOP... but hey, none of us are perfect ;-)

1 - At a high level, I ask myself if the language is pushing me towards the pit of success or the put of failure. More concretely I ask myself "given the class of problems appropriate for this language, are good abstractions easier to create than bad abstractions?". If so, I have a positive reaction. If the reverse is true, and it's easier to make bad abstractions (think early Java where functions were not first class), then I have a negative reaction.

2 - If the language is trying to sell you on a certain paradigm? If so, is there a good escape hatch? This at least means a good FFI like that found in Erlang, but also within the language, OCaml's refs which allow you to write imperative code are a great example. Elm is a language that does this badly, 0.19 removed FFIs and the language is much reduced for it.

3. Is packaging a first class consideration of the language? This is one of the places where say, Rust shines and Python really suffers. I'm going to need packages and if managing them is hard I'm going to get mad.

4. Does the language free me from some class of problems? Big positive.

Some languages I've liked: Common Lisp, C, OCaml, Haskell, Python2, Javascript ES5, Racket, Erlang

Some languages I've disliked: Java5 and earlier, C++, Go, Python3, PHP circa 1999

I had this very experience today! I was looking at some code for markdown parsers (I'm trying to build one in Hare), and I stumbled upon a Nim example. I don't know what it was, but just seeing how complex the author made what seemed like a simple struct to me filled me with dread. I'm sure if I took the time to understand the language it would make sense, but Nim is just one of those languages you need a second glance to understand if you're not already familiar with it.
Easy to answer for me personally. I don't specifically care about the syntax family of the language, but I found Haskell (and functional Scala) to be pretty much unreadable. I have no problems with Lisp dialects or ML, or C-family or a lot of others... but Haskell with its heavy use of symbols instead of words... nope. I seem to need a certain 'instant hook' from which I can learn. If there are only symbols it won't work. Maybe that's also why I always found reading and writing math much harder than grasping the concepts. (I studied Computer Science, not real Math ;))