How do you stay productive in OCaml?

I am not sure if I’m doing something wrong;

Here it is:

  • I try to develop something
  • No getting started guides (except for libraries like Dream, which looks like something I am used to in other languages)
  • Consuming libraries takes awhile because documentation is sparse and requires reading of source code, but that’s incredibly time consuming - spend a few hours reading source code, before being able to start
  • reach a brick wall
  • try to find what others that encountered such issues have done
  • there are no others people that have encountered such issues
  • post in discuss.ocaml.org

Do people not encounter issues? Or there is a better place to ask and search? Where do you find simple examples?

15 Likes

It’s probably worth listing some examples of what you’ve tried to write; for different classes of things, there’ll be different ways of approaching the problem.

Ror example, when it comes to writing lexers/parsers, I look for inspiration in existing ones, e.g. the OCaml lexer/parser.

Did you try reading not the source code but only interface files? Did you manage to get an enlightenment from them? If no, could you describe examples of these library and we could try figure out where exactly the problem is.

2 Likes

I don’t know what is the current recommended practice, but I confirm that the first thing I do when trying to use a new library is to read the .mli. Typically that gives you enough information to get started. In fact one of the main motivation for having .mli files (as opposed to, say, having the signatures inlined in .ml files) is that they serve as documentation.

Cheers,
Nicolas

4 Likes

I don’t know if it counts as a getting started guide, but there is a very good (if somewhat opinionated) book aimed at beginners available for free online:

The following book is a bit outdated (it is pre-OPAM), but it is also very good:

The next book (in french) is even more outdated (it dates from when there was no “O” in OCaml), but if you are willing to put up with that, you will be rewarded in spades. In my opinion, one of the best books to learn Caml and programming anywhere:

Finally, and on a more specific subject, if you are interested in classical Unix programming you have the excellent

Cheers,
Nicolas

8 Likes

You are not doing wrong. There is no better place to ask or search.
Programming is a really difficult task no matter what the language / environment / documentation.
Some people are more productive than others, that’s why i program only as a hobby.
Another thing to take into account is that web development is not where ocaml started.
That means most the brilliant girls and guys here are very busy constructing some innovative type system that push the boundaries of what a typed programming language can do.
I don’t say one can’t develop a large web project using ocaml, of course you can, the thing is if you succeed then you can consider yourself as a pioneer.

1 Like

We all go through this process of hitting the brick wall until we break through it … to reach the next brick wall, and so on. Unfortunately a lot of OCaml libraries are not documented in terms of ‘How do I actually use this?’, so it’s a common complaint. You will find many threads about it here. We need to change the culture to get people to write how-to guides for their libraries.

For the libraries where we get lucky, they have an examples directory which shows how to use it in sample projects, or a tests directory which show some usages in unit tests.

10 Likes

These are great starting guides to get into OCaml but the thing that grinds my (and probably OPs) gears the most is the lack of documentation on many open source libraries.

As mentioned by @yawaramin sometimes one is in luck and finds an example folder but ever so often this is just not the case. I don’t really know how to express this correctly, because I’m talking about open source projects provided by volunteers and I don’t want to be ungrateful and complain, but this is the single biggest missed opportunity in the OCaml ecosystem if we want to be more accessible to new developers.

That said, I also feel things are improving. Documentation gets better, just look at the recently released Dream, and the community grows, which results in more code being shared and more blogposts written about usage and design patterns.

7 Likes

I mostly use interfaces to find how to use stuff and merlin. I know that seems weird but most of what you need to know about a library is in the mli you just need to think in a straightway and if you’re doing type driven development that’s quite straightforward.

Which type do I need? Which types do I have?

This is how I think most of the time, then just search for the function that does the transformation.

3 Likes

I think the reality is that most people work off of code snippets they find, and there simply aren’t many of those available for OCaml libraries.

7 Likes

This is accurate. Others have mentioned the difficulty in getting OCaml library authors to write adequate documentation and examples. I have to confess, I’m as guilty as anybody else.

One thing I wonder about: perhaps if people wrote more tests, did more test-driven development, that might end up producing the plethora of examples/snippets you mention, and could be used by beginners.

5 Likes

thanks all, before reading source code I start with reading .mli files as well, which works a lot of the time, but still find myself reading the source code to get started, when I want to find out more about specific arguments and etc.

I think the reality is that most people work off of code snippets they find, and there simply aren’t many of those available for OCaml libraries.

I can’t speak for others, but this is definitely how I work in other languages like C#, F#, JavaScript, TypeScript and Rust: when starting something new / new library: take with a basic example, change it to have something working for my use case, then if there is a need - dive deeper;
I don’t find it works quite as well in OCaml, but I also don’t find many threads with people encountering issues / errors, so I felt like maybe

  • people have a different workflow
  • or work in code bases written in OCaml that most of the time already have plenty of examples to look out for
  • or maybe there’s a French forum (I don’t speak French)

(which is why I started this thread to learn what others do)

2 Likes

Which type do I need? Which types do I have?

Generally I start with the types, but I still find the errors often times rather confusing

For example in this simplified example:

module My_module : sig
  val do_things : 'a list -> f:('a -> 'b) -> 'b list
