Inductive Definitions - Cs.princeton.edu

Abstract Machines
COS 441
Princeton University
Fall 2004
A More Abstract View of Stacks
• Mitchell’s account of functions, scope, and
storage management is a very low-level
implementation orientated view of
programming language
• Harper presents a more abstract view of
many of the same ideas using a more
formal approach
The M-machine and C-Machine
• We start with the SOS for MinML and
progressively refine the semantics to a
more low-level model
• M-machine repeatedly searches for a
subterm that can be evaluated in one
primitive step
• C-machine keeps track of subterms that
need to be reduce with a stack of frames
Very Simplified Examples
We will workout an M, C, and C with return
for this very simple language
Numbers
n 2 N
Expressions e ::= n | +(e1,e2)
Values
v ::= n
M-Machine Evaluation
+(+(1,2),+(3,4))
M +(3,+(3,4))
M +(3,7)
M 10
A2 with A1
A3 with A1
A1
+(v1,v2) M v1 + v2
e1 M e’1
+(e1,e2) M +(e1’,e2)
A2
A1
e2 M e’2
+(v1,e2) M +(v1,e’2)
A3
Stacks and Frames
Stacks and Frames
Stacks
Frames
k ::= ² | f B k
f ::= +(v1,¤) | +(¤,e2)
Stacks and Frames
Stacks
Frames
k ::= ² | f B k
f ::= +(v1,¤) | +(¤,e2)
• Stack is just a sequence of frames the B
operator pushes a frame on to a stack
• A frame is an expression with a “hole”
• Holes are where values will be placed
when we “pop” the frame from the stack
Step Relation for the C-machine
Step Relation for the C-machine
Push a frame and evaluate e1
Step Relation for the C-machine
Push a frame and evaluate e1
Continue evaluation of e2
Step Relation for the C-machine
Push a frame and evaluate e1
Continue evaluation of e2
Return evaluation
Example: Evaluation
(²,+(+(1,2),+(3,4)))
C (+(¤,+(3,4)) B ²,+(1,2))
C (+(¤,2) B+(¤,+(3,4)) B ²,1)
C (+(1,¤) B+(¤,+(3,4)) B ²,2)
C (+(¤,+(3,4)) B ²,3)
C (+(3,¤) B ²,+(3,4))
C (+(¤,4) B +(3,¤) B ²,3)
C (+(3,¤) B +(3,¤) B ²,4)
C (+(3,¤) B ²,7)
C (²,10)
push
push
continue
return
continue
push
continue
return
return
C-Machine vs M-Machine
• C-Machine has an explicit stack
– Closer to lower-level machine implementation
• M-Machine hide “stacks” in premises
– Easier for proof of type soundness
• Relation to C and M machines
– Must defined a “hole” filling relation that
converts a C machine state into a closed
expression
• We can show
C and M-Machine States
+(1,¤) B+(¤,+(3,4)) B ² @ 2
+(¤,+(3,4)) B ² @ +(1,2)
² @ +(+(1,2),+(3,4))
Example: Evaluation
(²,+(+(1,2),+(3,4)))
C (+(¤,+(3,4)) B ²,+(1,2))
C (+(¤,2) B+(¤,+(3,4)) B ²,1)
C (+(1,¤) B+(¤,+(3,4)) B ²,2)
C (+(¤,+(3,4)) B ²,3)
C (+(3,¤) B ²,+(3,4))
C (+(¤,4) B +(3,¤) B ²,3)
C (+(3,¤) B +(3,¤) B ²,4)
C (+(3,¤) B ²,7)
C (²,10)
+(+(1,2),+(3,4))
+(+(1,2),+(3,4))
+(+(1,2),+(3,4))
+(+(1,2),+(3,4))
+(3,+(3,4))
+(3,+(3,4))
+(3,+(3,4))
+(3,+(3,4))
+(3,7)
10
Formal Relationship
From the above we have
(²,e) *C (²,v) iff e *M v
Intuition Behind Proof
Theorem: if (²,e) *C (²,v) then e *M v
If we have
(²,e) C S1C S2 C … Sn C (²,v)
we can replace the first few C steps with
one M step so that
e M e’ and (²,e’) C … Sn C (²,v)
until we have e *M v
Intuition Behind Proof
Theorem: if e *M v then (²,e) *C (²,v)
If we have
e M e1 M … en-1 M en M v
we can replace the last M step with some
C steps so that
e M … en-1 and (²,en) *C (²,v)
until we have (²,e) *C (²,v)
Modified C-Machine
• Before we talk about the E-Machine
• Modify the C-Machine in a small way to
make return of values more explicit with
new state (v,k)
(k,+(e1,e2))
(k,v)
(n2,+(n1,¤)Bk)
(v1,+(¤,e2)Bk)
C
C
C
C
(+(¤,e2)Bk,e1) push
(v,k)
return
(n1 + n2,k)
pop
(+(v1,¤)Bk,e2) continue
Example: Evaluation
(²,+(+(1,2),3))
C (+(¤,3) B ²,+(1,2))
C (+(¤,2) B+(¤,3) B ²,1)
C (1,+(¤,2) B+(¤,3) B ²)
C (+(1,¤) B+(¤,3)) B ²,2)
C (2,+(1,¤) B+(¤,3) B ²)
C (3,+(¤,3) B ²)
C (+(3,¤) B ²,3)
C (3,+(3,¤) B ²)
C (6,²)
push
push
return
continue
return
pop
continue
return
pop
Environments vs. Substitution
• M and C machine use substitution to
replace bound occurrence of a variable
with a value
• Substitution is an inefficient approach in
realistic implementations
• We can use environment semantics
instead to avoid substitution
– Study this in a big-step semantics first for
clarity
Substitution Semantics
Expr’s
x 2 …
n 2 N
e ::= num[n] | plus(e1,e2) | times(e1,e2)
Val’s
| let(e1,x.e2) | x
v ::= num[n]
Names
Num’s
E1  num[N] [X Ã num[N]] E2  V
let(E1,X.E2) V
eval-L
Environment Semantics
Names

