Interp-LETREC

Download Report

Transcript Interp-LETREC

Recursive Procedures and Scopes
EOPL3 Sections 3.4 - 3.7
The LETREC Language
• All of PROC, plus …
• Concrete:
– Expression :: =
letrec Identifier (Identifier) =
Expression in Expression
• AST:
– letrec-exp (proc-name bound-var
proc-body letrec-body)
CS784(pm)
2
value of a letrec expression
(value-of
(letrec-exp proc-name
bound-var proc-body letrec-body)
ρ)
= (value-of letrec-body
(extend-env-rec proc-name
bound-var proc-body ρ))
CS784(pm)
3
(extend-env-rec pnm bvar pbdy ρ)
• Let ρ1 == (extend-env-rec proc-name
bound-var proc-body ρ)
• (apply-env ρ1 proc-name)
= (proc-val
(procedure bound-var proc-body ρ1))
• (apply-env ρ1 var) = (apply-env ρ var)
CS784(pm)
4
environment datatype
(define-datatype environment environment?
(empty-env)
(extend-env
(var identifier?)
(val expval?)
(env environment?))
(extend-env-rec
(p-name identifier?)
(b-var identifier?)
(body expression?)
(env environment?)))
CS784(pm)
5
apply-env
(define apply-env (lambda (env search-var)
(cases environment env
(empty-env () (report-no-binding-found search-var))
(extend-env (saved-var saved-val saved-env)
(if (eqv? saved-var search-var)
saved-val
(apply-env saved-env search-var)))
(extend-env-rec (p-name b-var p-body saved-env)
(if (eqv? search-var p-name)
(proc-val (procedure b-var p-body env))
(apply-env saved-env search-var))))))
CS784(pm)
6
Exercise 3.35
• “The representations we have seen so far are inefficient, because
they build a new closure every time the procedure is retrieved.
But the closure is the same every time. We can build the
closures only once, by putting the value in a vector of length 1
and building an explicit circular structure, like …”
• .
CS784(pm)
7
Exercise 3.35: extend-env-rec
(define extend-env-rec
(lambda (p-name b-var body saved-env)
(let ((vec (make-vector 1)))
(let ((new-env (extend-env p-name
vec saved-env)))
(vector-set! vec 0
(proc-val (procedure b-var
body new-env)))
new-env))))
CS784(pm)
8
Scoping and Binding
• references
– (f x y)
– f, x, and y
• declarations
– (lambda (x) (+ x 3))
– (let ((x (+ y 7))) (+ x 3))
• y, and second x are refs
• first x is a declaration
• lexical scoping rules
CS784(pm)
9
Kinds of Scope
• Static or Lexical scope
– determined by structure of program
– Scheme, C++, Java, and many compiled languages
• Dynamic scope
– determined by path of execution
– Lisp dialects, Perl, and many interpreted languages
• Global Scope
– File scope
• Local Scope
– Block
• Body of a procedure
• Body of a loop
• Scope alters the meaning
CS784(pm)
10
Scoping : Free (non-local) Variables
proc main()
int x := 5;
proc q()
{ x := x + 1;}
proc r()
{int x := 0;
q();
}
{ r();
print(x); }.
CS784(pm)
• Static scoping
x -> x
output: 6
• Dynamic scoping
x -> x
output: 5
11
let x = 15
in let f = proc () x
in
( let x = 8
in (f)
)
Static Scoping
Dynamic Scoping
CS784(pm)
x -> x
x -> x
x -> x
+
(f)
30
23
12
Fig 3.13 Contour diagram
CS784(pm)
13
Figure 3.14 Contour diagram
CS784(pm)
14
Binding Rules
1.
A variable declared by a proc is bound when the procedure is
applied.
–
2.
A let-variable is bound by the value of its right-hand side.
–
3.
(apply-procedure (procedure var body ρ) val)
= (value-of body (extend-env var val ρ))
(value-of (let-exp var val body) ρ)
= (value-of body (extend-env var val ρ))
A variable declared by a letrec is bound using its righthand side as well.
–
CS784(pm)
(value-of (letrec-exp proc-name
bound-var proc-body letrec-body) ρ)
= (value-of letrec-body (extend-env-rec proc-name
bound-var proc-body ρ))
15
lexical depth
# of contours crossed
zero-based indexing
(lambda (x)
(
(lambda (a)
(x a))
x))
(nameless-lambda
(
(nameless-lambda
(#1 #0))
#0))
CS784(pm)
16
The Translator
•
•
•
•
•
Source: var-exp, let-exp, …
Target: nameless-var-exp, nameless-let-exp
translation-of: handles expressions
translation-of-program: handles programs.
translation-of-program runs translation-of
in a suitable initial static environment.
CS784(pm)
17
translation-of-program
(define translation-of-program
(lambda (pgm)
(cases program pgm
(a-program (exp1)
(a-program
(translation-of exp1 (init-senv)))))))
(define init-senv (lambda ()
(extend-senv ’i
(extend-senv ’v
(extend-senv ’x
(empty-senv))))))
CS784(pm)
18
Fg 3.16 lexical-address translator
(define translation-of
(lambda (exp senv)
(cases expression exp
(const-exp (num) (const-exp num))
(diff-exp (exp1 exp2)
(diff-exp
(translation-of exp1 senv)
(translation-of exp2 senv)))
… altogether 8 cases …
(else
(report-invalid-source-expression exp)) )))
CS784(pm)
19
translation-of zero?-exp
(zero?-exp (exp1)
(zero?-exp
(translation-of exp1 senv)))
CS784(pm)
20
translation-of if-exp
(if-exp (exp1 exp2 exp3)
(if-exp
(translation-of exp1 senv)
(translation-of exp2 senv)
(translation-of exp3 senv)))
CS784(pm)
21
translation-of var-exp
(var-exp (var)
(nameless-var-exp
(apply-senv senv var)))
CS784(pm)
22
translation-of let-exp
(let-exp (var exp1 body)
(nameless-let-exp
(translation-of exp1 senv)
(translation-of body
(extend-senv var senv))))
CS784(pm)
23
translation-of proc-exp
(proc-exp (var body)
(nameless-proc-exp
(translation-of body
(extend-senv var senv))))
CS784(pm)
24
translation-of call-exp
(call-exp (rator rand)
(call-exp
(translation-of rator senv)
(translation-of rand senv)))
CS784(pm)
25
Our top-level procedure
(define run
(lambda (string)
(value-of-program
(translation-of-program
(scan&parse string)))))
CS784(pm)
26
nameless environments
•
•
•
•
nameless-environment?
empty-nameless-env
extend-nameless-env
apply-nameless-env
: SchemeVal → Bool
: () → Nameless-env
: Expval × Nameless-env → Nameless-env
: Nameless-env × Lexaddr → DenVal
• (apply-procedure (procedure var body ρ) val)
= (value-of body (extend-env
var val ρ)) page 79
• (apply-procedure (procedure body ρ) val)
= (value-of body (extend-nameless-env val ρ)) page 98
CS784(pm)
27
procedure datatype
(define-datatype proc proc?
(procedure
(body expression?)
(saved-nameless-env
nameless-environment?)))
CS784(pm)
28
apply-procedure
(define apply-procedure
(lambda (pc val)
(cases proc pc
(procedure (body saved-nameless-env)
(value-of body
(extend-nameless-env val
saved-nameless-env))))))
CS784(pm)
29
redefine value-of
(define value-of
(lambda (exp nameless-env)
(cases expression exp
(const-exp (num)
...as before...)
(diff-exp (exp1 exp2) ...as before...)
(zero?-exp (exp1)
...as before...)
(if-exp (exp1 exp2 exp3) ...as before...)
(call-exp (rator rand) ...as before...)
… next slide …
(else
(report-invalid-translated-expression exp)))))
CS784(pm)
30
redefine value-of
(nameless-var-exp (n)
(apply-nameless-env nameless-env n))
(nameless-let-exp (exp1 body)
(let ((val (value-of exp1 nameless-env)))
(value-of body
(extend-nameless-env val nameless-env))))
(nameless-proc-exp (body)
(proc-val
(procedure body nameless-env)))
CS784(pm)
31
value-of-program
(define value-of-program
(lambda (pgm)
(cases program pgm
(a-program (exp1)
(value-of exp1
(init-nameless-env))))))
CS784(pm)
32
extra slide if time permits
• ===============================
===============================
===============================
===============================
===============================
===============================
===============================
===============================
CS784(pm)
33
Procedure Invocations
• Dynamic scoping
(define (eval-expression exp env)
(cases expression exp
(app-exp (rator rands)
...
(let ((proc (eval-expression rator env))
(args (eval-rands rands env)))
(if (procval? proc)
(apply-procval proc args env)
(eopl:error 'eval-expression
"Applying non-procedure ~s" proc)
)
))
CS784(pm)
34
Processing Procedure Invocations
• Dynamic scoping
• (define (apply-procval proc args c-env)
(cases procval proc
(procv (ids body)
(eval-expression body
(extend-env ids args c-env)) )))
CS784(pm)
35
Processing Procedure Invocations
>(run "let x = 1 in
let f = proc () x in
(f)")
1
>(run "let x = 1 in
let f = proc (x) x in (f 2)")
2
Static scoping
>(run "let x = 1 in
let
in
f = proc () x
x=5
(f)")
1
Dynamic scoping
>(run "let x = 1 in
let
in
f = proc () x
x=5
(f)")
5
CS784(pm)
36
Implementing Dynamic Scoping
• The value of variable x is the last value
bound to x. => stack discipline
– Deep binding
• Use a global stack for all variables
– Shallow binding
• Use a separate stack for each variable
• Implementing recursion is trivial.
• Meaning of a call depends on the context.
CS784(pm)
37
Lexical Scope Pros and Cons
•
•
•
•
•
Scope of names is known to the compiler.
Permits type checking by compiler
Easily check for uninitialized variables
Easier to analyze program correctness
Recursion is harder to implement
CS784(pm)
38
Dynamic Scope Pros and Cons
• Meaning of variables known only at run-time.
– Cannot perform type checking before execution
– Programs are flexible, but harder to understand
• Easy to implement recursion
• Renaming in the caller can effect the semantics of a
program
– Locality of formals destroyed.
– if renamed parameter now captures a free variable in the
called function.
– Recall the interpretation of a free variable in a function body
can change based on the context (environment) of a call.
CS784(pm)
39
Dyn Binding Can be Difficult
Exercise 3.29: … dynamic binding may be exceptionally difficult to
understand. For example, under lexical binding, consistently renaming
the bound variables of a procedure can never change the behavior of a
program: we can even remove all variables and replace them by their
lexical addresses, as in section 3.6. But under dynamic binding, this
transformation is unsafe.
For example, under dynamic binding, the procedure proc (z) a returns the
value of the variable a in its caller’s environment. Thus, the program
let a = 3
in let p = proc (z) a
in let f = proc (x) (p 0)
in let a = 5
in (f 2)
returns 5, since a’s value at the call site is 5. What if f’s formal parameter
were a?
CS784(pm)
40
Application of Dynamic Scoping
• Exception Handling
– provide a separate construct in statically scoped
language
• Setting Local Formatting Parameters
• Input-Output Redirection
– avoids passing parameters explicitly through
“intermediate” procedures
CS784(pm)
41
Dyn Binding Can be Powerful
• Exercise 3.37 With dynamic binding,
recursive procedures may be bound by let;
no special mechanism is necessary for
recursion. Test the following
let fact = proc (n) add1(n)
in let fact = proc (n) if zero?(n) then 1 else *(n,(fact (n,1)))
in (fact 5)
using both lexical and dynamic binding.
CS784(pm)
42