Com S 342 meeting -*- Outline -*- * Procedures (3.5) ** syntax ------------------------------------------ PROCEDURES (3.5) Concrete syntax: ::= ... | proc ( {}*(,) ) | ( {}* ) Examples: Abstract syntax: ------------------------------------------ ... let sum3 = proc (a, b, c) +(a, +(b, c)) in (sum3 3 4 2) (proc (a,b,c) +(a, +(b,c)) 3 4 2) Discuss why would need to use "call" to use a more normal syntax, because SLLGEN is LL(1)... Q: What should be the abstract syntax? ... (define-datatype expression expression? ... (proc-exp (ids (list-of symbol?)) (body expression?)) (app-exp (rator expression?) (rands (list-of expression?)))) Q: does this abstract syntax look familiar? ** semantics *** scoping ------------------------------------------ SEMANTICS OF PROCEDURES What scope rules for procedures? let three = 3 in let add3 = proc (y) +(y, three) in let three = 100 in (add3 2) ------------------------------------------ Want to understand what add3 means at the time it's defined, so the redefinition of three shouldn't affect it. We want to run the body of add3 in the environment in which it was defined, extended to bind the formals to the actuals. To do this have to save the environment in which add3 was defined (where three = 3), so that it's independent of the environment in which it's run (where three = 100). So it has to be a closed package, hence the name closure. *** domains ------------------------------------------ DOMAINS Expressed-Value = Number + ProcVal Denoted-Value = Expressed-Value (define-datatype Expressed-Value expval? (number->expressed (num number?)) (procval->expressed (pv procval?))) ------------------------------------------ The ProcVal domain is essentially ProcVal = Expressed-Value* -> Expressed-Value but we'll look into how to specify and implement it now... *** procedure values ------------------------------------------ PROCEDURE VALUES AS A DATATYPE Want: (apply-procval (closure ids body env) args) = (eval-expression body (extend-env ids args env)) Use this to define apply-procval : closure : ------------------------------------------ ... (-> (ProcVal (list-of Expressed-Value)) Expressed-Value) (-> ((list-of symbol) expression environment) ProcVal) Q: Which is the constructor? observer? ------------------------------------------ PROCEDURAL REP OF PROCVAL (define closure (lambda (ids body env) (define apply-procval (lambda (proc args) ------------------------------------------ ... (lambda (args) (eval-expression body (extend-env ids args env))))) ... (proc args))) ------------------------------------------ FOR YOU TO DO Convert the procedural rep of ProcVal to an AST rep ------------------------------------------ (define-datatype procval procval? (closure (ids (list-of symbol?)) (body expression?) (env environment?))) (define apply-procval (lambda (proc args) (cases procval proc (closure (ids body env) (eval-expression body (extend-env ids args env)))))) *** the interpreter ------------------------------------------ THE INTERPRETER (figure 3.7) (deftype eval-expression (-> (expression environment) Expressed-Value)) (define eval-expression (lambda (exp env) (cases expression exp (primapp-exp (prim rands) (let ((args (eval-rands rands env))) (apply-primitive prim args))) ... (proc-exp (ids body) (app-exp (rator rands) ------------------------------------------ ... (proc-exp (ids body) (procval->expressed (closure ids body env))) (app-exp (rator rands) (let ((proc (eval-expression rator env)) (args (eval-rands rands env))) (if (procval->expressed? proc) ;; note diff from book! (apply-procval (expressed->procval proc) args) (eopl:error 'eval-expression "Attempt to apply non-procedure ~s" proc)))) ** semantic implications and variations *** examples show an example evaluation, like p. 87-88 See the file procedure-calculation in this directory, or better yet, play with $PUB/lib/ch3-5-reducer.scm *** lexical addresses (exercises 3.23-3.28) We don't really need to use the names of variables, we can instead use their lexical addresses. *** dynamic binding (aka. dynamic scoping, exercises 3.30-3.33) Note: try to draw stacks that GROW DOWN the page, as this will be easier for all concerned. **** motivation explain what happens below ------------------------------------------ UTILITY OF DYNAMIC BINDING changing implicit parameters: let stdout = port in p(1,2) let font = italic in format(mytext) exception handlers: try { // Java syntax mydata.read(System.in); return mydata.average(); } catch (NoDataException e) { System.err.println("no data!"); return POSITIVE_INFINITY; } catch (ArithmeticExecption a) { System.err.println("no data!"); return POSITIVE_INFINITY; } ------------------------------------------ Q: Why couldn't stdout simply be a global variable? want to restore it back to standard binding when done modularity? (but you need access to it anyway) Q: Why not pass these around as parameters? how many parameters do you want to have for p? Q: What's like this in the Unix shell? I/O redirection Q: Why wouldn't the handlers be bound statically? call sites determine what to do. **** history Historically, this arose from the first LISP interpreters, because it was easier to write this instead of making closures for procedures... (Current LISP interpreters and compilers use static scope.) Still used in operating system command languages (Unix shell), and various other interpreters (TeX, ...) **** domains ------------------------------------------ DOMAINS FOR DYNAMIC SCOPING Expressed-Value = Number + ProcText (define-datatype ProcText ProcText? (procedure-text (ids (list-of symbol?)) (body expression?))) ------------------------------------------ Q: What's missing? the environment! Q: So when you apply a procedure text, where does the environment come from? The environment of the call (in the app-exp case of eval-expression) **** behavior ------------------------------------------ BEHAVIOR WITH DYNAMIC SCOPE let three = 3 in let add3 = proc(i) +(i, three) three = 5 in *(three, (add3 2)) ------------------------------------------ draw pictures of environments with static and dynamic scoping for this example. Q: What's the difference from static scoping? ;; Fig 5.7.3 of the first edition of EOPL ------------------------------------------ FOR YOU Consider: let a = 3 in let p = proc() +(x, a) f = proc(x, y) *((p), y) a = 5 in *(a, (f let a = 2 in a 1)) TO DO: What is the value with dynamic scoping? Does it make sense with static scoping? ------------------------------------------ **** the funarg problem ------------------------------------------ THE FUNARG PROBLEM let G = 6.670e-11 in let gravForce = proc(m1) proc(r) proc(m2) /(*(G, *(m1, m2)), *(r,r)) in (((gravForce 5.96e24) 6.37e6) 68) ------------------------------------------ Q: what will happen with dynamic scope? draw a picture **** reasoning problems (exercise 3.33) Q: Can we change formal parameter names and their uses without affecting the meaning of a program? no ------------------------------------------ COUNTER-EXAMPLE let a = 3 in let p = proc() +(x, a) f = proc(x, y) *((p), y) a = 5 in *(a, (f let a = 2 in a, 1)) vs. let a = 3 in let p = proc() +(x, a) f = proc(x, a) *((p), a) %% *** a = 5 in *(a, (f let a = 2 in a 1)) ------------------------------------------ Q: Can we replace a call to a procedure with its body, substituting the actuals for the formals in the body? no For beta counter-example, consider the difference between (f let a = 2 in a 1) and let a = 2 in (f a 1) Q: Can we change "proc(x) (f x)" to "f"? no, the former introduces a binding of x... Q: Could you do type checking at compile time if have dynamic scope?