CPS 343/543 Lecture notes:
(Non-recursive) Procedures and closures
Coverage: [EOPL] §3.5 (pp. 84-92)
Nomenclature
- actual parameters (often called arguments)
- formal parameters (also known as bound variables)
// defining function f
// a and b are the formal parameters
int f (int a, int b) {
return (a+b);
}
// invoking f
// x and y are the actual parameters
f(x,y);
Adding procedures to our language
- two new production rules and constructors on p. 84
- sample expression on p. 84
- we want procedures to be first class objects in the defined language
- therefore, we want expressed value = denoted value = number + procval
- what information
must we include in the value of a procedure?
- in order to determine this, let us examine what
happens at procedure-application time
Closures
Augmenting eval-expression
- now with this foundation in place, we can easily modify our interpreter
to handled first-class procedures
- new code for eval-expression on p. 87
- augmented eval-expression in Fig. 3.7 on p. 88
- sample expression evaluation example on pp. 88-89
- again, identifiers for variables need not appear in syntax
trees manipulated by an interpreter [EOPL]
- if we use the ribcage representation for an environment, then
the lexical address of a variable reference v (d p) tells
us exactly where the variable reference appears:
the d-th rib at position p [EOPL]
Referencing environment for
passed subprogram (function)
- deep binding:
uses the environment at the time the passed function was created (the
default in our defined language) (line a below)
- shallow binding: uses the environment of the expression which
invokes the passed function (line b below)
- ad hoc binding: uses the environment of the invocation expression in
which the subprogram is passed as an argument (line c below)
- example:
let
y = 3
in
let
x = 10
f = proc (x) *(y, +(x,x)) ; line a
in
let
y = 4
in
let
y = 5
x = 6
g = proc (x, y) *(y, (x y)) ; line b
in
let
y = 2
in
(g f x) ; line c
results:
- deep binding: 216
- shallow binding: 288
- ad-hoc binding: 144
We use deep binding in our defining language (i.e., our interpreter
implements deep binding).
When McCarthy and his students at MIT
were developing the first version of LISP, they really wanted static scoping,
but implemented pure dynamic scoping by accident.
Their second version of LISP (the patchversion) also did not implement
static scoping, but rather ad-hoc binding, which is closer to dynamic
scoping, but still not quite pure dynamic scoping.
Dubbed the (downward) funarg problem
(i.e., functional argument problem).
The upward funarg problem, which is more difficult,
involves return functions to functions (rather
than passing functions to functions).
The BIG picture
- we have so carefully designed our ADT's through interfaces
that now our interpreter is very flexible
- programming language concepts (on which this course focuses) often have
options:
- scoping (static or dynamic)
- binding (deep, shallow, ad-hoc)
-
we can often alter the semantics of the defined language
dramatically (e.g., from static to dynamic scoping)
by changing as little as 1-2 lines of code
in the interpreter (usually just change how and when we pass
the environment) (e.g., deep, shallow, or ad-hoc binding approaches).
- again, identifiers for variables need not appear in syntax
trees processed by an interpreter (depth and position are all we need)
- in summary, the interpreter for a language is nothing more than
another program [EOPL] Forward
References
| [EOPL] |
D.P. Friedman, M. Wand, and C.T. Haynes.
Essentials of Programming Languages.
MIT Press, Cambridge, MA, Second edition, 2001. |
|