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 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
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:
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
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!
Indeed, structural equality is not the optimal solution either
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
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.
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.
Ngl, I found it hard to find real-life examples of where this behaviour can be problematic
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
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.
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.
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.)
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.
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?)