+ All Categories
Home > Documents > Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the...

Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the...

Date post: 22-Dec-2015
Category:
View: 212 times
Download: 0 times
Share this document with a friend
Popular Tags:
49
Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it and see what happens. • What if it behaves differently on different machines or with different arguments? • What if you’re in charge of writing the first compiler? CS212 approach: Construct a formal, mathematical model.
Transcript
Page 1: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Introduction

Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression.

Hacker’s approach:

Run it and see what happens. • What if it behaves differently on different machines

or with different arguments? • What if you’re in charge of writing the first compiler?

CS212 approach:

Construct a formal, mathematical model.

Page 2: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Overview

Develop the model by starting with an extremely simple language and work our way up.– arithmetic– if, booleans– lambda, variables– substitution

Goal: be able to construct a formal proof that a given Scheme program evaluates to a specified value.

Page 3: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Scheme-0 Syntax:

(numbers) num (operators) op ::= + | - | * | /(expressions) e ::= num | op | (e1 … en )

English: Expressions are either numbers, an operator (+, -, *, or /) or a combination which is a sequence of nested expressions surrounded by parentheses.

Note: we use blue to denote meta-variables

Page 4: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Scheme-0 Values

(values) v ::= num | op

English: Values are either numbers or an operator.

Values are a subset of expressions.

Well-formed expressions evaluate to a value.

We write e => v when expression e evaluates to value v.

Page 5: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Rules for Evaluation

There are just two for Scheme-0:

One for values, and one for certain kinds of combinations.

If no rule applies, then the program is ill-formed. (DrScheme reports an error.)

Page 6: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Evaluation Rule 1: Values

v => v

English: a value evaluates to itself.

Examples: 3 => 3 (by rule 1)

+ => + (by rule 1)

Page 7: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Evaluation Rule 2: Arithmetic

To prove (e1 e2 e3) => v show:

(a) e1 => op (e1 evaluates to an operator)

(b) e2 => v1 (e2 evaluates to a value)

(c) e3 => v2 (e3 evaluates to a value)

(d) v1 op v2 = v

(applying the operator to the values yields

the value v.)

Page 8: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Evaluation Rule 2 example:

(+ 3 4) => 7 (by rule 2)

(a) + => + (by rule 1, since + is a value)

(b) 3 => 3 (by rule 1, since 3 is a value)

(c) 4 => 4 (by rule 1, since 4 is a value)

(d) 3+4 = 7 (by math)

Page 9: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Scheme-1:

op ::= + | - | * | / | < | > | =bool ::= #t | #f e ::= num | op | (e1 … en ) | (if e1 e2 e3)

| boolv ::= num | op | bool

• We added booleans (as values) and if expressions.

• #t is true, #f is false.

Page 10: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Eval. Rules 1 and 2 are the same:1. v => v

Examples: #t => #t, < => <

2. (e1 e2 e3) => v if:

(a) e1 => op

(b) e2 => v1

(c) e3 => v2

(d) v1 op v2 = v

Examples: (< 3 4) => #t

