
- •New to the Tenth Edition
- •Preface
- •Acknowledgments
- •About the Author
- •Contents
- •1.1 Reasons for Studying Concepts of Programming Languages
- •1.2 Programming Domains
- •1.3 Language Evaluation Criteria
- •1.4 Influences on Language Design
- •1.5 Language Categories
- •1.6 Language Design Trade-Offs
- •1.7 Implementation Methods
- •1.8 Programming Environments
- •Summary
- •Problem Set
- •2.1 Zuse’s Plankalkül
- •2.2 Pseudocodes
- •2.3 The IBM 704 and Fortran
- •2.4 Functional Programming: LISP
- •2.5 The First Step Toward Sophistication: ALGOL 60
- •2.6 Computerizing Business Records: COBOL
- •2.7 The Beginnings of Timesharing: BASIC
- •2.8 Everything for Everybody: PL/I
- •2.9 Two Early Dynamic Languages: APL and SNOBOL
- •2.10 The Beginnings of Data Abstraction: SIMULA 67
- •2.11 Orthogonal Design: ALGOL 68
- •2.12 Some Early Descendants of the ALGOLs
- •2.13 Programming Based on Logic: Prolog
- •2.14 History’s Largest Design Effort: Ada
- •2.15 Object-Oriented Programming: Smalltalk
- •2.16 Combining Imperative and Object-Oriented Features: C++
- •2.17 An Imperative-Based Object-Oriented Language: Java
- •2.18 Scripting Languages
- •2.19 The Flagship .NET Language: C#
- •2.20 Markup/Programming Hybrid Languages
- •Review Questions
- •Problem Set
- •Programming Exercises
- •3.1 Introduction
- •3.2 The General Problem of Describing Syntax
- •3.3 Formal Methods of Describing Syntax
- •3.4 Attribute Grammars
- •3.5 Describing the Meanings of Programs: Dynamic Semantics
- •Bibliographic Notes
- •Problem Set
- •4.1 Introduction
- •4.2 Lexical Analysis
- •4.3 The Parsing Problem
- •4.4 Recursive-Descent Parsing
- •4.5 Bottom-Up Parsing
- •Summary
- •Review Questions
- •Programming Exercises
- •5.1 Introduction
- •5.2 Names
- •5.3 Variables
- •5.4 The Concept of Binding
- •5.5 Scope
- •5.6 Scope and Lifetime
- •5.7 Referencing Environments
- •5.8 Named Constants
- •Review Questions
- •6.1 Introduction
- •6.2 Primitive Data Types
- •6.3 Character String Types
- •6.4 User-Defined Ordinal Types
- •6.5 Array Types
- •6.6 Associative Arrays
- •6.7 Record Types
- •6.8 Tuple Types
- •6.9 List Types
- •6.10 Union Types
- •6.11 Pointer and Reference Types
- •6.12 Type Checking
- •6.13 Strong Typing
- •6.14 Type Equivalence
- •6.15 Theory and Data Types
- •Bibliographic Notes
- •Programming Exercises
- •7.1 Introduction
- •7.2 Arithmetic Expressions
- •7.3 Overloaded Operators
- •7.4 Type Conversions
- •7.5 Relational and Boolean Expressions
- •7.6 Short-Circuit Evaluation
- •7.7 Assignment Statements
- •7.8 Mixed-Mode Assignment
- •Summary
- •Problem Set
- •Programming Exercises
- •8.1 Introduction
- •8.2 Selection Statements
- •8.3 Iterative Statements
- •8.4 Unconditional Branching
- •8.5 Guarded Commands
- •8.6 Conclusions
- •Programming Exercises
- •9.1 Introduction
- •9.2 Fundamentals of Subprograms
- •9.3 Design Issues for Subprograms
- •9.4 Local Referencing Environments
- •9.5 Parameter-Passing Methods
- •9.6 Parameters That Are Subprograms
- •9.7 Calling Subprograms Indirectly
- •9.8 Overloaded Subprograms
- •9.9 Generic Subprograms
- •9.10 Design Issues for Functions
- •9.11 User-Defined Overloaded Operators
- •9.12 Closures
- •9.13 Coroutines
- •Summary
- •Programming Exercises
- •10.1 The General Semantics of Calls and Returns
- •10.2 Implementing “Simple” Subprograms
- •10.3 Implementing Subprograms with Stack-Dynamic Local Variables
- •10.4 Nested Subprograms
- •10.5 Blocks
- •10.6 Implementing Dynamic Scoping
- •Problem Set
- •Programming Exercises
- •11.1 The Concept of Abstraction
- •11.2 Introduction to Data Abstraction
- •11.3 Design Issues for Abstract Data Types
- •11.4 Language Examples
- •11.5 Parameterized Abstract Data Types
- •11.6 Encapsulation Constructs
- •11.7 Naming Encapsulations
- •Summary
- •Review Questions
- •Programming Exercises
- •12.1 Introduction
- •12.2 Object-Oriented Programming
- •12.3 Design Issues for Object-Oriented Languages
- •12.4 Support for Object-Oriented Programming in Smalltalk
- •12.5 Support for Object-Oriented Programming in C++
- •12.6 Support for Object-Oriented Programming in Objective-C
- •12.7 Support for Object-Oriented Programming in Java
- •12.8 Support for Object-Oriented Programming in C#
- •12.9 Support for Object-Oriented Programming in Ada 95
- •12.10 Support for Object-Oriented Programming in Ruby
- •12.11 Implementation of Object-Oriented Constructs
- •Summary
- •Programming Exercises
- •13.1 Introduction
- •13.2 Introduction to Subprogram-Level Concurrency
- •13.3 Semaphores
- •13.4 Monitors
- •13.5 Message Passing
- •13.6 Ada Support for Concurrency
- •13.7 Java Threads
- •13.8 C# Threads
- •13.9 Concurrency in Functional Languages
- •13.10 Statement-Level Concurrency
- •Summary
- •Review Questions
- •Problem Set
- •14.1 Introduction to Exception Handling
- •14.2 Exception Handling in Ada
- •14.3 Exception Handling in C++
- •14.4 Exception Handling in Java
- •14.5 Introduction to Event Handling
- •14.6 Event Handling with Java
- •14.7 Event Handling in C#
- •Review Questions
- •Problem Set
- •15.1 Introduction
- •15.2 Mathematical Functions
- •15.3 Fundamentals of Functional Programming Languages
- •15.4 The First Functional Programming Language: LISP
- •15.5 An Introduction to Scheme
- •15.6 Common LISP
- •15.8 Haskell
- •15.10 Support for Functional Programming in Primarily Imperative Languages
- •15.11 A Comparison of Functional and Imperative Languages
- •Review Questions
- •Problem Set
- •16.1 Introduction
- •16.2 A Brief Introduction to Predicate Calculus
- •16.3 Predicate Calculus and Proving Theorems
- •16.4 An Overview of Logic Programming
- •16.5 The Origins of Prolog
- •16.6 The Basic Elements of Prolog
- •16.7 Deficiencies of Prolog
- •16.8 Applications of Logic Programming
- •Review Questions
- •Programming Exercises
- •Bibliography
- •Index

