# Evaluating expressions

evaluating

``(take 2 (range 0 3)) ≡ (take @@ 2) @@ ((range @@ 0) @@ 3)``

by value:

``````(take 2 (0::(range 1 3)))
(take 2 (0::(1::(range 2 3))))
(take 2 (0::1::2::(range 3 3)))
(take 2 (0::1::2::[]))
0::(take 1 1::2::[])
0::1::(take 0 2::[])
0::1::[]``````

by name:

``````(take 2 (0::(range 1 3)))
0::(take 1 (range 1 3))
0::(take 1 (1::(range 2 3)))
0::1::(take 0 (range 2 3))
0::1::[]``````

Evaluation stops when a normal form, or value is reached:

• A constant belonging to a built-in type; or
• A constructor applied to the correct number of arguments (in normal form); or
• A function applied to no arguments (i.e. `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.

The rules can lead to different behavior, e.g. …

``````let rec flip x y = flip y x in
let ignore z = "ignored" in
(ignore (flip 100 200))``````
``````let cdec x  = if x > 64 then x-1 else x
let v = cdec (fib 40)``````

Does call-by-name always use fewer evaluations?

``````let cdec x = if x > 64 then x-1 else x
let v = cdec (fib 40)``````

Call-by-name will evaluate `(fib 40)` twice.

We can avoid this using expression sharing, where expressions are graphs rather than trees.

Call-by-name with sharing is also called call-by-need or lazy evaluation.

# `lazyCaml`

Imagine programming in `lazyCaml`:

``````type 'a tree = Empty | Leaf of 'a | Node of ('a tree) * ('a tree)

let rec dfs t = match t with
| Empty -> []
| Leaf x -> [x]
| Node (lt,rt) -> (dfs lt) @ (dfs rt)``````

Suppose we want to decide if two trees result in the same depth-first search:

``````let rec listeq l1 l2 = match (l1,l2) with
| ([],[]) -> true
| ((h1::t1), (h2::t2)) -> h1=h2 && (listeq t1 t2)
| _ -> false
let eq_dfs t1 t2 = listeq (dfs t1) (dfs t2)``````

What happens when we evaluate:

``eq_dfs Node(Node(Leaf 3, Leaf 7), Leaf 11) Node(Leaf 7, Node(Leaf 3, Leaf 11))``
``list_eq (dfs Node(Node(Leaf 3, Leaf 7), Leaf 11)) (dfs Node(Leaf 7, Node(Leaf 3, Leaf 11)))``
``list_eq ((dfs Node(Leaf 3, Leaf 7)) @ (dfs Leaf 11)) (dfs Node(Leaf 7, Node(Leaf 3, Leaf 11)))``
``list_eq ((dfs Leaf 3) @ (dfs Leaf 7) @ (dfs Leaf 11)) (dfs Node(Leaf 7, Node(Leaf 3, Leaf 11)))``
``list_eq 3::((dfs Leaf 7) @ (dfs Leaf 11)) (dfs Node(Leaf 7, Node(Leaf 3, Leaf 11)))``
``list_eq 3::((dfs Leaf 7) @ (dfs Leaf 11)) ((dfs Leaf 7) @ (dfs Node(Leaf 3, Leaf 11)))``
``list_eq 3::((dfs Leaf 7) @ (dfs Leaf 11)) 7::(dfs Node(Leaf 3, Leaf 11))``
``(3=7) && (list_eq ((dfs Leaf 7) @ (dfs Leaf 11)) (dfs Node(Leaf 3, Leaf 11)))``
``false``
``````eq_dfs Node(Node(Leaf 7, Node(Leaf 3, Leaf 11)),Leaf 2)
Node(Leaf 7, Node(Leaf 3, Leaf 11))``````
``````≡ list_eq (dfs Node(Node(Leaf 7, Node(Leaf 3, Leaf 11)),Leaf 2))
(dfs Node(Leaf 7, Node(Leaf 3, Leaf 11)))
≡ list_eq ((dfs Node(Leaf 7, Node(Leaf 3, Leaf 11))) @ (dfs Leaf 2))
(dfs Node(Leaf 7, Node(Leaf 3, Leaf 11)))
≡ list_eq ((dfs Leaf 7) @ (dfs Node(Leaf 3, Leaf 11)) @ (dfs Leaf 2))
(dfs Node(Leaf 7, Node(Leaf 3, Leaf 11)))
≡ list_eq 7::((dfs Node(Leaf 3, Leaf 11)) @ (dfs Leaf 2))
(dfs Node(Leaf 7, Node(Leaf 3, Leaf 11)))
≡ list_eq 7::((dfs Node(Leaf 3, Leaf 11)) @ (dfs Leaf 2))
((dfs Leaf 7) @ (dfs Node(Leaf 3, Leaf 11))
≡ list_eq 7::((dfs Node(Leaf 3, Leaf 11)) @ (dfs Leaf 2))
7::(dfs Node(Leaf 3, Leaf 11))
≡ list_eq ((dfs Node(Leaf 3, Leaf 11)) @ (dfs Leaf 2)) (dfs Node(Leaf 3, Leaf 11))
≡ list_eq ((dfs Leaf 3) @ (dfs Leaf 11)) @ (dfs Leaf 2)) (dfs Node(Leaf 3, Leaf 11))
≡ list_eq 3::((dfs Leaf 11) @ (dfs Leaf 2)) (dfs Node(Leaf 3, Leaf 11))
≡ list_eq 3::((dfs Leaf 11) @ (dfs Leaf 2)) ((dfs Leaf 3) @ (dfs Leaf 11))
≡ list_eq 3::((dfs Leaf 11) @ (dfs Leaf 2)) 3::(dfs Leaf 11)
≡ list_eq ((dfs Leaf 11) @ (dfs Leaf 2)) (dfs Leaf 11)
≡ list_eq 11::(dfs Leaf 2) (dfs Leaf 11)
≡ list_eq 11::(dfs Leaf 2) 11::[]
≡ list_eq (dfs Leaf 2) []
≡ list_eq 2::[] []

``````

(we could write an eager OCaml function to compare trees this way…)

Also with lazy evaluation:

``````let rec nats n = n :: (nats (n+1))
let rec take n lst = match (n,lst) with
| (0,_) | (_,[]) -> []
| (_,(h::t)) -> h::(take (n-1) t)``````

Then `(take 2 (nats 0))` is ok, even though `(nats 0)` doesn’t terminate:

``````(take 2 (nats 0))
(take 2 (0 :: (nats 1)))
0::(take 1 (nats 1))
0::(take 1 (1::(nats 2)))
0::1::(take 0 (nats 2))
0::1::[]``````

Other infinite objects:

``````let rec squares n = (n*n) :: (squares (n+1))
let rec sum = function
| [] -> 0
| x::xs -> x + sum xs``````

evaluating `(sum (take 3 (squares 3)))`:

``````sum (take 3 (9::(squares 4)))
sum 9::(take 2 (squares 4))
9+(sum (take 2 (squares 4)))
9+(sum (take 2 (16::(squares 5))))
9+(sum 16::(take 1 (squares 5)))
9+16+(sum (take 1 (squares 5)))
25+(sum (take 1 (squares 5)))
25+(sum (take 1 25::(squares 6)))
25+(sum 25::(take 0 (squares 6)))...``````

Even with lazy evaluation, some expressions will fail to terminate, e.g.

``````List.exists ((>) 0) (nats 0)
List.exists ((>) 0) 0::(nats 1)
(0 > 0) || (List.exists ((>) 0) (nats 1))
false || (List.exists ((>) 0) (nats 1))
List.exists ((>) 0) (nats 1)
List.exists ((>) 0) 1::(nats 2)
(0 > 1) || (List.exists ((>) 0) (nats 2))
...``````

And

``````let rec whee x = whee (x+1)
List.exists (whee) 
≡ (whee 1) || (List.exists whee [])
≡ (whee 2) || (List.exists whee [])
≡ (whee 3) || (List.exists whee [])
...``````

# `cs2041.org`

// reveal.js plugins