`(f arg1 arg2 arg3)`

in which the
argument values are expensive to compute and don't have side effects. If
`f`

is defined as
(define f (lambda (x y z) 1))Then we need not bother to compute the argument values. If we delay the evaluation of an argument until its value is needed we have a new parameter passing method,

(Var (name) ((unbox (lookup env name)))) (Ap (fun arg) (variant-case (eval fun env) (Closure (formal body env) (eval body (extend env formal (lambda () (eval arg env)))))))Prefixing

`lambda ()`

to an expression is called a
(let ((a 0) (b (array 1 0)) (swap (lambda (x y) (let ((temp x)) (set! x y) (set! y temp))))) (swap a (array-ref b a)))We can achieve the effect of call-by-name in a call-by-value language by using thunks in the code, rather than the interpreter.

(f arg1 arg2 arg3) -> (f (lambda () arg1) (lambda () arg2) (lambda () (arg3))) (define f (lambda (x y z) (... (x) ... (x) ... (y) ... (z) ... )))

(define memo (lambda (f) (let ((first-time #t) (value #f)) (lambda () (if first-time (begin (set! value (f)) (set! first-time #f)) value) value)))) ... (Ap (fun arg) (variant-case (eval fun arg) (Closure (formal body env) (eval body (extend env formal (memo (lambda () (eval arg env))))))))Call-by-need is the basis of several so called ``lazy'' languages, like Haskell and Miranda. Since it is even more difficult to reason about call-by-need in the presence of assignment, call-by-need tends to be used only in languages that do not have assignment.

So what good are call-by-name and call-by-need? Languages with
these parameter passing methods (and without assignment)
provide a powerful reasoning
principle called ** beta-reduction**:

((lambda (x) e1) e2) = e1[x -> e2]Here

`e1[x -> e2]`

denotes the substitution of
`e2`

for free occurrences of `x`

in `e1`

,
avoiding capture. Avoiding capture means we must ensure that
`BV(e1)`

does not intersect `FV(e2)`

.
This condition doesn't prevent us from using applying `e1`

. (By the way, the rule that lets us change
names is called ((lambda (x) 1) Omega) != (1 [x -> Omega] = 1)In Scheme we have a weaker rule called

((lambda (x) e) v) = e[x -> v] where v is a constant or lambda.We can get most of the benefit of call-by-need in Scheme using

`delay`

and `force`

. These procedures return a "promise"
and evaluate the "promise" respectively.
- Implement delay and force.

`Pi`

which computes the product of a list.
(define Pi (lambda (l) (cond ((null? l) 1) ((zero? (car l)) 0) (else (* (car l) (Pi (cdr l)))))))This implementation is certainly correct, but it performs all the multiplications preceding the first zero in the list. Let's see if an accumulator will solve the problem.

(define Pi (lambda (l) (Pi-Prime l 1))) (define Pi-Prime (lambda (l acc) (cond ((null? l) acc) ((zero? (car l)) 0) (else (Pi-Prime (cdr l) (* (car l) acc))))))This has the same problem as the previous attempt. But we're close. Let's add some thunks.

(define Pi (lambda (l) (Pi-Prime l (lambda () 1)))) (define Pi-Prime (lambda (l acc) (cond ((null? l) (acc)) ((zero? (car l)) 0) (else (Pi-Prime (cdr l) (lambda () (* (car l) (acc))))))))This works. Now all the multiplications are delayed in thunks until every element of the list has been examined. The thunks start being thawed only when we hit the end of the list (see the null? test).

This is nice, but the base case is not a part of `Pi-Prime`

.
To move it in, we add an argument to the thunks:

(define Pi (lambda (l) (Pi-Prime l (lambda (x) x)))) (define Pi-Prime (lambda (l acc) (cond ((null? l) (acc 1)) ((zero? (car l)) 0) (else (Pi-Prime (cdr l) (lambda (x) (* (car l) (acc x))))))))Now we've got a function very much like the original, except it does less work if there is a zero in the list. Unfortunately, it also does the multiplies from left to right, where the original function did them from right to left. To make this function more like the original, we change the order in which the multiply and the invocation of

`acc`

occur in the last line:
(define Pi (lambda (l) (Pi-Prime l (lambda (x) x)))) (define Pi-Prime (lambda (l acc) (cond ((null? l) (acc 1)) ((zero? (car l)) 0) (else (Pi-Prime (cdr l) (lambda (x) (acc (* (car l) x))))))))This final modification gives us the property that there is nothing to do after to recursive call to

`Pi-Prime`

. When a procedure satisfies
this property, it is said to be in
To CPS-convert a procedure f to f-prime, we do the following.

- add an extra argument, k, to f-prime
- where f returns a result v, f-prime does (k v)
- at recursive call in an evaluation context E, ie. E[(f a[1] ... a[n])]

do (f-prime a[1] ... a[n] (lambda (x) E[x])).

Evaluation contexts are defined as E ::= [] | (E e) | (e E). - at a recursive call in a conditional, ie. (if E[(f a[1] ... a[n])] then else)

do (f-prime a[1] ... a[n] (lambda (x) (if E[x] then else))) - replace the outermost call (f a[1] ... a[n]) with (f-prime a[1] ... a[n] (lambda (x) x)).

(define fact (lambda (n) (if (zero? n) 1 (* n (fact (sub1 n)))))) (define fact-prime (lambda (n k) (if (zero? n) (k 1) (fact-prime (sub1 n) (lambda (x) (k (* n x)))))))Another more complicated example is finding the maximum of set.

(define max (lambda (tree) (if (number? tree) tree (if (>= (max (car tree) (max (cdr tree)))) (max (car tree)) (max (cdr tree)))))) (define max-prime (lambda (tree k) (if (number? tree) (k tree) (max-prime (cdr tree) (lambda (x) (max-prime (car tree) (lambda (y) (if (>= y x) (max-prime (car tree) (lambda (x) (k x))) (max-prime (cdr tree) (lambda (x) (k x)))))))))))Note:

`(lambda (x) (k x)) => k`

(this rule is called `max-prime`