[Blog] 7 OCaml Gotchas

Hi everyone! :wave:

I’ve been using OCaml for a while, and I’m quite enjoying the language. In my not-so-long journey, I discovered a few surprising OCaml behaviours, so I decided to share them with everyone in a blog post.

I hope it reduces frustration for newcomers when they see something unexpected for the first time!

12 Likes

Hi!

Thanks for sharing!

I have a two small comments.
In the 4th section, " Type inference doesn’t work well: Part 1". For record you can use the following syntax: book.Book.words. Also note that you need to use it once then you can use book.title without the extra Book.
And in the 7th section, if you need to control the order of the argument, the safe way is to do:

let arg1 = expr1 in
let arg2 = expr2 in
f arg1 arg2
3 Likes

Regarding equality functions, you advise to configure your linter to warn on physical equality, but many style guides prefer warning on structural equality instead. The reason is that physical equality has a well-defined meaning on all types (even if it’s not the one you’re expecting), while structural equality is defined in terms of memory representation, and can throw exceptions or loop indefinitely on some inputs. Even when it does terminate, it is not always useful:

# module ISet = Set.Make(Int);;
...
# open ISet;;
# add 0 (add 1 empty) = add 1 (add 0 empty);;
- : bool = false
# equal (add 0 (add 1 empty)) (add 1 (add 0 empty));;
- : bool = true

(equal here is ISet.equal, the set equality function that has the expected semantics)

4 Likes

I’m aware about this syntax. In fact, my article originally mentioned this approach (and we use it a lot at work). However, I realised that I hate this syntax. So I decided to avoid encouraging syntax I don’t like in my post :sweat_smile:

For record you can use the following syntax: book.Book.words . Also note that you need to use it once then you can use book.title without the extra Book .

That’s a good simple solution as well, thanks for mentioning it!

3 Likes

Indeed, structural equality is not the optimal solution either :disappointed:

You mentioned one reason. Another reason is that it throws an exception when you try to compare functions (so you may suddenly get runtime errors).

I’d recommend using the equal function from a structure of choice. Still, I feel like the Haskell solution with the Eq typeclass is so far the best solution for a polymorphic well-having equality comparison. I’m trying to cope with the OCaml approach meanwhile :sweat_smile:

2 Likes

I understand it’s not your personal preference, but somehow doesn’t feel right to hide it from your readers. Let them make their own judgment. Otherwise the article seems to be giving them a skewed view of the language.

3 Likes

Any more practical examples of the 6th gotcha? I have never seen that notation used outside of GADT function definitions.

1 Like

I believe it’s fine to provide a personal opinion in an article written on my website :relieved:

If it was published somewhere on the official OCaml documentation then it should be more objective and less personal.

1 Like

I sometimes like to write OCaml functions in Haskell style, where type signature leaves nearby the code.

A few examples:

let unlines : string list -> string = String.concat ~sep:"\n"
let unwords : string list -> string = String.concat ~sep:" "

These ones are monomorphic but I can imagine writing polymorphic functions too.

1 Like

I kind of get what you’re going for with the last one, but I think this may be a holdover from your 8 years of Haskell programming :). Your function/operator:

let (>>) action1 action2 = action1; action2

has type unit -> 'a -> 'a, making it essentially just syntactic sugar for ; (but with the evaluation order inverted). I can imagine you’re expecting action1 and action2 to be evaluated within the body of this function but because OCaml is eagerly evaluated, you’re essentially saying

() >> ()

by the time the function is called. The larger point of the RTL evaluation order still stands though.

1 Like

Ngl, I found it hard to find real-life examples of where this behaviour can be problematic :sweat_smile:

Again, after 8 years of Haskell, I don’t rely on order of arguments at all, and prefer to control it explicitly!

Still, I see that many people find this behaviour surprising (myself included). Especially considering that this is different from all mainstream languages. Although, for me it’s not a big problem :relieved:

1 Like

Yeah I think that’s a fair assessment – it’s not something that should really ever be an issue unless the two arguments share some mutual mutable state (here that’s stdout). I agree that it’s a gotcha but it should be a pretty rare “bug” to encounter in the real world.

2 Likes

The order of evaluation of functions argument is not really right-to-left : it is unspecified which means that different implementations of OCaml could implement whatever order they want. Of course in practice, people ends up relying on the evaluation order by accodent so the compiler will not willy-nilly change the evaluation order for the sake of backward compatibility. However, fundamentally any code that relies on a specific evaluation order of function arguments is bugged.

4 Likes

In C and C++, the evaluation order of arguments (and of sub-expressions) is unspecified. The same is true of scheme. (The latter not a mainstream language but I think C and C++ would count as mainstream.)

1 Like

I think a more convincing example for the last one would be something like:

let user = find_user (prompt_id ()) (prompt_pwd ()) in ...

which will flip the id and the password. Your (>>) function just doesn’t make sense, it isn’t doing anything. In general you cannot do anything about the side-effects of computing the arguments from inside a function. If your arguments are genuinely lazy then you control the order of evaluation so there is no gotcha.

1 Like

This is a good example, thanks for providing it!

1 Like

I was referring to the a . a notation using a dot in the function’s signature

1 Like

Book.(book.words) will also work if you want C style

1 Like

This is not the same though. This local open might be bringing this book value in the environment. So one must be much more careful when using opens vs the value.Module.field syntax (what’s the name of this syntax btw?)

3 Likes

Based on the manual I don’t see an explicit name for this syntax: OCaml - The OCaml language

From the wording I guess it’s OK to call it a ‘prefixed field name’?

1 Like