Subprograms
A letter written for another human is organized into paragraphs, and each paragraph represents a coherent chunk of thought. Paragraphs make the letter easier to write and easier to read. Likewise, a program is made of subprograms, and each subprogram represents a small and focused chunk of a larger algorithmic process.
Subprograms ease the pain of writing complex software. They do this in several ways:
- Subprograms are used to decompose a complex algorithm into smaller, independent tasks, and each subprogram may be written by a different member of the development team.
- Each subprogram operates in its own scope. It is insulated from the rest of the program and is less likely to violate another subprogram's data.
- A subprogram gives a sequence of code a name and a clear purpose, which focuses the task for the programmer implementing it.
- Subprograms define a reusable action that may be invoked in a variety of contexts. In this sense, subprograms are like spells or recipes.
- Each subprogram can be tested in isolation.
Many terms have been used to describe subprograms. A subprogram defined in a class is a method, whereas a subprogram not in a class is a function. This distinction is often unimportant, however. Henceforth, we will use the term function to refer to any kind of subprogram, no matter its context, and method to refer to a function in a class. Some developers strictly define function as a subprogram that returns a value and procedure or subroutine as a subprogram that produces some side-effect but does not return a value. We ignore this distinction as well. For our purposes, all subprograms are functions.
Programming languages provide special syntax for defining functions. Defining a function is not the same as executing a function, just as writing a recipe doesn't produce anything edible. Other code must call the function for its code to be executed. Functions often depend on information from outside their scope and produce information needed in the calling scope. Information is shared across scope boundaries via parameters and return values. A language's function definition syntax usually provides some means of describing this shared information.
The syntax of a function definition consists of two main parts: a header and a body. At the very least, the header names the function, but it may also list parameters, return values, modifiers that influence the function's behavior or visibility, and exceptions that it may throw. The header establishes the interface of the function, which is the public information that callers must know in order to call the function correctly and understand the value that is returned. The body is the code that will be executed when the function is called. Its details are generally hidden from the caller, who should ideally not concerned with the function's implementation.
In this chapter, we will explore how functions shape the programming experience in our most widely-used languages. By its end, you'll be able to answer the following questions:
- What forms do function definitions and calls take in our myriad languages?
- How is data communicated from the caller to the function and from the function back to the caller?
- How can a single name be associated with and invoke several different functions?
- How can common algorithmic patterns be abstracted away into functions when not all their steps are known ahead of time?
Just as crossing a river is made easier by strategically placing rocks, so too is solving a computation made easier by strategically defining functions. Let's see how they ease the difficulties of programming.