432 |
Chapter 9 Subprograms |
The makeAdder function is called twice, once with a parameter of 10 and once with 5. Each of these calls returns a different version of the closure because they are bound to different values of x. The first call to makeAdder creates a function that adds 10 to its parameter; the second creates a function that adds 5 to its parameter. The two versions of the function are bound to different activations of makeAdder. Obviously, the lifetime of the version of x created when makeAdder is called must extend over the lifetime of the program.
This same closure function can be written in C# using a nested anonymous delegate. The type of the nesting method is specified to be a function that takes an int as a parameter and returns an anonymous delegate. The return type is specified with the special notation for such delegates, Func<int, int>. The first type in the angle brackets is the parameter type. Such a delegate can encapsulate methods that have only one parameter. The second type is the return type of the method encapsulated by the delegate.
static Func<int, int> makeAdder(int x) { return delegate(int y) { return x + y;};
}
...
Func<int, int> Add10 = makeAdder(10);
Func<int, int> Add5 = makeAdder(5);
Console.WriteLine("Add 10 to 20: {0}", Add10(20));
Console.WriteLine("Add 5 to 20: {0}", Add5(20));
The output of this code is exactly the same as for the previous JavaScript closure example.
The anonymous delegate could have been written as a lambda expression. The following is a replacement for the body of the makeAdder method, using a lambda expression instead of the delegate:
return y => x + y
Ruby’s blocks are implemented so that they can reference variables visible in the position in which they were defined, even if they are called at a place in which those variables would have disappeared. This makes such blocks closures.
9.13 Coroutines
A coroutine is a special kind of subprogram. Rather than the master-slave relationship between a caller and a called subprogram that exists with conventional subprograms, caller and called coroutines are more equitable. In fact, the coroutine control mechanism is often called the symmetric unit control model.
Coroutines can have multiple entry points, which are controlled by the coroutines themselves. They also have the means to maintain their status between activations. This means that coroutines must be history sensitive and

