What are the biggest reasons newcomers give up on OCaml?

I encourage people to check out the free-form answers in the user survey for answers to the question that opens the thread.

My subjective summary would be that the most commonly-cited pain points are documentation, and the tools (with some people complaining in particular about the documentation of the tools). Other things people mention less frequently include the syntax and the lack of libraries.

I think this thread currently on the front page is also a good illustration of the sort of problems people have with the tools.


I would even like full ocaml scripts instead of a yet another language such as stanza.


I dabbled in OCaml for a bit, I love the type system and the fast compiler. For me the biggest hurdles were:

  • Tooling (Dune / Opam). They are quite confusing and different for someone coming from npm or cargo. Specially the part of pinning dependecies and reinstalling them. Why do I need to learn about switches, why can’t I just have a lockfile.

  • Structuring a project and the lack of namespacing. Figuring out how to structure a project in sub directories in a way that everything works. You need dune files everywhere. You need to learn too much about how dune works in order to structure a project using subdirectories. I had to look at many examples in github to figure out how to reference depencies. Some kind of automatic namespacing based on the file system would be much clearer.

  • Libraries documentation - Most documentation is very poor, the documentation is mostly the function signatures. It is very hard to figure out how to use something just by looking at a bunch of function signatures. Providing examples for each function makes a huge difference.


Or even, providing task-based examples in a ‘tutorial’ or ‘quick reference’ section. With many libraries it’s not obvious which functions are supposed to be the entry point, or how to integrate them into my system, so it’s good to know what the library author intended. As others have said here, some people are using the workaround of looking at the test code.


I totally agree, and it sounds like this should finally be fixed in the next version of dune.

BTW I’m very happy this thread exists. Hopefully we’ll keep updating it.


If documentation seems to be missing then check the .mli, in some cases the examples and documentation you are looking for are present there.
There is a bug in odoc currently which causes the module level documentation to vanish in some cases, and module level documentation is what would contain the examples, e.g. for Re.Posix it gives the impression that there is no documentation, or no examples, which isn’t the case.


I’m really excited about the potential for the new package documentation feature on ocaml.org to change the documentation story for the better. Just having documentation in one centralized place is an enormous improvement on the status quo. I think library authors will be more motivated to write docs when their docs are automatically generated, hosted, and easy for users to find.


That reminds be of other nasty (and quite old) bug (which I’m sure was reported but I don’t remember where), it seems that preambles are often discarded in the automatically generated doc: compare for instance: Tsdl · tsdl 0.9.9 · OCaml Packages and Tsdl (tsdl.Tsdl)
( the whole first paragraph " Consult the binding convention… etc" that can be found in the latter link has disappeared in the former.)

There are many other instances of this bug. The preambles of all modules of the Bogue doc have similarly disappeared.


Indeed you would expect such a bug to be high-priority. Another sad thing about Tsdl’s doc is that sections headers have links into the original SDL docs but odoc drops them, see this Cross-references and links are not allowed in headers · Issue #342 · ocaml/odoc · GitHub.

How can we motivate programmers to write docs if the tooling does not respect your work ?


If I might pour some petrol on this fire: every other language I see does at the very least color syntax highlighting of code in documentation and other type inferred languages provide tooltips in the browser so you can see the inferred types as if you were using an IDE.

No need to increase your carbon footprint on that one. odoc does syntax highlighting in code samples (random example). Inferring and showing the tooltips would be a more complicated business, since you’d need to compile the samples.


Yes ocaml.org’s doc inclusion has been really helpful. It’s one step more towards becoming npmjs and crates.io-like. I think adding autocomplete on the home page’s nav search bar is another nice incremental add.

1 Like

Yes! Although I’d personally prefer using the full power of font faces rather than colors, think Algol (bold keywords, highlight, italics, dim, etc…). The usual way to approach compiler output for examples is to hook the docs to a playground. A great example of executable examples is rust docs, and for tooltips is koka’s docs.

1 Like

I can consider myself as a newcomer. I have just rewriten a “real” program made to patch a bunch of MIDI files. It was written in Python, and I rewrote it in Ocaml.

I was guided toward the Janestreet’s Base and Stdio libraries by Real World OCaml… but it is quite hard to figure out what does the different functions. For example In_channel (stdio.Stdio.In_channel) doesn’t describe the semantic of all functions. I have picked input thinking it would read the whole file and return the read length. Only 65536 chars where read ! I should have written really_input_exn. Then I am puzzled… Should I use the StdLib which is better documented, or Base which I have read is better (and the reference library of the book I have purchased) ? (Python users don’t ask these type of question). (The Base libraries also lack Bytes.get_int32_be and comparable functions… then I have to deal with both documentations).

