Theorem (Soundness of eager semantics) If M is a lambda term, and M eval N holds in the early semantics, then M ->> N (M beta-reduces to N).
The vice versa does not hold. Take for instance the term M = (\x y. y) P N, where P is any term whose evaluation does not terminate (for example, Y). We have that M beta-reduces to N, but, if the evaluation of P does not terminate, then the evaluation of M does not terminate either.
For the lazy semantics, on the contrary, also the other directions holds, at least in a weak form:
Theorem (Soundness and weak completeness of the lazy semantics) If M is a lambda term, and M eval N holds in the lazy semantics, then M ->> N holds. Viceversa, if M ->> N holds, then M eval P holds in the lazy semantics for some P such that P ->> N.
In the completeness part of the above theorem, in general N is different from P. Consider for instance M = \x. P, where P ->> Q. We have that M ->> \x. Q, but we cannot evaluate M to (\x. Q), since M is already in canonical form.
For instance, we can encode streams and its constructors/selectors in ML as follows:
datatype 'a stream = empty | cons of 'a * (unit -> 'a stream); fun head (cons(a,f)) = a; fun tail (cons(a,f)) = f();(we are obliged to use different names than ::, hd and tl, because the latter are reserved for the (eager) lists, which in ML are predefined). Now we can define function on streams in the usual way. For instance, the functions nats, times and facts of previous lecture can be defined as follows:
fun nats n = cons(n, fn() => nats(n+1)); fun times r s = cons((head r)*(head s), fn() => times (tail r) (tail s)); fun facts () = cons(1, fn () => times (facts ()) (nats 2));(here we need to give an argument to facts only to satisfy the ML constraint on the syntax of functions.) Now, we can define, for instance
val s = facts();The following are examples of interactions with ML (after giving the above definitions):
- head(tail s); val it = 2 : int - head(tail(tail(s))); val it = 6 : int - head (tail(tail(tail(tail s)))); val it = 120 : int
[.] : PCF -> tmWe define [.] compositionally, introducing a new constructor each time we need it, in the following way:
[n] = c n (for n integer), where c : int -> tmNotes:
[true] = tt, where tt : tm
[false] = ff, where tt : tm
[M op N] = opp [M] [N], where for op = +, opp = plus : tm -> tm -> tm, etc.
[if M then N else P] = ite [M] [N] [P], where ite : tm -> tm -> tm -> tm
[(M,N)] = pair [M] [N] [P], where pair : tm -> tm -> tm
[fst M] = fst [M], where fst : tm -> tm
[snd M] = snd [M], where fst : tm -> tm
[M N] = app [M] [N], where app : tm -> tm -> tm
[\x.M] = ab (x\ [M]), where ab : (tm ->tm) -> tm
[let x = M in N] = let [M] (abs (x\[N])), where let : tm -> (tm -> tm) -> tm
[fix M] = fix [M], where fix : tm -> tm
[x] = x (for x variable)
module PCF_Syntax. %%% main syntactic category: kind tm type. % terms of PCF %%% constructors for representing terms of Mini-ML. type c int -> tm. % numerical constant type tt, ff tm. % boolean constants type plus, minus, times, dv tm -> tm -> tm. % numerical ops type equal tm. % comparison type ite tm -> tm -> tm -> tm. % conditional type pair tm -> tm -> tm . % pairing type fst, snd tm -> tm. % projections type ab (tm -> tm) -> tm. % abstaction type app tm -> tm -> tm. % application type let tm -> (tm -> tm) -> tm. % local declaration type fix tm -> tm. % fixpoint operator
module PCF_eager_semantics. import PCF_Syntax. eval (c N) (c N). eval tt tt. eval ff ff. eval (plus M N) (c X) :- eval M (c Y), eval N (c Z), X is Y + Z. eval (minus M N) (c X) :- eval M (c Y), eval N (c Z), X is Y - Z. eval (times M N) (c X) :- eval M (c Y), eval N (c Z), X is Y * Z. eval (dv M N) (c X) :- eval M (c Y), eval N (c Z), X is Y div Z. eval (equal M N) tt :- eval M (c Y), eval N (c Z), Y = Z. eval (equal M N) ff :- eval M (c Y), eval N (c Z), (Y < Z ; Z < Y). eval (ite M N P) V :- eval M tt , eval N V. eval (ite M N P) V :- eval M ff , eval P V. eval (pair M N) (pair V W) :- eval M V, eval N W. eval (fst M) V :- eval M (pair V W). eval (snd M) W :- eval M (pair V W). eval (ab M) (ab M). eval (app M N) V :- eval M (ab P) , eval N Q , eval (P Q) V. eval (let M N) V :- eval M P, eval (N P) V. eval (fix M) V :- eval M (ab N), eval (N (fix M)) V.
module PCF_eager_semantics. import PCF_Syntax. eval (c N) (c N). eval tt tt. eval ff ff. eval (plus M N) (c X) :- eval M (c Y), eval N (c Z), X is Y + Z. eval (minus M N) (c X) :- eval M (c Y), eval N (c Z), X is Y - Z. eval (times M N) (c X) :- eval M (c Y), eval N (c Z), X is Y * Z. eval (dv M N) (c X) :- eval M (c Y), eval N (c Z), X is Y div Z. eval (equal M N) tt :- eval M (c Y), eval N (c Z), Y = Z. eval (equal M N) ff :- eval M (c Y), eval N (c Z), (Y < Z ; Z < Y). eval (ite M N P) V :- eval M tt , eval N V. eval (ite M N P) V :- eval M ff , eval P V. eval (pair M N) (pair M N). eval (fst M) V :- eval M (pair N P), eval N V. eval (snd M) W :- eval M (pair N P), eval P V. eval (ab M) (ab M). eval (app M N) V :- eval M (ab P), eval (P N) V. eval (let M N) V :- eval M P, eval (N P) V. eval (fix M) V :- eval M (ab N), eval (N (fix M)) V.Example of terms on which we can test eval:
type test int -> tm -> o. %%% Test #1: term representing the factorial function: test 1 (fix (ab (f\ ab (x\ ite (equal x (c 0)) (c 1) (times x (app f (minus x (c 1)))))))). %%% Test #2: term representing factorial applied to 4 test 2 (F (c 4)) :- test 1 F.We can for instance formulate the query
?- test 2 M, eval M N.which should return the solution N = (c 24).