Val’s
Env’s
Prog’s
x 2 …


v ::= num[n]

Env ::= {} | {x1 v1,… , xn  vn}
Prg ::= (Env,e)
(Env,X)  Env(X)
eval-X
(Env,E1)  num[N] (Env[X  num[N]], E2)  V
(Env,let(E1,X.E2))  V
eval-L
Dynamic Scoping
• Values of arithmetic expressions do not contain
any variables
– What about -calculus semantics?
• Naïve/incorrect environment semantics for calculus leads to dynamic scoping
– Original scoping rules for LISP
– Easy to implement in an interpreter hard for compiler
– Still lives on in emacs LISP, TCL provides controlled
version via “upvar”, limited form of dynamic scoping
in Python, Mathmatica “Block” and S-plus)
Dynamic Scoping
Names
Expr’s
Val’s
Env’s
Prog’s
x 2 …
e ::= lam(x.e) | apply(e1,e2)| x
v ::= lam(x.e)
Env ::= {} | {x1 v1,… , xn  vn}
Prg ::= (Env,e)
• Similar to simple arithmetic language
• Values are subset of expressions
Dynamic Scoping
Val’s
Env’s
v ::= lam(x.e)
Env ::= {} | {x1 v1,… , xn  vn}
Prog’s
Prg ::= (Env,e)
(Env,X)  Env(X)
eval-X
eval-L
(Env,lam(X.E))  lam(X.E)
(Env,E1)  lam(X’.E’)
(Env,E2)  V’
(Env[X’  V’], E’)  V
(Env,apply(E1,E2))  V
eval-A
Example: Dynamic Scoping
• Evaluates function body in calling
environment
• Allows capturing of unbound variables in
expressions
({},(f.((y.f 0) 1)) (x.y))  ??
Example: Dynamic Scoping
({},f.((y.f 0) 1))  (f.((y.f 0) 1))
({},(x.y))  (x.y)
({f(x.y)},(y.f 0) 1)  ??
({},(f.((y.f 0) 1)) (x.y))  ??
Example: Dynamic Scoping
({f(x.y)},(y.f 0))  (y.f 0)
({f(x.y)},1)  1
({f(x.y),y1},f 0)  ??
({f(x.y)},(y.f 0) 1)  ??
Example: Dynamic Scoping
({f(x.y),y1},f)  (x.y)
({f(x.y),y1},0)  0
({f(x.y),y1},(x.y) 0)  ??
({f(x.y),y1},f 0)  ??
Example: Dynamic Scoping
({f(x.y),y1},(x.y))  (x.y)
({f(x.y),y1},0)  0
({f(x.y),y1,x0},y)  ??
({f(x.y),y1},(x.y) 0)  ??
Example: Dynamic Scoping
({f(x.y),y1},(x.y))  (x.y)
({f(x.y),y1},0)  0
({f(x.y),y1,x0},y)  1
({f(x.y),y1},(x.y) 0)  1
Example: Dynamic Scoping
({f(x.y),y1},f 0)  (x.y)
({f(x.y),y1},0)  0
({f(x.y),y1},(x.y) 0)  ??
({f(x.y),y1},f 0)  ??
Dynamic Scope
• Many languages seem to support dynamic
scope because it is easy to implement
• Is dynamic scoping a “broken” or “useful”
feature?
– Trend seems to be against dynamic scope
• However, lets illustrate why dynamic
scope can be useful
– Will use TCL and ELISP as examples