ocamlsmlsmlnjmlmosml

Evaluation order of let-in expressions with tuples


My old notes on ML say that

let (๐‘ฃโ‚, โ€ฆ , ๐‘ฃโ‚™) = (๐‘กโ‚, โ€ฆ , ๐‘กโ‚™) in ๐‘กโ€ฒ

is a syntactic sugar for

(ฮป ๐‘ฃโ‚™. โ€ฆ (ฮป ๐‘ฃโ‚. ๐‘กโ€ฒ)๐‘กโ‚ โ€ฆ )๐‘กโ‚™

and that

let (๐‘ฃโ‚, ๐‘ฃโ‚‚) = ๐‘ก ๐‘กโ€ฒ in ๐‘กโ€ณ

is equivalent to

let ๐‘ฃ = ๐‘ก ๐‘กโ€ฒ in 
let ๐‘ฃโ‚‚ = snd ๐‘ฃ in 
let ๐‘ฃโ‚ = fst ๐‘ฃ in 
๐‘กโ€ณ

where

I'm wondering whether I got the evaluation order right because I didn't note the original reference. Could anyone ((confirm or reject) and (supply a reference))?


Solution

  • It shouldn't matter whether it's:

    let ๐‘ฃ = ๐‘ก ๐‘กโ€ฒ in 
    let ๐‘ฃโ‚‚ = snd ๐‘ฃ in 
    let ๐‘ฃโ‚ = fst ๐‘ฃ in 
    ๐‘กโ€ณ
    

    Or:

    let ๐‘ฃ = ๐‘ก ๐‘กโ€ฒ in  
    let ๐‘ฃโ‚ = fst ๐‘ฃ in
    let ๐‘ฃโ‚‚ = snd ๐‘ฃ in 
    ๐‘กโ€ณ
    

    Since neither fst nor snd have any side-effects. Side-effects may exist in the evaluation of ๐‘ก ๐‘กโ€ฒ but that's done before the let binding takes place.

    Additionally, as in:

    let (๐‘ฃโ‚, ๐‘ฃโ‚‚) = ๐‘ก ๐‘กโ€ฒ in ๐‘กโ€ณ
    

    Neither ๐‘ฃโ‚ nor ๐‘ฃโ‚‚ is reliant on the value bound to the other to determine its value, so the order in which they're bound is again seemingly irrelevant.

    All of that said, there may be an authoritative answer from those with deeper knowledge of the SML standard or the inner workings of OCaml's implementation. I simply am uncertain of how knowing it will provide any practical benefit.

    Practical test

    As a practical test, running some code where we bind a tuple of multiple expressions with side-effects to observe order of evaluation. In OCaml (5.0.0) the order of evaluation is observed to be right-to-left. We observe tthe same when it comes to evaluating the contents of a list where those expressions have side-effects as well.

    # let f () = print_endline "f"; 1 in
    let g () = print_endline "g"; 2 in
    let h () = print_endline "h"; 3 in
    let (a, b, c) = (f (), g (), h ()) in a + b + c;;
    h
    g
    f
    - : int = 6
    # let f () = print_endline "f"; 1 in
    let g () = print_endline "g"; 2 in
    let h () = print_endline "h"; 3 in
    let (c, b, a) = (h (), g(), f ()) in a + b + c;;
    f
    g
    h
    - : int = 6
    # let f _ = print_endline "f"; 1 in
    let g () = print_endline "g"; 2 in
    let h () = print_endline "h"; 3 in
    let a () = print_endline "a" in
    let b () = print_endline "b" in
    let (c, d, e) = (f [a (); b ()], g (), h ()) in
    c + d + e;;
    h
    g
    b
    a
    f
    - : int = 6
    

    In SML (SML/NJ v110.99.3) we observe the opposite: left-to-right evaluation of expressions.

    - let
    =   fun f() = (print "f\n"; 1)
    =   fun g() = (print "g\n"; 2)
    =   fun h() = (print "h\n"; 3)
    =   val (a, b, c) = (f(), g(), h())
    = in
    =  a + b + c
    = end;
    f
    g
    h
    val it = 6 : int
    - let
    =   fun f() = (print "f\n"; 1)
    =   fun g() = (print "g\n"; 2)
    =   fun h() = (print "h\n"; 3)
    =   val (c, b, a) = (h(), g(), f())
    = in
    =   a + b + c
    = end;
    h
    g
    f
    val it = 6 : int
    - let
    =   fun f _ = (print "f\n"; 1)
    =   fun g() = (print "g\n"; 2)
    =   fun h() = (print "h\n"; 3)
    =   fun a() = print "a\n"
    =   fun b() = print "b\n"
    =   val (c, d, e) = (f [a(), b()], g(), h())
    = in
    =   c + d + e
    = end;
    a
    b
    f
    g
    h
    val it = 6 : int