Search code examples
ocamllazy-evaluation

Does OCaml support call-by-name parameter passing?


Scala allows function parameters to be call-by-name using the '=>' syntax. For example, the boolean and function can be defined as:

def and(x: Boolean, y: => Boolean): Boolean =
  if (x) y else false

The second parameter is call-by-name. This ensures the expected "short-circuiting" behaviour – if x is false, y will not be evaluated at all.

In general, this is useful for functions where a parameter might not be used at all.

How can this be done in OCaml?

I am aware that OCaml has constructs for lazy evaluation, but it is not clear to me if and how these could be used for call-by-name parameter passing.


Solution

  • First of all, by construction, boolean operators are lazy:

    # false || (print_endline "second"; true);;
    second
    - : bool = true
    # true || (print_endline "second"; true);;
    - : bool = true
    # true && (print_endline "second"; false);;
    second
    - : bool = false
    # false && (print_endline "second"; false);;
    - : bool = false
    

    Then, for more custom functions, you should indeed use lazy (with the huge benefit that lazy values are memoised after being evaluated):

    let fib n =
      let rec aux n b a =
        Printf.printf "%d %d %d\n" n b a;
        if n <= 0 then a else aux (n - 1) (a + b) b
      in
      aux n 1 0
    
    let f a b = if b > 0 then Lazy.force a + 1 else b
    (* val f : int lazy_t -> int -> int = <fun> *)
    
    let () =
      let r = lazy (fib 10) in
      let r2 = lazy (fib 1000000) in
      Printf.printf "%d\n" (f r 1 + f r 2 + f r2 0)
    

    The result will be:

    10 1 0
    9 1 1
    8 2 1
    7 3 2
    6 5 3
    5 8 5
    4 13 8
    3 21 13
    2 34 21
    1 55 34
    0 89 55
    112
    

    r has only be evaluated once and r2 has never been evaluated.