9.13 Coroutines |
433 |
thus have static local variables. Secondary executions of a coroutine often begin at points other than its beginning. Because of this, the invocation of a coroutine is called a resume rather than a call.
For example, consider the following skeletal coroutine:
sub co1(){
...
resume co2();
...
resume co3();
...
}
The first time co1 is resumed, its execution begins at the first statement and executes down to and including the resume of co2, which transfers control to co2. The next time co1 is resumed, its execution begins at the first statement after its call to co2. When co1 is resumed the third time, its execution begins at the first statement after the resume of co3.
One of the usual characteristics of subprograms is maintained in coroutines: Only one coroutine is actually in execution at a given time.
As seen in the example above, rather than executing to its end, a coroutine often partially executes and then transfers control to some other coroutine, and when restarted, a coroutine resumes execution just after the statement it used to transfer control elsewhere. This sort of interleaved execution sequence is related to the way multiprogramming operating systems work. Although there may be only one processor, all of the executing programs in such a system appear to run concurrently while sharing the processor. In the case of coroutines, this is sometimes called quasi-concurrency.
Typically, coroutines are created in an application by a program unit called the master unit, which is not a coroutine. When created, coroutines execute their initialization code and then return control to that master unit. When the entire family of coroutines is constructed, the master program resumes one of the coroutines, and the members of the family of coroutines then resume each other in some order until their work is completed, if in fact it can be completed. If the execution of a coroutine reaches the end of its code section, control is transferred to the master unit that created it. This is the mechanism for ending execution of the collection of coroutines, when that is desirable. In some programs, the coroutines run whenever the computer is running.
One example of a problem that can be solved with this sort of collection of coroutines is a card game simulation. Suppose the game has four players who all use the same strategy. Such a game can be simulated by having a master program unit create a family of coroutines, each with a collection, or hand, of cards. The master program could then start the simulation by resuming one of the player coroutines, which, after it had played its turn, could resume the next player coroutine, and so forth until the game ended.

434 |
Chapter 9 Subprograms |
|
|
|
|
|
|
||||
Figure 9.3 |
|
|
resume |
|
|
|
A |
B |
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
from master |
|
• |
|
|
|
• |
||
Two possible execution |
|
|
|
|
|||||||
|
• |
|
|
|
• |
||||||
control sequences for |
|
|
|
|
|||||||
|
resume B |
|
|
|
resume A |
||||||
two coroutines without |
|
• |
|
|
|
• |
|||||
loops |
|
|
|
|
|
|
• |
|
|
|
• |
|
|
|
|
|
|
• |
|
|
|
• |
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
resume B |
|
|
|
• |
|
|
|
|
|
|
|
• |
|
|
|
resume A |
|
|
|
|
|
|
|
• |
|
|
|
• |
|
|
|
|
|
|
|
• |
|
|
|
• |
|
|
|
|
|
|
|
• |
|
|
|
• |
|
|
|
|
|
|
|
resume B |
|
|
|
• |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(a) |
|
|||
|
|
|
A |
|
|
|
|
|
B |
||
|
|
|
|
|
|
|
resume |
|
|
|
|
|
|
|
• |
|
from master |
• |
|||||
|
|
|
• |
|
|
|
|
|
• |
||
|
|
|
• |
|
|
|
|
|
• |
||
|
|
|
resume B |
|
|
|
|
|
• |
||
|
|
|
• |
|
|
|
|
|
resume A |
||
|
|
|
• |
|
|
|
|
|
• |
||
|
|
|
• |
|
|
|
|
|
• |
||
|
|
|
resume B |
|
|
|
|
|
• |
||
|
|
|
|
|
|
|
|
|
|
|
• |
|
|
|
|
|
|
|
|
|
|
|
• |
|
|
|
|
|
|
|
|
|
|
|
resume A |
|
|
|
|
|
|
|
|
|
|
|
• |
|
|
|
|
|
|
|
|
|
|
|
• |
|
|
|
|
|
|
|
|
|
|
|
• |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(b) |
|
Suppose program units A and B are coroutines. Figure 9.3 shows two ways an execution sequence involving A and B might proceed.
In Figure 9.3a, the execution of coroutine A is started by the master unit. After some execution, A starts B. When coroutine B in Figure 9.3a first causes control to return to coroutine A, the semantics is that A continues from where it ended its last execution. In particular, its local variables have the values left them by the previous activation. Figure 9.3b shows an alternative execution sequence of coroutines A and B. In this case, B is started by the master unit.
Rather than have the patterns shown in Figure 9.3, a coroutine often has a loop containing a resume. Figure 9.4 shows the execution sequence of this scenario. In this case, A is started by the master unit. Inside its main loop, A resumes B, which in turn resumes A in its main loop.
Among contemporary languages, only Lua fully supports coroutines.13
13. However, the generators of Python are a form of coroutines.

