**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)

- In general for all the operators (like conditional, pairing etc.) we have the choice of encoding them as tm (and then using app) or as n-rary constructors. It seems reasonable to encode them as n-rary constructors since their semantics is different from the semantics of application.
- It would not be a good idea to encode [M N] as [M] [N]: first of all, we would have a type mismatch ([M] is of type tm, not tm -> tm). Second, and more important, the lambda Prolog semantics of [M] [N] might be different from the intended PCF semantics of M N.
- The encoding of \x.M uses the higher-order facilities of lambda Prolog. The definition [\x.M] = abs (x\ [M]) has the advantage that the term M[N/x] can then be encoded as (x\ [M]) [N]. The underlying beta-conversion mechanism of lambda Prolog will take care of performing the substitution (i.e. of replacing x by [N] in [M]).
- Since we are interested in evaluating only closed terms (open terms do not have a value), and we encode PCF abstractions as lambda Prolog abstractions, then the PCF variables have to be encoded as the lambda Prolog (abstraction) variables with the same name.

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).