CS 234319: Programming Languages Yossi Gil
*
January 31, 2015
*
[email protected]
1
6
Imperative programming 6.1 Commands (70 frs.) . . . . . . . . . . . . . . 6.1.1 Commands vs. expressions (16 frs.) . . 6.1.2 Recursive definitions (4 frs.) . . . . . . 6.1.3 Expressions’ evaluation order (6 frs.) . 6.1.4 Atomic commands (12 frs.) . . . . . . 6.1.5 Block commands (11 frs.) . . . . . . . 6.1.6 Conditional commands . . . . . . . . 6.1.7 Iterative commands . . . . . . . . . . 6.1.8 Exercises (7 frs.) . . . . . . . . . . . . 6.2 Structured programming (11 frs.) . . . . . . . 6.3 Sequencers (1 fr.) . . . . . . . . . . . . . . . . 6.4 Exceptions (35 frs.) . . . . . . . . . . . . . . . 6.4.1 Robustness (3 frs.) . . . . . . . . . . . 6.4.2 Policy I: resumption (5 frs.) . . . . . . 6.4.3 Policy II: error rolling (6 frs.) . . . . . 6.4.4 Policy III: setjmp/longmp of C (1 fr.) 6.4.5 Policy IV: exceptions (4 frs.) . . . . . 6.4.6 Kinds of exceptions (8 frs.) . . . . . . 6.4.7 Resource acquisition is (or isn’t) initialization . . . . . . . . . . . . . . . . 6.5 Functional abstractions (102 frs.) . . . . . . . 6.5.1 Closures (37 frs.) . . . . . . . . . . . . 6.5.2 Function objects (12 frs.) . . . . . . . 6.5.3 Generators (11 frs.) . . . . . . . . . . . 6.5.4 Iterators (17 frs.) . . . . . . . . . . . . 6.5.5 Iterator examples (15 frs.) . . . . . . . 6.5.6 Coroutines (10 frs.) . . . . . . . . . . . 6.5.7 Exercises . . . . . . . . . . . . . . . . 1. Imperative programming: mindmap
Commands vs. expressions
6.1.1 Frames:
◊ Commands: what are they?
◊ Commands vs. state-
. . . . . . . . . . . . . . . . . .
2 ments ◊ Commands vs. expressions ◊ Expressions changing the 2 program’s state? ◊ Expressions without side-effects? ◊ “Statement4 expressions” in Gnu-C ◊ and in Mock … ◊ “Commandexpression” ◊ Reasonable realizations of command-expression I 6 ◊ Reasonable realizations of command-expression II 8 2. Commands: what are they? 9 Commands are characteristic of imperative languages1 10 12 Definition 6.1 (Command). A command is a part of a 14 computer program, which: 14 • does not produce a value, 16 17 • whose main purpose is altering the program’s state. 17 • even vacuously 18 19 Examples 20 21 • I/O: print, read,… 21 • Assignment
. . . . . . . . .
22 23 23 30 33 35 38 42 43
• Loops • Conditional • “nop” • “\relax” • “;” 3. Commands vs. statements
a visual
• The misnomer statement is (much) more frequently used in the literature. • But, there is nothing declarative in commands!
Command Expressions
• “Statement” also means:
Expression Oriented Languages Escape
Jump
– definitions – declarations . – ..
Variations
Commands vs.Expressions
Structured vs.Non-Structured Programs NassiShneiderman Diagrams
Commands
Examples – Pascal –C Atomic – Java Commands
– anything ending with a “;”
Basics
Varieties – Definite/Indefinite – Sequential/Simultaneous/Collateral
Iterative
Varieties – Sequential – Simultaneous – Collateral
4. Commands vs. expressions
Skip
Command Constructors
Conditional
Block
Varieties – Sequential – Simultaneous – Collateral
Partial Specification
Varieties Assignment – Vanilla – Update – Multiple – Simultaneous – Collateral
Ideally, they should be distinct Commands • Change state • No value
Figure 6.1: Imperative programming: a visual mindmap
6.1
Expression • No state change
Commands 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.1.6 6.1.7 6.1.8
Commands vs. expressions (10 frs.) . . Recursive definitions (11 frs.) . . . . . Expressions’ evaluation order (16 frs.) Atomic commands (4 frs.) . . . . . . . Block commands (6 frs.) . . . . . . . . Conditional commands (12 frs.) . . . . Iterative commands (11 frs.) . . . . . . Exercises . . . . . . . . . . . . . . . .
• Produce a value . 2 . 4 . 6 . 8 . 9 . 10 . 12 . 14
In practice, the borderline is not so clear
Commands
1 No
2
Expressions
commands in purely functional languages
5. Expressions changing the program’s state?
8. and in Mock …
Nasty CS101 Exam Question You are given a seemingly
innocent Pascal code, and asked…
return if ( (while (*s++ = *t++) ;) > (while (*t++ == *s++) ;) ) 3; else while (*s++ != *t++) return 7;
Procedure Hamlet;
VAR happy: Boolean; Function toBe:Boolean; Begin … happy := not happy; toBe := happy End; Begin … happy := false; If toBe and not toBe WriteLn("The Answer!"); End; Could "The Answer"
Mock
Huh? What does this mean? Is this useful to anyone?
ever be written?
9. “Command-expression”
• Suppose that toBe is a function nested in procedure Hamlet,
Command expressions is an idealistic notion:
• which may have access to a global variable,
• Any expression may be substituted by a command
• whose initial value is false,
• Every command is an expression, so every command returns a value:
• In fact, function toBe returns the value of this global variable,
Atomic atomic commands are expressions
• just after flipping it!
Conditional the selected branch
Sequence the last expression Iteration the last iteration? What if there were no iterations?
• So, the answer is,…
Return What value should “return 3” return?
6. Expressions without side-effects?
What happened here?
10. Reasonable realizations command-expression I
• Expressions do not make sense without function calls
of
• In “Statement-Expressions” of Gnu-C
• Functions may invoke commands
• ML, in with the semicolon, “;” operator:
• Commands, by definition, alter the program state!
– takes 2 operands
• Worse, in some PLs, certain operators have side-effects
– computes the 1st operand…
Would it be possible to prevent side-effects at the PL design level?
– and then discards it! – computes the 2nd operand…
• Representation of state?
– and then returns it.
ML
• How would you do I/O?
Standard ML of New Jersey … - (1;2)*(3;4); val it = 8 : int
• In general, tough, but awkward Obvious example, pure-ML
• The ancient BCPL
7. “Statement-expressions” in Gnu-C
An excerpt from Section 6.1 Statements and Declarations in Expressions of Chapter 6 Extensions to the C Language Family of the Gnu-C manual:
11. Reasonable realizations command-expression II
of
• PostScript
Gun-C
({ int y = foo (); int z; if (y > 0) z = y; else z = - y; z; })
• Icon, in which every expression is a generator; – atomic expressions are things such as values, which can only yield one value;
is a valid (though slightly more complex than necessary) expression for the absolute value of foo().
– iterations return a sequence of values; – sequencing means concatenating the output of generators
Note Gnu-C uses the misnomer “statement” instead of command
– … 3
6.1.2
Recursive definitions
15. Three atomic commands in Pascal
Frames: ◊ Expressions are recursively defined ◊ Function call expression constructor ◊ Commands are also recursively defined! ◊ Three atomic commands in Pascal ◊ More on Pascal’s atomic commands ◊ The advent of “expression oriented languages” ◊ Two kinds of atomic commands in C++ ◊ Command expressions in C ◊ More on atomic expressions in C ◊ Two kinds of atomic commands in Java2 ◊ Not all Java expressions make commands
(Ignoring goto, the only sequencer of the language) Empty Can you figure out where it hides?
Pascal
Procedure swap(Var a, b: Integer); Begin a := a + b; b := a - b; a := a - b; end;
12. Expressions are recursively defined
Naturally, each PL is different, but the general scheme is: Atomic expressions
• literals
• variable inspection Expression constructors “-”, …
• Operators such as
Pascal is a separatist language; the semicolon is not part of Assignment the command; hence, an empty command is“+”, hiding here.
As in the above,
Pascal
happy := not happy
• Function call:
Procedure call As in the above,
The set of atomic expressions and the constructors’ set are PL dependent, but the variety is not huge.
Pascal
WriteLn("The Answer!")
13. Function call expression constructor
16. More on Pascal’s atomic commands
Definition 6.2 (Function call expression constructor (dynamic typing version)). If f is a function taking n ≥ 0 arguments, and E1 , . . . , En are expressions, then
Empty no change to state; no computation; no textual representation; existence determined solely by context.
f (E1 , . . . , En )
Assignment
is an expression.
Definition 6.4 (Assignment atomic command). Let v be a variable of type τ , and let E be an expression of type τ , or of compatible type τ 0 , τ 0 ≤ τ . Then,
Only a few cases of type compatibility Pascal, e.g.,
Definition 6.3 (Function call expression constructor INTEGER ≤ REAL, (static typing version)). Let f be a (typed) function of n ≥ 0 in the sense that an integer arguments,
v := E
value could be assigned to a real variable.
(6.1)
is an atomic command.
f ∈ τ1 × · · · × τn → τ .
Let E1 , . . . , En be expressions of types τ1 , . . . , τn . Then, the Procedure call
f (E1 , . . . , En )
Definition 6.5 (Procedure call atomic Command). If p is a procedure taking arguments of types τ1 , . . . , τn , where n ≥ 0 and E1 ∈ τ1 , . . . , En ∈ τn are expressions, then the procedure call
is an expression of type τ . 14. Commands are also recursively defined!
Each PL is different. The scheme is the same, but the variety is huge: Atomic Commands •
p(E1 , . . . , En )
• the empty command
is an atomic command.
…3
• “sequencers”4 Command constructors tor (huge variety)
(6.2)
17. The advent of “expression oriented languages”
• Block command construc-
Pascal sharp distinction between expressions and commands
• Conditional command constructor (huge variety) • Iterative command constructor (huge variety)
• distinction between Function and Procedure
• “try-catch-catch-· · · -finally” command constructor (huge variety)
• distinction between epxression and command
• “With” command constructor (huge variety)
C, Java, Go,… blurred distinction:
• …5
• a procedure is a function returning Unit
2 ignoring
sequencers PL is different 4 WTF? sequencers will be discussed later 5 each PL is different
• an expression is a command, more or less, and subject to PLs variety.
3 each
4
• Not every semicolon makes a command
18. Two kinds of atomic commands in C++
• Not every lonely semicolon makes an empty command
The empty Command does not change the program state; does not perform any computation; textual representation is the semicolon, i.e., “;”
Empty command; no need for loop “body”; all work is carried out by the side-effects of the expression used in the loop condition
• Not every expression followed by a semicolon makes a command
C
while (*s++ = *t++) ;
21. Two kinds of atomic commands in Javaa a ignoring
Just as in C,
Expression marked as Command An atomic command is also “an expression followed by a semicolon”, e.g., In C, assignment is an operator taking two arguments L (left) and R (right). The operator returns R, and as sideeffect, assigns R into L.
sequencers
; the empty command is a lonely semicolon;
C
Expression; provided that the first step in the recursive decomposition of expression is “something” that has (might have) side-effects:
0; 1*1; i; (i=i)==-i; i++ + ++i;
• Function call • Operator with side effects: Assignmnet e.g., =, +=, !sum, add = fn r:real => (n := !n + 1; sum := !sum + r), mean = fn() =>(!sum / real(!n)) } end;
• In discussing type constructors, we introduced the mapping type constructor, e.g., in ML we have the type int->int. • Values of type mapping, can be realized as – Arrays – Functions • It is often the case that functions are not “pure” mappings. – In the imperative paradigm, functions may have side effects – Even in the functional paradigm, functions may depend on values which are not parameters • Do function values behave just like other values?
val makeAccumulator =fn : unit -> {add:real -> unit, count:unit -> int, mean:unit -> real, total:unit -> real} 124. Computing your average grade
• create a new accumulator
ML
val grades = makeAccumulator();
val grades = {add=fn,count=fn,mean=fn,total=fn}: : {add:real -> unit, count:unit -> int, mean:unit -> real, total:unit -> real}
120. Discrimination against function values
In Pascal • Can define variables of almost any type, but not of type function.
• record grade in CS101
ML
(#add grades)(82.0);
• Functions can take function values as parameters, but functions cannot return function values.
val it = () : unit
• record grade in PL ( note that the parenthesis are optional)
121. Function values in C
In C, function values are realized as “function pointers”
ML
#add grades (100.0);
• Can be stored in variables
val it = () : unit
• Can be stored in arrays
• record grade in calculus
• Can be fields of structures
ML
#add grades (37.0);
• Can be passed as parameters
val it = () : unit
• Can be returned from functions
• how many grades so far?
But, C does not allow nested functions.
#count grades ();
122. Nested functions in Gnu-C
val it = 3 : int
Gnu-C is a C dialect, supporting nested functions
Gnu-C
• what’s their total?
int isRightTriangle(double a, double b, double c) { double sqr(double x) { return x * x; } return sqr(a) + sqr(b) == sqr(c); }
#total grades ();
ML ML
val it = 219.0 : real
• what’s my GPA?
• Function sqr is nested in isRightTriangle
#mean grades ();
• Function sqr is inaccessible outside isRightTriangle
val it = 73.0 : real
24
ML
125. The nest is accessible to nested functions
The Gnu-C compiler generates clever code that makes this possible.
Why use nested function?
C
• Nested functions can access variables and definitions of nest
127. Body of function pivot()
• Saves lots of argument passing
int pivot() { // “inherits” // from sort: a, // from qsort: from, to int last = to - 1; int pivot = a[first]; int split = from; swap(split, last); for (int i = first; i < last; i++) if (a[i] < pivot) swap(split++, i); swap(split, last); return split; }
Nesting structure for an implementation of the quick sort algorithm using nested functions: void sort(int a[], int n) { void swap(int i, int j) { }
…
void qsort(int from, int to) { int pivot() { } } }
…
128. Using a nested function outside of context
…
The nested function swap escapes the nest: The Escape
…
void (*exchange)(int, int);
Figure 6.13: Function nesting structure for an implementation of the quick sort algorithm 126. “Inheritance” of arguments with nested functions
void sort(int a[], int n) { void swap(int i, int j) { // “inherits” (and uses) a // “inherits” (without using) n int t = a[j]; a[i] = a[j]; a[j] = t; } void qsort(int from, int to) { // “inherits” (and uses) a // “inherits” (without using) n int pivot() { // “inherits” and uses argument a // “inherits” (without using) n // also “inherits” (and uses) from, to … } if (from == to) return; int p = pivot(); qsort(from, p); // 1st recursive call qsort(p, to); // 2nd recursive call } qsort(0, n); }
void sort(int a[], int n) { void swap(int i, int j) { … } … exchange = swap; … }
Gnu-C
The escaped value is used in another context: Access to refugee … #define SIZEOF(z) (sizeof(z) / sizeof(z[0]))
Gnu-C
int main(int argc, char **argv) { int a[500]; int b[200]; … sort(b, SIZEOF(b)); … (*exchange)(10,12); … }
Gnu-C
• Would this work? • Does swapping take place in array a or b? Answer: Undefined behavior due to the realization of “activation record” as a machine stack frame.
• At run time, there might be several instances of the recursive function qsort • Function pivot inherits – integers from and to from the correct instance of qsort – integers a from the single instance of sort
129. Function variables & dangling references
Similarly, suppose that Pascal had function variables… 25
132. A stack frame
VAR fv: Integer -> Char; Procedure P; VAR v: … ; Function f(n: Integer): Char; begin …v… end; begin fv := f; end begin P; … fv(0) … end;
int gcd(int m, int n) { if (m == n) return m; int m1 = m; int n1 = n; if (m > n) m1 = m % n; else n1 = n % m; return gcd(m1, n1); }
Java
Pascal
• Each recursive call of gcd – generate a new stack frame
• The environment “dies” as a stack frame is popped.
– push it into the stack
• C forbids nested functions for this reason.
• Each return from a (recursive) call
• Pascal forbids function variable for this reason • To make 1st class “function values”, the activation record cannot be allocated on the hardware stack.
– pop the frame out of the stack – resume computation
[fragile]Understanding the machine stack
R1 , . . . , Rn Saved Registers
int gcd(int m, int n) { if (m == n) return m; int m1 = m; int n1 = n; if (m > n) m1 = m % n; else n1 = n % m; return gcd(m1, n1); }
PC
Java
SP Saved Stack Pointer optional Return Variable m, n Parameters m1, n1 Local Variables m % n, n % m, … Intermediate Results
How does this function“exist” at runtime? Common sequence of bytes, representing machine code
Figure 6.15: Activation record (implemented as stack frame) for function gcd
Per activation the “environment”: • Parameters
133. Activation record
• Local variables
A function call has two components
131. Reminder: CPU & memory in the classical model
Unmapped
• The caller • The callee
Unmapped
Virtual Address Space Code
Heap
Data
Constants
⇒ break
PC ProALU gram Counter Arithmetical Logical Unit R1
R2
···
Rn−1
An activation record represents the interface between the two:
232 − 1
0 Zero
Saved Program Counter
⇐ Stack
• Saved context everything that is required to reconstruct the caller’s state at the end of the call; typically, saved registers.
stack pointer
SP Stack Pointer
• Local state local variables of the callee, intermediate results, etc.
Rn
General Purpose Registers
• Arguments values that the caller sent to the callee
CPU
• Environment variables, functions, definitions, etc. defined by the caller and used by the callee.
Figure 6.14: Reminder: CPU & memory in the classical model
An activation record is often realized by a stack frame. 26
138. The environment: a slightly more precise definition
134. What’s the “environment”?
Definition 6.28 (Environment (first approximation)). Variables, functions, constants, etc., of the caller, which Definition 6.30 (Environment (second approximation)). The set of bindings made in the caller which are available can be used by callee. to the callee. • Does not exist in C/C++ In Pascal, the environment includes • In Pascal, definitions made in a function, are available • The CONST section of the caller (binding of names to to any nested function. values) 135. Acquired bindings in nested functions
• The VAR section of the caller (binding of names to variables)
What’s the environment of pivot?
• The TYPE section of the caller (binding of names to types)
void sort(int a[], int n) { void swap(int i, int j) { …} void qsort(int from, int to) { int pivot() { ???} } … }
Gnu-C
• The LABEL section of the caller (binding of names to labels) • Functions and procedures defined within the caller (binding of names to functions and procedure values)
• Function sort
In C/C++, there is no “caller environment”
• Function swap
139. Environment of a nested function
What’s the environment of function pivot?
• Function qsort • Function pivot
void sort(int a[], int n) { void swap(int i, int j) { …} void qsort(int from, int to) { int pivot() { ??? } } }
Gnu-C
• Arguments to function sort – Array a – Integer n • Arguments to function qsort
Function names in the environment:
– Integer from – Integer to
• The binding of the names: sort, swap, qsort and pivot to the “functions”
136. Name vs. entity
• Binding here is of names to the pointers to functions. Function arguments in the environment:
• But what’s really in the phrase
• Binding of names of arguments to function sort
“variables, functions, constants, etc.”
• Binding of names of arguments to function qsort
• We distinguish between
– These bindings are distinct in each recursive calls.
– Name – Entity
– The semantics of Gnu-C are such that pivot acquires the “correct” bindings.
Note that:
140. Envionment vs. scope
– Entities may have no name (e.g., anonymous functions, classes, allocated variables, etc.) – Entities may have more than one name (e.g., type aliases) – In some cases, a name might have a name, (e.g., tetragrammaton, shemhamphorasch, but also found in reflective programming)
In simple words… • Q: What’s the environment? • A: All variables which are in “scope” • Q: What’s scope? • A: The extent of locations in the program in which a binding is recognized.
137. Binding
Two scoping (binding) rules are found in PLs
Definition 6.29 (Binding). Binding is the tie between a name and an entity
Lexical scoping Bindings not made in function, are determined by where the function is defined.
The phrase “variables, functions, constants, etc.”, means
Synamic scoping Bindings not made in function, are determined by where the function is used.
the set of bindings in the caller available to the callee 27
143. Lexical nesting does not imply call order
141. More on semantics of the environment
When does f2 “inherit” bindings made in f1 ?
void sort(int a[], int n) { void swap(int i, int j) { … } void qsort(int from, int to) { int pivot() { … } } }
Gnu-C
Answer I: (static scoping) If f2 is defined inside f1
Program Code Function f0 {
swap and pivot:
Function f1 {
• Function swap is called by pivot
Function f2 { }
• Function swap is not nested in pivot
}
swap and sort:
}
• Function swap is nested in sort • Function swap is not called by sort
Figure 6.16: Function f2 defined within f1 defined within f0
144. Convoluted calls
• By induction, f2 inherits bindings of f0 if f1 is defined inside f0 .
Who can call function f2 ()?
• Environment defined by static code structure.
Function f0 () { Function f1 () {
(sometimes called static binding, or lexical scoping)
Function f2 () { Function f3 () { f4 () {…}
Answer II: (dynamic scoping) If f2 is called by f1 .
Machine Stack
}
Function f0 { f1 (); }
} }
Function f1 { f2 (); }
Function g1 () { Function g2 () {
Function f2 { … }
Function g3 () {…} }
⇓
} }
Figure 6.17: Function f2 called by f1 called by f0 • By induction, f2 inherits bindings of f0 if f1 is called by f0 . • Environment defined by program dynamics (sometimes called dynamic binding)
Figure 6.18: Deep function nesting structure: function fi+1 defined in fi for i = 0, 1, 2, 3; g j+1 defined in gi for j = 1, 2; and, g1 defined in f1 . The caller of f2 is not necessarily f1 ; it could be • function f2 itself
142. Intricacies in static scoping
Definition 6.31 (Static scoping). The environment is determined by scope; if f1 is defined in f2 , then f2 “inherits” bindings made by f1 .
• function f3 defined in f2 • function f4 defined in f3 • …
• There might be more than one activation record of f1 on the stack
• another function g1 defined in f1
• The caller of f2 might not be f1
• function g3 defined in g2
• Static scoping means the most recent version of f1 .
• …
• function g2 defined in g1
But not function f0 within which f1 is defined! 28
148. A simplified static scoping
145. Back pointers in the quick sort example
Consider the following chain of calls: main() → sort() → qsort() → qsort() → qsort() → pivot() → swap()
Frames on the hardware stac
• Then, instead of juggling the BP, one can simply copy the entire set of bindings as arguments.
main
saved state of O/S
swap
>
main
sort
swap
⇐
qsort
saved state of pivot void *bp int i int j int t
pivot
pivot saved state of qsort3 void *bp int last int pivot int split int i
of
• Suppose that all bindings in the environment are readonly.
(grows from high memory to low memory, depicted right to left)
Nesting structure
implementation
int argc
qsort3
char **argv
qsort2
qsort1
saved state of qsort2
saved state of qsort1
saved state of sort
void *bp
void *bp
void *bp
saved state of main
int from
int from
int from
int *a
int to
int to
int to
int n
int p
int p
int p
sort
149. Dynamic scoping
int a[500] a[499] a[498]
.. .
Definition 6.32 (Dynamic scoping). The environment is determined by calls; if f1 calls f2 , then f2 “inherits” bindings made by f1 .
a[1] a[0]
int b[200]
Legend
b[199]
Caller’s saved state
Back pointer
Arguments
Local variables
.. .
Dynamic scoping cares not whether there is any relationship between f1 and f2 .
b[0]
• Found in Lisp (but not in Scheme)
Figure 6.19: Back pointers in the quick sort example
• Found in TEX(text processing PL used to make this material)
146. Managing static scoping with back pointers
• The environment is represented as a “back pointer” (BP)
• Found in Perl! • Found in Bash! • Found in HTML/CSS!
• The BP is part of the stack frame • BP points at the stack frame of the most recent version of caller
The de-facto semantics of the C preprocessor. 150. Semantics of dynamic scoping
Using the back pointer: • To access variables defined in f1 function f2 traverses the BP, to find the local state of f1 • To access variables defined in f0 function f2 hop twice through the BP list, to find the local state of f0 • To access variables defined in f−1 (the function within which f0 is defined), make two hops along the BP list. • … 147. Maintaining the back pointer
• Let
void (*exchange)(int, int); void sort(int a[], int n) { void swap(int i, int j) { int t = a[j]; a[i] = a[j]; a[j] = t; } … exchange = swap; … }
Gnu-C
… #define SIZEOF(z) (sizeof(z) / sizeof(z[0]))
F ∈ { f1 , f2 , f3 , . . . , g1 , g2 , g3 , . . .} be a function ready to call f2 .
int main(int argc, char **argv) { int a[500]; int b[500]; … sort(b, SIZEOF(b)); … (*exchange)(10,12); … }
pseudo Gnu-C
• F must traverse the back pointers list to find the “most recent” stack frame of f1 . • Copy this address to the BP of the stack frame of the newly created stack frame for f2 . Number of hops? • If F = f1 , then no hops.
What would work?
• If F = f2 , then one hop.
• sort calling qsort? Yes.
• If F = f3 , then two hops.
• qsort calling qsort? Yes.
• If F = g1 , then one hops.
• qsort calling pivot? Yes.
• If F = g2 , then two hops.
• main calling swap? Yes.
• … 29
151. The runtime bindings dictionary
155. Closures and lifetime
The “binding dictionary”: • Local state is represented by a dictionary • Dictionary supports mapping between names and entities
• Q: what’s the lifetime of a variable enclosed in a closure? • A: the lifetime of the enclosing value.
• Typical entities are variables, constants, and functions.
– In this example, the lifetime of “delta” for each function returned by makeAdder is the lifetime of the return value of makeAdder.
• Typical implementations of dictionary are linked list and hash table.
– The same as lifetime of fields in a record allocated on the heap: live as long as the record is still allocated.
The environment: • The environment is represented as a “back pointer” (BP) to the most recent stack frame. • Thus, we have a “stack of dictionaries”.
In general, you cannot have closures without using GC, rather than the stack for implementation of activation • Search for name is carried out by searching in the stack records. With closures, activation records are allocated of dictionaries. from the heap. • There are means to make this search quite efficient. 156. Closures in ML
152. The environment: a precise definition
Just as in JavaScript, in ML, all functions are closures.
Definition 6.33 (Environment). The environment of a function, is the set of bindings available to this function.
• Standard programming idiom of the language
Environment could be
• Also supports anonymous functions
• Defined statically (static scoping)
• Function values are first class values (including the environment)
• Defined dynamically (dynamic scoping) 153. Closures
6.5.2
• Many modern PLs support closures, where a function can be created in a way that it “keeps with it” the variables that exist in its scope. • Often used in functions-that-return-functions • You can’t do that in Pascal, C, C++ • Very common in functional languages, ML, Python, JavaScript and Java (since version 8.0; uses a hack).
157. Emulation closures with C++ function objects
Class of “adding something” function objects:
class FunctionObjectForAdding { public: FunctionObjectForAdding(int _b): b(_b) {} int operator() (int a) { return a + b; } private: int b; // Saved environment };
A closures factory
# C
function makeAdder(i) { var delta = i; function adder(n) { return n + delta; } return adder; }
• A function object stores a copy of relevant pieces of the environment as data members of the class.
# C
• Environment copy is managed by the function object, which, just like any other C++ variable, can be
var add3 = makeAdder(3); document.write(add3(7)); document.write(add3(12)); var add8 = makeAdder(8) document.write(add8(12));
Output:
Frames: ◊ Emulation closures with C++ function objects ◊ Using C++’s function objects ◊ Closures in Java? ◊ Function objects with Java ◊ An interface for function objects ◊ Another use of the interface for function objects ◊ Using objects of type Function ◊ Functions returning function objects ◊ Using factory functions ◊ Smarter function objects with inner classes ◊ Function objects with inner classes ◊ Inner class of factory function
C++
154. Closures in JavaScript
Usage
Function objects
– present on the stack – allocated from the heap – global
10 15 20
• Memory management is the programmer’s responsibility.
30
Java
158. Using C++’s function objects
class LOG implements Function { // captured environment: final Double logBase; LOG(final Double base) { logBase = Math.log(base); } public Double apply(Double t) { return Math.log(t) / logBase; } }
Usage: #include
C++
int main() { FunctionObjectForAdding add3(3); std::cout >> range
Python
public // sould be pulic to be useful static // factory methods tend to be static Function // Abstract function type mapping(String[] map) { class IntMap // Local class implements Function { public String apply(Integer v) { … // as beforee } } return new IntMap(); }
Java
Python does not make much distinction between functions and generators >>> def fu(n): ... return n ... >>> def countdown(n): ... while n >= 0: ... yield n ... n -= 1 ... >>> fu >>> countdown
Python
Look mom! No hands! Inner class IntMap does not have a map data member! 6.5.3
23:59:50:
Generators
Frames: ◊ A simple Python generator ◊ A generator built on top of another ◊ Functions vs. generators in Python ◊ Use of functions & generators in Python ◊ What’s a generator? ◊ Generators in Icon ◊ Comparions generators ◊ Control with generators ◊ Icon implements control using generators ◊ Generators in C# ◊ Summary of generators 169. A simple Python generator
172. Use of functions & generators in Python
Function fu def fu(x): Python return x
A generator is defined just like a function, except that it uses yield instead of return 33
Use as function:
176. Control with generators
Iteration and conditional are both generting contexts:
>>> fu(3) Python 3
• if statement:
Use as generator
– Execute body if a value is yielded – Only takes on the first first yielded value
>>> for i in fu(3): ... print i ... Traceback (most recent call last): File "", line 1, in TypeError: 'int' object is not iterable
Python
• while statement: – Execute body for each value yielded – Uses all yielded values
Generator gen
A simple Icon loop
Pascal
while line := read() do write(line)
def gen(x): Python yield x
A loop with empty body
Use as function:
Python
Pascal
>>> gen(3)
while write(read())
Use as generator
177. Icon implements control using generators
>>> for i in gen(3): ... print i ... 3 >>> exit()
In Icon, all expressions are generators:
Python
Singleton generators 3 is a generator of the sequence {3}. Or generator 3|4|5 quence {3, 4, 5}
173. What’s a generator?
is
a
generator
of
the
se-
Definition 6.34 (Generator). A generator is a function Depth first generation (30|40|50)+(1|2) is a generathat produces a (possibly empty, possibly infinite) sequence tor of the sequence {31, 32, 41, 42} of results, instead of returning a single result. “Product” of sequences (1|2|3)*f() + (1|2) * g() is a generator yielding 6 × n × m where 174. Generators in Icon • n is the number of values that f yields
In Icon, all expressions are generators:
• m is the number of values that f yields
Singleton generators 3 is a generator of the sequence {3}.
178. Generators in C#
Or generator 3|4|5 is a generator of the sequence
In statically typed PLs, generators call for sophisticated Depth first generation (30|40|50)+(1|2) is a genera- typing tor of the sequence {31, 32, 41, 42} “Product” of sequences (1|2|3)*f() + (1|2) * g() is a generator of a sequence with 6 × n × m values, where • n is the number of values that f yields • m is the number of values that f yields 175. Comparions generators
Comparison generators simplify arithmetical conditions • i == j yields { j} if i = j; otherise, the empty sequence {} • i == (3|4|5) succeeds if i ∈ {3, 4, 5}; it yields the value to which i equals. • 0 n if (p.multiply(p).compareTo(n) > 0) break; else if (n.mod(p).compareTo(big(0)) == 0) return false; // Divisor found } // No divisor found return true; }
Java
public static BigInteger nextPrime(BigInteger n) { for (BigInteger $ = succ(n);; $ = succ($)) if (isPrime($)) return $; }
2
208. Maintaining a list of previously found primes for efficiency
2
3
3
5
7
Figure 6.23: Cache of primes (after first insertion) Function extend
Cache.last
Cache.first
Cache.last
Cache.first
static class Cache { … static void extend() { last = last.append( nextPrime(last.prime) ); } }
Java
5
Figure 6.21: Cache of primes (initial state) Cache list item
210. The primes() pseudo generator
static class Node { final BigInteger prime; Node next = null; Node(int p) { this(big(p)); } Node(BigInteger prime) { this.prime = prime; } Node append(BigInteger p) { return next = new Node(p); } Node append(int p) { return append(big(p)); } }
Let’s begin with the trivial, technical parts public static Iterable primes() { return new Iterable() { public Iterator iterator() { return new Iterator() { // There are infinitely many primes: public boolean hasNext() { return true; } // Cannot remove primes public void remove() { throw new UnsupportedOperationException( "remove" ); } … }; } }; }
Java
Java
The cache list static class Cache { static final Node first = new Node(2); static Node last = first.append(3).append(5); static void extend() { last = last.append( nextPrime(last.prime) ); } }
Java
The challenge is in the iterator function next() 211. Function next() in pseudo generator primes()
Iterator function next() must:
209. Extending the cache list
• Retrieve the next prime from the cache • If the cache is exhausted, extend it
Cache.last
Cache.first 2
3
Tricky recursion: • To extend the cache, nextPrime() is called
5
• nextPrime() uses isPrime() • isPrime() iterates over primes()
Figure 6.22: Reminder: initial state of the cache of primes
• But to yield a prime, nextPrime() may need to extend the cache
41
# C
Luckily
void piglet2() { for (;;) { defecate(); if (hungry || thirsty) { suck(); yield sow; } }}
• To yield √ a prime in the order of n, you needs primes until about n. • Cache was initialized with sufficiently primes to get this clockwork going
… return new Iterator() { … // Data member of the inner class Node next = Cache.first; // Yield the next prime from the cache public BigInteger next() { // Save value to be returned final BigInteger $ = next.prime; // Was cache exhausted? if (next == Cache.last) Cache.extend(); // Advance to next node of the cache next = next.next; return $; } };
If piglet2 is not hungry • cow may die
Java
6.5.6
• piglet1 will then die • piglet2 himself will eventually die 214. Issues with the pigsty
It is not clear at all • how the coroutines are started? • how each coroutine gains access to the other? • how can one create multiple instances of the same coroutine? (generate, e.g., several active invocation of piglet2()
Coroutines
Frames: ◊ What are coroutines? ◊ Unconvincing example of coroutines ◊ Issues with the pigsty ◊ Coroutines vs. threads ◊ The sad story of coroutines ◊ Killer application of coroutines ◊ Continuations ◊ Data stored in a continuation ◊ Operations on a continuation ◊ Things you can do with continuations: 212. What are coroutines?
• why coroutines are better than plain threads? So, why the heck do we need these? 215. Coroutines vs. threads
Both offer multi-tasking:
Definition 6.39 (Coroutine). A coroutine is a function, which can
Threads preemptive; execution can be interrupted at any point
• suspend anywhere in its execution by executing a yield command.
Coroutine cooperative; control is Zillions of weird scheduling schemes?
• resume a suspended execution, proceeding to the command that follows the suspending yield
Threads Yes!
Coroutines vs. generators:
Coroutine only with careless programming
• Generators are also called semicoroutines
Race conditions?
• Generator can only yield to its caller
Threads Yes!
• Coroutine typically yield to another coroutine
Coroutine No! (coincidentally, two overloaded meanings of the word “yield” are “to produce” and “to surrender to another the physical Generally speaking, coroutines grant the programmer fine control of something”) control of scheduling, but it may take skill to effectively use this control. 213. Unconvincing example of coroutines A sow and its two little piglets may all die in the pigestry
216. The sad story of coroutines
# C
void sow() { for (;;) { defecate(); if (hungry) feed(); if (thirsty) drink(); yield pigglet1; }}
• invented in the early 60’s (if not earlier) • mostly forgotten; often discarded as “cumbersome multitasking” • have (partial) implementation in mainstream PLs such as C, C++, Java, Python, Perl, Object-Pascal, Smalltalk and more
# C
void piglet1() { for (;;) { defecate(); if (hungry || thirsty) suck(); yield pigglet2; }}
• implementations did not really catch Hence, the poorly designed syntax in the pigsty. 42
217. Killer application of coroutines
220. Operations on a continuation
Event Loops:
c ← start( f (. . .)) create a new continuation for the call f (. . .)
void mainWindow() { for (;;) { Message m = getMessage(); Area a = findScreenArea(a); Windows ws = findWindows(as); push(m); for (Window w: ws) { yield(w); if (!there(m)) break; } } }
# C
c0 ← clone(c) save execution state for later resumable(c) determine whether resumption is possible. c ← resume(c) resume execution of continuation c retrievable(c) determine whether computation has generated a result r ← retrieve(c) obtain the pending returned (yielded) value of c In fact, continuations are nothing but “generation records” with slightly more operations.
# C
void anyWindow() { for (;;) { switch (Message m = getMessage()) { case M1: … case M2: … … default: push(m) } yield mainWindow; } }
221. Things you can do with continuations:
• Good old function calls • Closures • Generators • Exceptions • Coroutines With continuations, you can conceal the fact that Web server should be stateless; (you just provide the client with the server’s continuation record)
Useful in browser applications such Gmail 218. Continuations
Quite an obscure and abstract definition:
6.5.7
Definition 6.40 (Continuation). A continuation is an abstract representation of the control state of a computer program More plainly, a continuation is making the activation record (more generally, the generating record) into a 1st class citizen.
Exercises
1. Which items stored in activation records are not part of the data stored with continuations? 2. How are closures different than anonymous functions? 3. How would you argue that C supports first class functions?
• Invented many years ago
4. What’s non-preemptive multitasking? How is it related to coroutines? Which is more general?
• Has full implementation in many dead and obscure PLs
5. Why is C++ forced to support nested functions?
• Has partial/non-official implementation in many mainstream PLs
6. Enumerate the data items that continuations have to store.
• Did not catch (yet!)
7. What’s the difference between static- and non-static nested classes?
219. Data stored in a continuation
8. What’s the difference between generators and iterators?
Code Where the function is stored in memory, its name, and other meta information, e.g., for debugging.
9. Enumerate all operations allowed on first-class functions.
Environment Activation record, including back-pointers as necessary.
10. Can you use Java’s nested classes to implement anonymous functions?
CPU State The saved registers, most importantly, the PC register
11. Discuss the restrictions placed on nested classes in C++.
Result Last result returned/yielded by the function
12. How are closures different than lambda functions?
Termination status can the continuation be continued
13. What’s the difference, if any, between Java’s local classes, and non-static nested classes? 43
14. Discuss the restrictions placed on anonymous functions in Gnu-C.
41. Can coroutines be implemented with closures? Explain.
15. Given is a language which only has generators, can you use these to implement coroutines?
42. Explain why letting the called function to remove the arguments from the stack is more efficient than having the caller do so.
16. What’s the etymology of the name “lambda” functions?
43. What’s the difference between coroutines and generators?
17. How are C++ nested classes different than those of Java?
44. Does Java make distinction between generators and iterators? How?
18. Why do continuations require GC? 19. Why does Pascal refuse to give equal rights to functions? 20. Can closures be implemented with activation records? Explain. 21. What are the two meanings of the word “yield”? How are these meaning used in generators and 22. Which stored in activation records are not part of the data stored with closures? 23. Explain why C does not have nested functions. 24. Are data items stored with activation records different in C and in Pascal? 25. Enumerate the data items stored in an activation record? 26. Are nested functions in C++ first class? 27. Enumerate the data items that closures have to store. 28. Can you use Java’s nested classes to implement closures? 29. When are closures planned for Java? What’s the proposed syntax? Any restrictions on the implementation? 30. Why do closures require GC? 31. Which data items stored with closures do not occur in activation records? 32. How does Java implement generators? 33. What information is contained in coroutines and is missing in closures? 34. When are closures planned for C++? What’s the proposed syntax? Any restrictions on the implementation? 35. Can one emulate closures with coroutines? Explain. 36. Are goroutines a kind of coroutines? 37. In C the calling function is responsible for removing the pushed arguments from the stack. Why is this the only reasonable implementation? 38. Describe the typical situation in which you want your closure to be anonymous? 39. Can you use Java’s nested classes to implement coroutines? If yes, explain how; if no, explain why not. 40. Experts claim that all “implementation of all web services are poor-man’s continuations”. Explain and elaborate. 44