Figure 9.4 |
A |
|||
|
resume |
|
|
|
|
|
. |
||
|
from master |
|||
Coroutine execution |
• |
|||
. |
||||
sequence with loops |
• |
|||
. |
||||
|
|
|
|
• |
|
|
|
|
. |
|
|
|
|
• |
|
|
|
|
. |
|
|
|
|
• |
|
|
|
|
. |
|
|
|
|
• |
|
|
|
|
resume B |
|
|
|
|
. |
|
|
|
|
• |
|
|
|
|
. |
|
|
|
|
• |
|
|
|
|
. |
|
|
|
|
• |
|
|
|
|
. |
|
|
|
|
• |
|
|
|
|
. |
|
|
|
|
• |
|
|
|
|
|
Summary 435
B
|
. |
|
|
• |
|
|
. |
|
resume |
• |
|
. |
||
|
• |
|
|
. |
|
First |
• |
|
. |
||
|
||
|
• |
|
|
. |
|
|
• |
|
|
. |
|
|
• |
|
|
. |
|
|
• |
|
|
resume A |
|
|
. |
|
Subsequent |
• |
|
. |
||
resume |
• |
|
. |
||
|
• |
|
|
|
S U M M A R Y
Process abstractions are represented in programming languages by subprograms. A subprogram definition describes the actions represented by the subprogram. A subprogram call enacts those actions. A subprogram header identifies a subprogram definition and provides its interface, which is called its protocol.
Formal parameters are the names that subprograms use to refer to the actual parameters given in subprogram calls. In Python and Ruby, array and hash formal parameters are used to support variable numbers of parameters. Lua and JavaScript also support variable numbers of parameters. Actual parameters can be associated with formal parameters by position or by keyword. Parameters can have default values.
Subprograms can be either functions, which model mathematical functions and are used to define new operations, or procedures, which define new statements.
Local variables in subprograms can be stack dynamic, providing support for recursion, or static, providing efficiency and history-sensitive local variables.
JavaScript, Python, Ruby, and Lua allow subprogram definitions to be nested.
There are three fundamental semantics models of parameter passing—in mode, out mode, and inout mode—and a number of approaches to implementing them. These are pass-by-value, pass-by-result, pass-by-value-result, pass- by-reference, and pass-by-name. In most languages, parameters are passed in the run-time stack.
Aliasing can occur when pass-by-reference parameters are used, both among two or more parameters and between a parameter and an accessible nonlocal variable.

