Tuple and function execution order

I have a very basic OCaml program that reads two lines and adds them to a tuple. I expect that this tuple contains two strings in the order user inputs them (s1, s2) but strings placed in reversed order (s2, s1). Could someone explain this behavior or point me to the docs? Thanks.

let () =  (read_line(), read_line()) |> (fun (s1, s2) -> Printf.printf "%s\n%s\n" s1 s2);;

Hello Andrei,

The order of evaluation of sub-expressions making up a tuple expression is not specified:

http://caml.inria.fr/pub/docs/manual-ocaml/expr.html#sec151

Most of the time, it does not matter, but typically, if the sub-expressions do side effects, you have to explicit the order of evaluation to avoid “surprises”. So you can rewrite your piece of code like this to get what you expected:

let () = 
  let s1 = read_line () in
  let s2 = read_line () in
  Printf.printf "%s\n%s\n" s1 s2

Note that the order of evaluation of expressions making up a function application is unspecified too:

http://caml.inria.fr/pub/docs/manual-ocaml/expr.html#sec138

4 Likes

Thanks for the great explanation!

Back when Caml-light started out, this “unspecified order of evaluation” meant “right-to-left” for function applications, and that allowed a runtime optimization that dramatically reduced consing. For a big memory win.

3 Likes

That sounds super interesting! Do you have any more information on how the optimization worked?

IIRC it’s all in Xavier’s master’s thesis ZINC: An Economical Implementation of ML. That sucker, its a wonderful piece of work. I read it several times over the years and recommend it to students. Lovely piece of work.

People forget that at the time, all ML implementations were heavy – big-ass memory consumption [viz. SML/NJ, but also the ones based on LISP VMs]. Xavier proved that it was possible to really cut that down and run on modest hardware while paying only a modest penalty in performance. Which (of course) was recouped by the eventual native-code compiler.

3 Likes

Besides the original paper, see this old comment: Why are OCaml function arguments evaluated right-to-left?

As for documentation, I’ve seen some unpublished notes that describe this behavior for various language constructs, including records and labeled arguments. I could probably get the author to post them.

Thanks for the links, that makes a lot of sense.

Interestingly, the order of evaluation for tuple inside a match statement seems to be from left to right.

Heh: turns out, it differs between the native-code and byte-code execution environment (at least, last I checked – this is what’s really meant by “unspecified”: “we can change it anytime we want”).

It’s a wonderful and lovely thing. And I’m not joking. It was quite a lesson to me, just how much value that … “ambiguity” was worth, at a time when memory and cycles really, really, really mattered.

2 Likes