Download presentation
Presentation is loading. Please wait.
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 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.
2
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.
3
Scheme-0 Syntax: (numbers) num (operators) op ::= + | - | * | / (expressions) e ::= num | op | (e 1 … e n ) 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
4
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.
5
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.)
6
Evaluation Rule 1: Values v => v English: a value evaluates to itself. Examples: 3 => 3 (by rule 1) + => + (by rule 1)
7
Evaluation Rule 2: Arithmetic To prove (e 1 e 2 e 3 ) => v show: (a) e 1 => op (e 1 evaluates to an operator) (b) e 2 => v 1 (e 2 evaluates to a value) (c) e 3 => v 2 (e 3 evaluates to a value) (d) v 1 op v 2 = v (applying the operator to the values yields the value v.)
8
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)
9
Scheme-1: op ::= + | - | * | / | | = bool ::= #t | #f e ::= num | op | (e 1 … e n ) | ( if e 1 e 2 e 3 ) | bool v ::= num | op | bool We added booleans (as values) and if expressions. #t is true, #f is false.
10
Eval. Rules 1 and 2 are the same: 1. v => v Examples: #t => #t, < 2. (e 1 e 2 e 3 ) => v if: (a) e 1 => op (b) e 2 => v 1 (c) e 3 => v 2 (d) v 1 op v 2 = v Examples: ( #t
11
Evaluation Rule 3a: (if #f) To prove ( if e 1 e 2 e 3 ) => v show: (a) e 1 => #f (b) e 3 => v Example: ( if ( 0 (by rule 3a) (a) ( #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)
12
Evaluation Rule 3b (if #t) To prove ( if e 1 e 2 e 3 ) => v show: (a) e 1 => v 1 and v 1 is not #f (b) e 2 => 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)
13
Notes on If Unlike 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)
14
Scheme-2 e ::= num | op | (e 1 … e n ) | ( if e 1 e 2 e 3 ) | bool | fn | x v ::= num | op | bool | fn fn ::= ( lambda (x 1 … x n ) 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.
15
Eval. Rule 4: (most important) To prove (e 1 e 2 e 3 … e n ) => v show: (a) e 1 => ( lambda (x 2 x 3 … x n ) e) (b) e 2 => v 2, e 3 => v 3, …, e n => v n (c) e[v 2 /x 2, v 3 /x 3,…, v n /x n ] = e’ (i.e., substitute v 2,…,v n for x 2,…,x n in e) (d) e’ => v We’ll formally define substitution later.
16
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)
17
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)...
18
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)
19
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
20
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.
21
Formalizing Substitution e ::= num | op | (e 1 … e n ) | ( if e 1 e 2 e 3 ) | bool | x | ( lambda (x 1 …x n ) e) We write e[v 1 /x 1,…,v n /x n ] 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):
22
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. (e 1 … e n )[v/x] = (e 1 [v/x] … e n [v/x]) s5. ( if e 1 e 2 e 3 )[v/x] = ( if e 1 [v/x] e 2 [v/x] e 3 [v/x] )
23
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)
24
Substitution: The Real Action s6. y [v/x] = v if y and x are the same = y otherwise s7. ( lambda (x 1 …x n ) e) [v/x] = a. ( lambda (x 1 …x n ) e) if x is one of x 1 …x n. b. ( lambda (x 1 …x n ) e[v/x]) if x is not one of x 1 …x n. Substitution Rule 7 is very important!!!
25
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 x y )
26
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.
27
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.
28
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!
29
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.
30
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...
31
Scheme-3 Expressions and values are as before... (programs) p ::= d 1 … d n 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...
32
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.
33
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 Env 0 = {} –evaluate (+ 3 4) in Env 0, yielding 7 and bind x to 7 resulting in Env 1 = {x:=7} –bind inc to the lambda-value resulting in Env 2 = {x:=7, inc:=(lambda (x) (+ x 1))} –evaluate (inc x) in Env 2 replacing inc with (lambda (x) (+ x 1)) and x with 7 to get 8.
34
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. Env 1 |- d => Env 2 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.
35
Revisiting Evaluation Rules 1 and 2 1. Env |- v => v (no real change) 2. Env |- ( e 1 e 2 e 3 ) => v if: (a) Env |- e 1 => op (b) Env |- e 2 => v 1 (c) Env |- e 3 => v 2 (d) Env |- v 1 op v 2 = v
36
Evaluation Rule 3: 3a. Env |- ( if e 1 e 2 e 3 ) => v if: (a) Env |- e 1 => #f (b) Env |- e 3 => v 3b. Env |- ( if e 1 e 2 e 3 ) => v if: (a) Env |- e 1 => v’ and v’ is not #f (b) Env |- e 3 => v
37
Evaluation Rule 4: Env |- (e 1 e 2 e 3 … e n ) => v if: (a) Env |- e 1 => ( lambda (x 2 x 3 … x n ) e) (b) Env |- e 2 => v 2,…, Env |- e n => v n (c) e[v 2 /x 2,…, v n /x n ] = e’ (d) Env |- e’ => v
38
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.
39
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.)
40
Eval.Rule 7: (new rule -- program) To prove d 1 … d n e => v show: (a) {} |- d 1 => Env 1 … Env n-1 |- d n => Env n (b) Env n |- e => v That is, start with an empty environment, evaluate the definitions (in order), take the final environment and use it to evaluate e.
41
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)
42
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)
43
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}.
44
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.
45
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)
46
Continuing... (d) Env |- (if ( 6 (3a) (a) Env |- ( #f (2 & obvious) (b) Env |- (+ 3 (f (- 3 1))) => 6 (2) +, 3 obvious, need to show Env |- (f (- 3 1)) => 3
47
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)
48
And on... (a) Env |- ( #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 (3b)
49
And on... Env |- (if ( 1 (3b) (a) Env |- ( #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.
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.