436 |
Chapter 9 Subprograms |
Parameters that are multidimensioned arrays pose some issues for the language designer, because the called subprogram needs to know how to compute the storage mapping function for them. This requires more than just the name of the array.
Parameters that are subprogram names provide a necessary service but can be difficult to understand. The opacity lies in the referencing environment that is available when a subprogram that has been passed as a parameter is executed.
C and C++ support pointers to functions. C# has delegates, which are objects that can store references to methods. Delegates can support multicast calls by storing more than one method reference.
Ada, C++, C#, Ruby, and Python allow both subprogram and operator overloading. Subprograms can be overloaded as long as the various versions can be disambiguated by the types of their parameters or returned values. Function definitions can be used to build additional meanings for operators.
Subprograms in C++, Java 5.0, and C# 2005 can be generic, using parametric polymorphism, so the desired types of their data objects can be passed to the compiler, which then can construct units for the requested types.
The designer of a function facility in a language must decide what restrictions will be placed on the returned values, as well as the number of return values.
A closure is a subprogram and its referencing environment. Closures are useful in languages that allow nested subprograms, are static scoped, and allow subprograms to be returned from functions and assigned to variables.
A coroutine is a special subprogram that has multiple entries. It can be used to provide interleaved execution of subprograms.
R E V I E W Q U E S T I O N S
1.What are the three general characteristics of subprograms?
2.What does it mean for a subprogram to be active?
3.What is given in the header of a subprogram?
4.What characteristic of Python subprograms sets them apart from those of other languages?
5.What languages allow a variable number of parameters?
6.What is a Ruby array formal parameter?
7.What is a parameter profile? What is a subprogram protocol?
8.What are formal parameters? What are actual parameters?
9.What are the advantages and disadvantages of keyword parameters?
10.What are the differences between a function and a procedure?
11.What are the design issues for subprograms?
12.What are the advantages and disadvantages of dynamic local variables?

Review Questions |
437 |
13.What are the advantages and disadvantages of static local variables?
14.What languages allow subprogram definitions to be nested?
15.What are the three semantic models of parameter passing?
16.What are the modes, the conceptual models of transfer, the advantages, and the disadvantages of pass-by-value, pass-by-result, pass-by-value- result, and pass-by-reference parameter-passing methods?
17.Describe the ways that aliases can occur with pass-by-reference parameters.
18.What is the difference between the way original C and C89 deal with an actual parameter whose type is not identical to that of the corresponding formal parameter?
19.What are two fundamental design considerations for parameter-passing methods?
20.Describe the problem of passing multidimensioned arrays as parameters.
21.What is the name of the parameter-passing method used in Ruby?
22.What are the two issues that arise when subprogram names are parameters?
23.Define shallow and deep binding for referencing environments of subprograms that have been passed as parameters.
24.What is an overloaded subprogram?
25.What is parametric polymorphism?
26.What causes a C++ template function to be instantiated?
27.In what fundamental ways do the generic parameters to a Java 5.0 generic method differ from those of C++ methods?
28.If a Java 5.0 method returns a generic type, what type of object is actually returned?
29.If a Java 5.0 generic method is called with three different generic parameters, how many versions of the method will be generated by the compiler?
30.What are the design issues for functions?
31.What two languages allow multiple values to be returned from a function?
32.What exactly is a delegate?
33.What is the main drawback of generic functions in F#?
34.What is a closure?
35.What are the language characteristics that make closures useful?
36.What languages allow the user to overload operators?
37.In what ways are coroutines different from conventional subprograms?

438 |
Chapter 9 Subprograms |
|
|
|
P R O B L E M S E T |
|
|
1.What are arguments for and against a user program building additional definitions for existing operators, as can be done in Python and C++? Do you think such user-defined operator overloading is good or bad? Support your answer.
2.In most Fortran IV implementations, parameters were passed by reference, using access path transmission only. State both the advantages and disadvantages of this design choice.
3.Argue in support of the Ada 83 designers’ decision to allow the implementor to choose between implementing inout-mode parameters by copy or by reference.
4.Suppose you want to write a method that prints a heading on a new output page, along with a page number that is 1 in the first activation and that increases by 1 with each subsequent activation. Can this be done without parameters and without reference to nonlocal variables in Java? Can it be done in C#?
5.Consider the following program written in C syntax:
void swap(int a, int b) { int temp;
temp = a; a = b;
b = temp;
}
void main() {
int value = 2, list[5] = {1, 3, 5, 7, 9}; swap(value, list[0]);
swap(list[0], list[1]); swap(value, list[value]);
}
For each of the following parameter-passing methods, what are all of the values of the variables value and list after each of the three calls to swap?
a.Passed by value
b.Passed by reference
c.Passed by value-result
6.Present one argument against providing both static and dynamic local variables in subprograms.
7.Consider the following program written in C syntax:
void fun (int first, int second) { first += first;