When OCaml (or interpreter.ml) evaluates an expression, eg:
x + 7 * y
It (generates code that) evaluates x, y,7*val(y), and val(x)+val(7*y)
Evaluate (n=0), then if true, evaluate e1, otherwise e2.
let rec f n = if n=0 then 1 else n*(f (n-1))
must be evaluated in this order!
Another way to think of evaluation: rewriting expressions (trees):
…until we reach a normal form expression that can’t be reduced.
Starting from
With n=4 we eventually get to
How do we re-write (f (n-1))? Considering also “chained applications” like ((f g) h) or f (g h) n…
Answer: the “application” operator @@, defined by:
Now we need a rule for evaluating f @@ e.
There are two common rules for evaluating e1 @@ e2…
Call by Value
e2 to get value ve1 to get function body ff with param → vCall by Name
e1 to get function body ff with param → e2The main difference is when evaluating applications to applications:
(fun y -> ... y ...) (g x) ≡ ( ... (g x) ...)Example: using these definitions
let rec range i n = if i>=n then [] else i::(range (i+1) n)
let rec take n lst = match (n,lst) with
| (0,_) | (_,[]) -> []
| (n,(h::t)) -> h::(take (n-1) t)and evaluating the expression
Evaluation stops when a normal form, or value is reached:
fun x -> e)Call-by-value is also called applicative order or eager evaluation, and is the more common eval rule. (used in Python, Java, C/C++, OCaml,…)
Call-by-name is also called lazy or normal-order evaluation.
cs2041.org