Some constructions are not friendly. Just compare

buffer[pos] += transposition


Caml.Bytes.set_uint8 buffer !pos ((Caml.Bytes.get_uint8 buffer !pos) + !ref_transposition)

But I have to admit that having a reference to a variable (the current position in a buffer) and pass it to a function is quite handy (In Python, I can only pass the value… and get in return the new value).

Then, I am puzzled… I have found Ocaml less surpising by moment (just forget a global keyword in Python…). Quite surprizing sometimes (if I forget some parenthesis). Since I am using Windows and didn’t manage to make lablgtk, lablgtk3, labltk work with Diskuv, I guess I should wait Ocaml5 + Opam2.2 (tier 1). Or try opam-repository-mingw. I have to admit that with Python, it is more straightforward (the Windows installer install Tkinter).

I know that most of this post doesn’t deal with the core language, but the choice of a language is also the choice of an ecosystem. And this is important.


This reminds me that I am still planning to work on improving the documentation for the new {In,Out}_channel of the standard library, but didn’t get the time to do it yet. If anyone around here is interested in submitting a contribution to the OCaml compiler codebase, I think it would be a fairly nice change that would benefit a lot of users in the future:

1 Like

Unfortunately, there is no single answer to this question. Both are perfectly usable. The standard library is probably the smallest of the standard libraries out there and somewhat idiosyncratic in parts due to its long history. Base and Core are more featureful but they are also more opinionated in their choices, so they may be a good fit if you agree with those choices, or not, if you don’t :slight_smile:

I think the only way to decide is to try both and see what feels better. My personal recommendation is to start with the standard library and only look elsewhere if you feel the need.

As a data point, at LexiFi we use the standard library and are very happy with it.

You should define a small helper function to help you in these cases (and to avoid mistakes):

let incr buf pos n =
  Caml.Bytes.set_uint8 buffer pos (Caml.Bytes.get_uint8 buffer pos + n)

then you can write incr buf !pos !ref_transposition, roughly as short as the Python equivalent.



Another approach would be to use user-defined indexing operators.

let ( .%() ) = Bytes.get_int8
let ( .%() <- ) = Bytes.set_int8

(* usage example: *)
let incr buf pos n =
      buf.%(pos) <- buf.%(pos) + n

in absence of overloading, this does not scale nicely to supporting all integer sizes in a readable way, but if you are heavily working with just int8 it is worth a try.


It is also possible to define multiple module for each integer size:

module Int8 = struct
  let ( .%() ) = Bytes.get_int8
  let ( .%() <- ) = Bytes.set_int8
module Int16 = struct
  let ( .%() ) = Bytes.get_int16_ne
  let ( .%() <- ) = Bytes.set_int16_ne

and use opens and qualified names to mix them in a single function:

let incr_four buf pos n =
  let open Int8 in
  buf.%(pos) <- buf.%(pos) + n;
  buf.%(pos+1) <- buf.%(pos+1) + n;
  buf.Int16.%(pos+2) <- buf.Int16.%(pos+2) + n

Thanks for these hints… I haven’t seen (.%()<-) which seems to be an extension of the core language.

Here Error: Library "netcgi2" not found - #9 by geoffder is an other series of issue… and I guess that having a well integrated interface with Apache is not something exotic if I want something done which could be done easily in Perl, Python, PHP, Java (tomcat)…

Perhaps I should go to a pure Ocaml way (Dream ?) and have Apache relaying from HTTPS:443 to Dream:8080… (I am not sure Dream could interface with letscrypt)

The answer « Condition moved from systhreads into the stdlib recently, so I suspect there is a versioning issue. » seems to indicate that we can have something working today, and not tomorrow ! Perhaps we should have something in a Debian way. A testing release could break some times, but before a switch to a stable release a freeze period prevents something nasty then the stable release is born, evolves with bug correction and not something which could break too many things.

Debian/stable could be boring, there are not many things very new, but it works.

I didn’t mean to imply that something could work “today, not tomorrow”, when I’ve had similar errors it was my fault. e.g. Using Out_channel, but not bumping my ocaml dependency version to 4.14 where the module exists.