end = struct
  let rec aux (xs : 'a list) ~(f : 'a -> 'b) ~(acc : 'b list) =
    match xs with [] -> acc | x :: xs -> aux xs ~f ~acc:(f x :: xs)

  let do_things xs ~f = aux xs ~f ~acc:[]
end

I believe that I have been very explicit about what types I want, but the lsp puts lines in the whole module (making it difficult to find where the error really is)

The error in this case is also kind of in the wrong place - despite specifying the wanted type of aux it is inferred as:

'b list -> f:('b -> 'b) -> acc:'b list -> 'b list

These make it difficult to track the simple bug:

-    match xs with [] -> acc | x :: xs -> aux xs ~f ~acc:(f x :: xs)
+    match xs with [] -> acc | x :: xs -> aux xs ~f ~acc:(f x :: acc)

When interfacing with a more complex library such type errors are kind of difficult to troubleshoot and I find myself having to read the source code, create simple examples and etc.

1 Like

Using unification variables is classical mistake when debugging or specifying types. As you have seen,

let id (x:'a) (y:'a) : 'a = x  + 1 

is well-typed, because the variable 'a is an unification type variable that can be unified with int.

Locally abstract types behave better for this purpose:

let rec aux: type a b. a list -> f:(a -> b) ->  acc:b list -> b list =
  fun xs ~f ~acc -> match xs with
  | [] -> acc 
  | x :: xs -> aux xs ~f ~acc:(f x :: xs)

fails with the expected error

 |     | x :: xs -> aux xs ~f ~acc:(f x :: xs)
                                           ^^ 
Error: This expression has type a list but
  an expression was expected of type
        b list
  Type a is not compatible with type b
2 Likes

I wasn’t even aware that this can be an issue - when debugging I’ve always added the externally visible types 'a, 'b and etc.

for completeness adding:

and OCaml - Language extensions
which supports both function syntaxes

  let rec aux : type a b. a list -> f:(a -> b) -> acc:b list -> b list =
   fun xs ~f ~acc ->
    match xs with [] -> acc | x :: xs -> aux xs ~f ~acc:(f x :: acc)
  let rec aux2 (type a b) (xs : a list) ~(f : a -> b) ~(acc : b list) : b list =
    match xs with [] -> acc | x :: xs -> aux xs ~f ~acc:(f x :: acc)

Note that second case only works due to the typo aux xs ~f ~acc:(f x :: acc) in the body of aux2. Otherwise the locally abstract types are introduced too late and it is not possible to call aux2 on arguments whose types contain either a and b (because those types were defined after aux2).

If you want to use that notation you need to first introduce the locally abstract types and then define the recursive function:

let aux2 (type a b)=
   let rec aux2 (xs : a list) ~(f : a -> b) ~(acc : b list) : b list =
    match xs with
    | [] -> acc
    | x :: xs -> aux2 xs ~f ~acc:(f x :: acc)
   in
   aux2

This is starting to be quite subtle, so most of the time it is simpler to use the type a b. ... shorthand notation which works in all cases.

2 Likes

I’m going through similar frustrations, currently feeling stuck with my OCaml project.

I’m using a complicated 3rd party library (Irmin) which does actually have some docs. I’ve navigated the types and got code that compiles, but I’m not getting the result I was expecting. Surely the bug is on my side, but the relevant decision is happening somewhere inside Irmin.

In Python I would just drop into a debugger and step through to understand where I messed up.

But I have learned that ocamldebug can’t be used at all with a threaded program, ruling out my project because Irmin uses Lwt (which I think is a pretty common lib in the OCaml ecosystem wherever file or network IO is happening?)

AFAICT my options here are to temporarily vendor Irmin into my project and add some print debugging, or just try harder at reading the source code and running it in my head. Both of those can work, but it’s a spare-time “for fun” project and so far I’ve lacked the energy to embark of either of those tedious courses just yet.

Are there any alternative debuggers for OCaml that might help here?

I guess it’s one of those things that’s just harder and more cumbersome in a compiled language.

3 Likes

@anentropic maybe you could shoot your questions on Irmin Discussions (Discussions · mirage/irmin · GitHub)?

cc @CraigFe

I don’t mean to minimize the rest of the troubles, and I would not say no to a shiny threaded debugger, and this is more a curiosity question, but isn’t this relatively easy? For simple packages you can git clone the project into a subdirectory of your project and start hacking on it and dune will resolve it locally instead of from the system.

The more complicated packages, where the problem is in a thing that opam packages you use depend on, you have to opam pin add a copy of it and rebuild the world downstream of it. A bit more time consuming but definitely tractable.

This question is similar to other recent threads about getting started with OCaml (especially on Windows) and documentation practices. I think that all of these are related to the fact that the OCaml community is fairly small and, therefore, there’s just not a ton of current introductory and intermediate material out there. This is one of the reasons that we started OCaml Café which (shameless plug!) meets this coming Wednesday at 7pm Central.

I basically follow the same series of steps that @mudrz outlined because I want to understand what others have done, try to figure it out myself, etc. But the OCaml community is super friendly, so I suggest posting sooner rather than later to Discuss (or Reddit or SE).

7 Likes