Do any OCaml compilers take advantage of the unspecified order of evaluation of let ... and bindings?

55 Views Asked by At

According to the OCaml manual

let pattern_1 = expr_1 and … and pattern_n = expr_n in expr .... evaluates expr_1 … expr_n in some unspecified order ....`

Does any compiler or rutnime for OCaml take advantage of the order being unspecified? For example, ocamlopt and ocamlc+ocamlrun in practice use a distinct evaluation order for function arguments and I was wondering if something similar arises for let ... and.

I wasn't able to find any case where the evaluation order isn't left-to-right, here's some code that prints 123 whether I use ocaml or ocamlopt:

let p = Printf.printf "%d%!"

let rec spin = function
  | 0 -> ()
  | n -> spin (n - 1)

let () =
  let x0 = spin 99999999
  and x1 = p 2
  and x2 = p 3 in
  let _ = x0, x1, x2 in
  ()
1

There are 1 best solutions below

0
ivg On BEST ANSWER

First of all. There might be many compilers and some may not be open-sourced and/or released, so it is impossible to answer your question in the way it is stated.

In general, a common practice when defining the semantics of a language is to use statements in their weakest forms, i.e., to leave as much space for maneuver to the compiler. Since the least constrained is the input the more options will have a compiler to produce a more optimal program.

Speaking of the mainstream OCaml distribution, there is no difference1 in the compiled code between let p1 and p2 .. and pN in e and let p1 in let p2 in ... let pN in e provided that p1,...,pN are data independent2. The compiler will produce the same lambda code for both expressions, meaning that even the bytecode will be the same.

More specifically, both expressions will be translated to the lambda form (let (p1 p2 ... pN) e), even if there are data dependencies between the patterns (the compiler will rename all variables, ensuring that all variables are fresh). Since translating to lambda is the first step in the compilation (apart from parsing and typing, which is not strictly a part of a compilation process) the information about the form of the let expression that you used in the source code is lost, so the later stages of compilation, especially the instruction scheduling, will not be able to utilize this hint.

To be honest, the compiler doesn't really need this hint from the developer. The OCaml compiler is clever enough to perform the data and side-effect analysis to re-arrange the terms as much as needed. So the main use of the let p1 and ... pN in e form is to communicate to the readers of the code, i.e., to our fellow programmers, that patterns p1 and pN are independent and could be read independently. Since this vastly reduces the cognition burden (each expression now has a much smaller context) it makes your program more readable. Even with side-effectful expressions, when you write

let x = f () and y = g () in k x y

You explicitly say to the reader that there's no difference in which order f and g are executed, therefore they are independent functions.


1) You can check this by adding the -dlambda option to ocamlopt (or ocamlc).

2) If they have dependencies, it means that they are different programs, so the compiler must produce different code. In other words, there shouldn't be any optimizations.