Page 11: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Evaluation Rule 3a: (if #f)To prove (if e1 e2 e3) => v show:

(a) e1 => #f

(b) e3 => v

Example: (if (< 3 1) -1 0) => 0 (by rule 3a) (a) (< 3 1) => #f (by rule 2) (a) < => < (by rule 1) (b) 3 => 3 (by rule 1) (c) 1 => 1 (by rule 1) (d) 3 < 1 = #f (by math) (b) 0 => 0 (by rule 1)

Page 12: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Evaluation Rule 3b (if #t)

To prove (if e1 e2 e3) => v show:

(a) e1 => v1 and v1 is not #f

(b) e2 => v

Example:(if (> 3 1) -1 0) => -1 (by rule 3a) (a) (> 3 1) => #t (by rule 2) (a) > => > (by rule 1) (b) 3 => 3 (by rule 1) (c) 1 => 1 (by rule 1) (d) 3 > 1 = #t (by math) (b) -1 => -1 (by rule 1)

Page 13: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Notes on IfUnlike other combinations, if is lazy.

For non-if combinations (rule 2):– evaluate arguments to values eagerly– apply operator to values

For if combinations (rule 3):– evaluate first argument (only) to value– if it’s #f, then evaluate third argument

(3a)– otherwise, evaluate second argument (3b)

Page 14: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Scheme-2e ::= num | op | (e1 … en ) | (if e1 e2 e3) |

bool | fn | xv ::= num | op | bool | fnfn ::= (lambda (x1 … xn) e)

• We add lambda expressions (functions) and variables. – We use x to represent an arbitrary variable

• Note that functions are values (i.e., lambdas evaluate to themselves.)

• Previous rules (1,2,3a,3b) still apply• It’s an error to run into an unbound variable.

Page 15: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Eval. Rule 4: (most important)

To prove (e1 e2 e3 … en) => v show:

(a) e1 => (lambda (x2 x3… xn) e)

(b) e2 => v2, e3 => v3, …, en => vn

(c) e[v2/x2, v3/x3 ,… , vn/xn] = e’

(i.e., substitute v2,…,vn for x2,…,xn in e)

(d) e’ => v

• We’ll formally define substitution later.

Page 16: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Example:((lambda (x) (if x 3 (* 4 2))) #f) => 8 (by

4)

(a) (lambda (x) (if x 3 (* 4 2))) => (lambda (x) (if x 3 (* 4 2))) (by 1)(b) #f => #f (by 1)(c) (if x 3 (* 4 2))[#f/x] = (if #f 3 (* 4 2)) (by subst.)(d) (if #f 3 (* 4 2)) => 8 (by 3a) (a) #f => #f (by 1) (b) (* 4 2) => 8 (by 2, subgoals obvious)

Page 17: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Another Example:

(((lambda (x) (lambda (y) y)) 3) 5) => 5

(by rule 4)

(a) ((lambda (x) (lambda (y) y)) 3) =>

(lambda (y) y) (by rule 4 & proof below)

(b) 5 => 5 (by rule 1)

(c) y[5/y] = 5 (by substitution)

(d) 5 => 5 (by rule 1)

So now all we have to show is part (a)...

Page 18: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Example Continued((lambda (x) (lambda (y) y)) 3) =>

(lambda (y) y) (by rule 4)

(a) (lambda (x) (lambda (y) y)) =>

(lambda (x) (lambda (y) y)) (by rule 1)

(b) 3 => 3 (by rule 1)

(c) (lambda (y) y)[3/x] = (lambda (y) y) (by subst.)

(d) (lambda (y) y) => (lambda (y) y) (by rule 1)

Page 19: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Hmmmm...

Consider changing y to x systematically:

Old: ((lambda (x) (lambda (y) y)) 3)

New: ((lambda (x) (lambda (x) x)) 3)

Following rule 4:

(a) (lambda (x) (lambda (x) x)) =>

(lambda (x) (lambda (x) x))

(b) 3 => 3

(c ) (lambda (x) x)[3/x] = ???

Body of outer function

Page 20: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Some Wrong Answers:

(lambda (x) x)[3/x] = (lambda (x) 3)

(lambda (x) x)[3/x] = (lambda (3) 3)

Why are these wrong?– The first x is a binding occurrence (the name

of a parameter)– The second x is a free occurrence that refers

to a use of the nearest enclosing bound variable.

– This is called lexical scope for variables.

Page 21: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Formalizing Substitution

e ::= num | op | (e1 … en ) | (if e1 e2 e3) |

bool | x | (lambda (x1…xn) e)

We write e[v1/x1,…,vn/xn] as an abbreviation for performing the substitutions one at a time.

So all we really need to define formally is e[v/x].

We do so by cases on e (7 cases):

Page 22: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Substitution Rules 1-5 are easy

No variable, no substitution:

s1. num[v/x] = num ex: 3[#t/y]=3

s2. op[v/x] = op ex: +[#t/y]=+

s3. bool[v/x] = bool ex: #f[#t/x]=#f

Usually, just push the substitution in:s4. (e1 … en )[v/x] = (e1 [v/x] … en [v/x])

s5. (if e1 e2 e3)[v/x] =

(if e1 [v/x] e2 [v/x] e3 [v/x] )

Page 23: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Examples for rules s4-s5(+ 3 2)[#t/y] = (by s4)

(+[#t/y] 3[#t/y] 2[#t/y]) = (+ 3 2)

because

+[#t/y] = + (by s2)

3[#t/y]= 3 (by s1)

2[#t/y]= 2 (by s1)

(if #f 3 2)[#t/y] = (by s5)

(if #f[#t/y] 3[#t/y] 2[#t/y]) =

(if #f 3 2)

Page 24: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Substitution: The Real Actions6. y [v/x] = v if y and x are the same = y otherwise

s7. (lambda (x1…xn) e) [v/x] =

a. (lambda (x1…xn) e) if x is one of x1…xn .

b. (lambda (x1…xn) e[v/x]) if x is not one of

x1…xn .

Substitution Rule 7 is very important!!!

Page 25: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Example For Rule s6:(+ y x)[3/y] = (by s4)

(+[3/y] y[3/y] x[3/y]) =

(+ 3 x)

because

+[3/y] = + (by s2)

y[3/y]= 3 (by s6 -- notice y = y)

x[3/y]= x (by s6 -- notice xy)

Page 26: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Example for rule s7:(lambda (x y) (+ z y))[3/z] = (s7b) (lambda (x y) (+ z y)[3/z] ) = (s5) (lambda (x y) (+[3/z] z[3/z] y[3/z] )) = (s2) (lambda (x y) (+ z[3/z] y[3/z] )) = (s6a) (lambda (x y) (+ 3 y[3/z] )) = (s6b) (lambda (x y) (+ 3 y)) = (s2)

In the first line, rule s7b applies because the variable we’re replacing (z) does not occur as a parameter to the function.

Page 27: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Another Example for rule s7(lambda (x y) (+ z y))[3/y] = (s7a) (lambda (x y) (+ z y))

Why? Because the variable we’re substituting for (y) is one of the parameters, so we do not push the substitution in to the body of the function.

Page 28: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Yet another 7 example(lambda (x y) (+ z y))[3/w] = (s7b) (lambda (x y) (+ z y)[3/w] ) = (s5) (lambda (x y) (+[3/w] z[3/w] y[3/w] )) = (s2) (lambda (x y) (+ z[3/w] y[3/w] )) = (s6b) (lambda (x y) (+ z y[3/w] )) = (s6b) (lambda (x y) (+ z y)) = (s2)

This time, the variable we’re replacing (w) is not one of the parameters, but it doesn’t occur in the body of the function so it disappears!

Page 29: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Revisiting:

(lambda (x) x)[3/x] =

(lambda (x) x) (by s7a)

Why? The x that we’re substituting 3 for was shadowed by another definition.

Most (modern) languages have similar scoping rules -- inner definitions of variables hide outer definitions.

Page 30: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Summary

Formalized evaluation of Scheme-2– Gave syntax of expressions

• numbers, operators, combinations, booleans, if, and lambda.

– Gave syntax-directed rules for evaluating expressions to values.

– Substitution comes into play for user-defined functions.

– Lexical scope determines rules for when we substitute what.

Still need to cover define...

Page 31: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Scheme-3Expressions and values are as before...

(programs) p ::= d1 … dn e

(defines) d ::= (define x e)

The top-level declarations allow us to define global variables (usually functions).

But they require a slightly different model...

Page 32: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Top-Level Environments:

An Environment (Env) is a way to keep track of top-level bindings.

It simply maps (some) variables to values.

Example:

{x:=3, y:=#t}

English: if you see x while evaluating, replace it with 3 and if you see y, replace it with #t.

Page 33: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Intuition:

Suppose our program is:

(define x (+ 3 4))

(define inc (lambda (x) (+ x 1)))

(inc x)

We evaluate as follows:– start with an empty environment Env0 = {}– evaluate (+ 3 4) in Env0, yielding 7 and bind x

to 7 resulting in Env1 = {x:=7}– bind inc to the lambda-value resulting in Env2 =

{x:=7, inc:=(lambda (x) (+ x 1))}– evaluate (inc x) in Env2 replacing inc with

(lambda (x) (+ x 1)) and x with 7 to get 8.

Page 34: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Three New Things:

1. Env |- e => v Same as before except if we run into a free

variable while evaluating e, we look it up in Env.

2. Env1 |- d => Env2

Evaluating a definition yields a new environment (with that definition)

3. P => v A program yields a value. Intuitively, start with an

empty environment, evaluate definitions to get a new environment, and then evaluate the expression of the program.

Page 35: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Revisiting Evaluation Rules 1 and 21. Env |- v => v (no real change)

2. Env |- ( e1 e2 e3 ) => v if:

(a) Env |- e1 => op

(b) Env |- e2 => v1

(c) Env |- e3 => v2

(d) Env |- v1 op v2 = v

Page 36: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Evaluation Rule 3:

3a. Env |- (if e1 e2 e3) => v if:

(a) Env |- e1 => #f

(b) Env |- e3 => v

3b. Env |- (if e1 e2 e3) => v if:

(a) Env |- e1 => v’ and v’ is not #f

(b) Env |- e3 => v

Page 37: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Evaluation Rule 4:

Env |- (e1 e2 e3 … en) => v if:

(a) Env |- e1 => (lambda (x2 x3… xn) e)

(b) Env |- e2 => v2 ,…, Env |- en => vn

(c) e[v2/x2 ,… , vn/xn] = e’

(d) Env |- e’ => v

Page 38: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Eval.Rule 5: (new rule -- variables)Env |- x => v if

x is mapped to v by Env.

That is, Env has a binding x:=v in it.

Page 39: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Eval. Rule 6: (new rule -- define)To prove Env |- (define x e) => Env{x:=v}

show Env |- e => v

That is, first evaluate e in the current environment Env to yield a value v.

Then bind v to the variable x to yield a new environment (for subsequent evaluation.)

Page 40: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Eval.Rule 7: (new rule -- program)To prove d1 … dn e => v show:

(a) {} |- d1 => Env1

Envn-1 |- dn => Envn

(b) Envn |- e => v

That is, start with an empty environment, evaluate the definitions (in order), take the final environment and use it to evaluate e.

Page 41: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Putting it all together:Let’s prove that evaluating the program P below

yields 6.

(define z 1)(define w (* 3 z))(define f (lambda (n) (if (< n 2) 1 (+ n (f (- n 1))))))(f w)

Page 42: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

First Define

We start off in an empty environment ({})

and show:

{} |- (define z 1)=> {z:= 1} (by rule 6)

because {} |- 1 => 1 (by rule 1)

Page 43: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Second Define

{z:=1} |- (define w (* z 3)) => {z:=1,w:=3} (6)

because {z:=1} |- (* z 3) => 3 (2):

(a) {z:=1} |- * => * (1)

(b) {z:=1} |- z => 1 (5 -- note lookup)

(c) {z:=1} |- 3 => 3 (1)

(d) 1*3 = 3 (by math)

So our 2nd environment is {z:=1,w:=3}.

Page 44: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Third Define:This one is easy like the first one, because the expression is already a value (a lambda):

{z:=1,w:=3} |- (define f (lambda (n) …)) => Env

where Env is {z:=1,w:=3,f:=(lambda (n) …)}

by the fact that {z:=1,w:=3} |- (lambda (n) …) => (lambda (n) …) (rule 1).

Finally, we must show that Env |- (f w) => 6.

Page 45: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Final Expression:

Env |- (f w) => 6 (4)(a) Env |- f => (lambda (n)

(if (< n 2) 2

(+ n (f(- n 1)))))

(5 since f maps to (lambda (n) …) in Env)

(b) Env |- w => 3 (5 since w maps to 3 in Env)

(c) (if (< n 2) 1

(+ n (f (- n 1))))[3/n] =

(if (< 3 2) 1 (+ 3 (f (- 3 1))))(subst)

Page 46: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

Continuing...(d) Env |-

(if (< 3 2) 1 (+ 3 (f (- 3 1)))) =>6(3a)

(a) Env |- (< 3 2) => #f (2 & obvious)

(b) Env |- (+ 3 (f (- 3 1))) => 6 (2)

+, 3 obvious, need to show

Env |- (f (- 3 1)) => 3

Page 47: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

And on...Env |- (f (- 3 1)) => 3 (by 4)

(a) Env |- f => (lambda (n) (if …)) (by 5)

(b) Env |- (- 3 1) => 2 (by 2 & obvious)

(c) (if …)[2/n] =

(if (< 2 2) 1 (+ 2 (f (- 2 1))))

(by subst)

(d) Env |- (if (< 2 2)

1

(+ 2 (f (- 2 1)))) => 3

(by 3a)

Page 48: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

And on...(a) Env |- (< 2 2) => #f (2 & obvious)

(b) Env |- (+ 2 (f (- 2 1))) => 3 (2)

+,2 obvious, need to show

Env |- (f (- 2 1)) => 1 (4)

(a) Env |- f => (lambda (n) (if …)) (5)

(b) Env |- (- 2 1) => 1 (2 & obvious)

(c) (if …)[1/n] = (by subst.)

(if (< 1 2) 1 (+ 1 (f (- 1 1))))

(d) Env |- (if (< 1 2) 1 …) => 1 (3b)

Page 49: Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.

And on...

Env |- (if (< 1 2) 1 …) => 1 (3b)

(a) Env |- (< 1 2) => #t (2)

(b) Env |- 1 => 1 (1)

Therefore, adding up the last umpteen slides, the program evaluates to 6.

Note: though many steps were skipped, they were obvious. When in doubt, do all of the steps.


Recommended