On this page:
2.1 Arithmetic
2.1.1 The Arithmetic of Numbers
2.1.2 The Arithmetic of Strings
2.1.3 Mixing It Up
2.1.4 The Arithmetic of Images
2.1.5 The Arithmetic of Booleans
2.1.6 Mixing It Up with Booleans
2.1.7 Predicates: Know Thy Data
2.2 Functions and Programs
2.2.1 Functions
2.2.2 Composing Functions
2.2.3 Programs
2.3 How to Design Programs
2.3.1 Designing Functions
2.3.2 Finger Exercises
2.3.3 Domain Knowledge
2.3.4 From Functions to Programs
2.3.5 On Testing
2.3.6 Designing World Programs
2.3.7 Virtual Pet Worlds
2.4 Intervals, Enumerations, etc.
2.4.1 Conditional Computations
2.4.2 How It Works
2.4.3 Enumerations
2.4.4 Intervals
2.4.5 Itemizations
2.4.6 Designing with Itemizations
2.4.7 A Bit More About Worlds
2.5 Adding Structure
2.5.1 Structures
2.5.2 Programming with Structures
2.5.3 The Universe of Data
2.5.4 Designing with Structures
2.5.5 Structure in the World
2.5.6 A Graphical Editor
2.5.7 More Virtual Pets
2.6 Itemizations and Structures
2.6.1 Designing with Itemizations, Again
2.6.2 Mixing up Worlds
2.6.3 Input Errors
2.7 Summary

2 Fixed-Size Data

2.1 Arithmetic

When a programmer studies a new language, the first item of business is the language’s “arithmetic,” meaning its basic forms of data and the operations that a program can perform on this data. At the same time, we need to learn how to express data and how to express operations on data.

It is not necessary to understand all forms of data or all operations at once; it suffices to know what exists and where to find out more. Here we take a look at the “arithmetic” of BSL, the Beginning Student language. Thus far, we know how to use operations:
  • write "(",

  • write down the name of a primitive operation op,

  • write down the arguments, separated by some space, and

  • write down ")".

Recall what you are writing down is called an expression.

Just as a reminder, here is a primitive expression:

(+ 1 2)

It uses +, the operation for adding two numbers, followed by two arguments, which are plain numbers. But here is another, somewhat complex example:

(+ 1 (+ 1 (+ 1 1) 2) 3 4 5)

This second example exploits two points in our description that are open to interpretation. First, some primitive operations may consume more than two arguments. Second, the arguments don’t have to be numbers or data per se; they can be expressions, too.

Evaluating expressions is also straightforward. First, BSL evaluates all the arguments of a primitive application. Second, it “feeds” the resulting pieces of data to the primitive operation, which produces a result. Thus,
(+ 1 2)
=
3
and
(+ 1 (+ 1 (+ 1 1) 2) 3 (+ 2 2) 5)
=
(+ 1 (+ 1 2 2) 3 4 5)
=
(+ 1 5 3 4 5)
=
18
These calculations should look familiar, because they are the same kind of calculations that you performed in mathematics classes. You may have written down the steps in a different way; you may have never been taught how to write down a sequence of calculation steps. Yet, BSL performs calculations just like you do, and this should be a relief. It guarantees that you understand what it does with primitive operations and primitive data, so there is some hope that you can predict what your programs will compute. Generally speaking, it is critical for a programmer to know how the chosen language calculates, because otherwise a program’s computation may harm the people who use them or on whose behalf the programs calculate.

It is not necessary to read and understand the entire chapter in order to make progress. As soon as you sense that this chapter is slowing you down, move on to the next one. Keep in mind, though, that you may wish to return here and find out more about the basic forms of data in BSL when the going gets rough.

The rest of this chapter introduces four forms of data: numbers, strings, images, and Boolean values. It also illustrates how these forms of data are manipulated with primitive operations, often called built-in operations or primitive functions. In many cases, these manipulations involve more than one form of data.

2.1.1 The Arithmetic of Numbers

Most people think “numbers” and “operations on numbers” when they hear “arithmetic.” “Operations on numbers” means adding two numbers to yield a third; subtracting one number from another; or even determining the greatest common divisor of two numbers. If we don’t take arithmetic too literally, we may even include the sine of an angle, rounding a real number to the closest integer, and so on.

The BSL language supports Numbers and arithmetic in all these forms. As discussed in the Prologue, an arithmetic operation such as + is used like this:

(+ 3 4)

i.e., in prefix notation form. Here are some of the operations on numbers that our language provides: +, -, *, /, abs, add1, ceiling, denominator, exact->inexact, expt, floor, gcd, log, max, numerator, quotient, random, remainder, sqr, and tan. We picked our way through the alphabet, just to show the variety of operations. Explore what these do in the interactions area, and then find out how many more there are and what they do.

If you need an operation on numbers that you know from grade school or high school, chances are that BSL knows about it, too. Guess its name and experiment in the interaction area. Say you need to compute the sin of some angle; try

> (sin 0)

0

and use it happily ever after. Or look in the HelpDesk. You will find there that in addition to operations, BSL also recognizes the names of some widely used numbers, e.g., pi and e.

When it comes to numbers, BSL programs may use natural numbers, integers, rational numbers, real numbers, and complex numbers. We assume that you have heard of the first four. The last one may have been mentioned in your high school. If not, don’t worry; while complex numbers are useful for all kinds of calculations, a novice doesn’t have to know about them.

A truly important distinction concerns the precision of numbers. For now, it is important to understand that BSL distinguishes exact numbers and inexact numbers. When it calculates with exact numbers, BSL preserves this precision whenever possible. For example, (/ 4 6) produces the precise fraction 2/3, which DrRacket can render as a proper fraction, an improper fraction, or as a mixed decimal. Click (on the fraction) and choose.

Some of BSL’s numeric operations cannot produce an exact result. For example, using the sqrt operation on 2 produces an irrational number that cannot be described with a finite number of digits. Because computers are of finite size and BSL must somehow fit such numbers into the computer, it chooses an approximation: #i1.4142135623730951. As mentioned in the Prologue, the #i prefix warns novice programmers of this lack of precision. While most programming languages choose to reduce precision in this manner, few advertise it and fewer even warn programmers.

Exercise 1: Add the following two lines to the definitions area of DrRacket:
(define x 8)
(define y 36)
Create an expression that computes the distance of the Cartesian point (x,y) from the origin (0,0)

Just in case you haven’t taken geometry yet or in case you forgot the formula that you encountered there, the point (x,y) has the distance

from the origin.

To create expressions it is best to hit RUN and to experiment in the interactions area.

Once you have the expression, copy it below the two definitions into the definitions area. Change the two definitions in some way, figure out what the result of the expression should be, and then RUN it to find out what it really is. You may wish to use 12 for x and 5 for y as a first test. Another good example consists of 3 and 4.

As you experiment, do not change the expression unless you think it is wrong.

2.1.2 The Arithmetic of Strings

A wide-spread prejudice about computers concerns its innards. Many believe that it is all about bits and bytes—whatever those are—and possibly numbers, because everyone knows that computers can calculate. While it is true that electrical engineers must understand and study the computer as just such an object, programmers and everyone else should never (ever) succumb to this thinking.

Programming languages are about calculating with information, and information comes in all shapes and forms. For example, a program may deal with colors, names, business letters, or conversations between people. Even though we could encode this kind of information as numbers, it would be a horrible idea. Just imagine remembering large tables of codes, such as 0 means “red” and 1 means “hello,” etc.

Instead most programming languages provide at least one kind of data that deals with such symbolic information. For now, we use BSL’s strings. Generally speaking, a String is a sequence of the characters that you can enter on the keyboard enclosed in double quotes, plus a few others, about which we aren’t concerned just yet. In Prologue: How to Program, we have seen a number of BSL strings: "hello", "world", "blue", "red", etc. The first two are words that may show up in a conversation or in a letter; the others are names of colors that we may wish to use.

BSL includes only one operation that exclusively consumes and produces strings: string-append, which, as we have seen in Prologue: How to Program concatenates two given strings into one. You should think of string-append as an operation that is just like +. While the latter consumes two (or more) numbers and produces a new number, the former consumes two or more strings and produces a new string:
> (string-append "what a " "lovely " "day" " for learning BSL")

"what a lovely day for learning BSL"

Nothing about the given numbers changes when + adds them up; and nothing about the given strings changes when string-append juxtaposes them into one big string.

Exercise 2: Add the following two lines to the definitions area:
(define prefix "hello")
(define suffix "world")
Then create an expression using string primitives that concatenates prefix and suffix and adds "_" between them. So the result for these two definitions should be "hello_world", but see exercise 1 for how to create expressions and how to make sure they really work.

2.1.3 Mixing It Up

All other operations concerning strings consume or produce data other than strings. Here are some examples:
  • string-length consumes a string and produces a (natural) number;

  • string-ith consumes a string s and extracts the one-character substring located at the ith position (counting from 0); and

  • number->string consumes a number and produces a string.

Also look up substring and find out what it does.

If the documentation in HelpDesk appears confusing, experiment with the functions in the interaction area. Give them appropriate arguments, find out what they compute. Also use inappropriate arguments for some operations just to find out how BSL reacts:
> (string-length 42)

string-length: expects argument of type <string>; given: 42

As you can see, BSL reports an error. The first part “string-length” informs you about the operation that is misapplied; the second half states what is wrong with the arguments. In this specific example, string-length is supposed to be applied to a string but is given a number, specifically 42.

Naturally, it is possible to nest operations that consume and produce different kinds of data as long as you keep track of what is proper and what is not. Consider this expression from the Prologue:

(+ (string-length "hello world") 60)

The inner expression applies string-length to our favorite string, "hello world". The outer expression has + consume the result of the inner expression and 60. Let us calculate out the result:
(+ (string-length "hello world") 60)
=
(+ 11 60)
=
71
Not surprisingly, calculating with such nested expressions that deal with a mix of data is no different from calculating with numeric expressions. Here is another example:
(+ (string-length (number->string 42)) 2)
=
(+ (string-length "42") 2)
=
(+ 2 2)
=
4
Before you go on, construct some nested expressions that mix data in the wrong way, e.g.,

(+ (string-length 42) 1)

Run them in DrRacket. Study the red error message but also watch what DrRacket highlights in the definitions area.

Exercise 3: Add the following two lines to the definitions area:
(define str "helloworld")
(define i 5)
Then create an expression using string primitives that adds "_" at position i. In general this means the resulting string is longer than the original one; here the expected result is "hello_world".

Position means i characters from the left of the string—but computer scientists start counting at 0. Thus, the 5 the letter in this example is "w", because the 0th letter is "h". Hint: when you encounter such “counting problems” you may wish to add a string of digits below str to help with counting:
(define str "helloworld")
(define ind "0123456789")
(define i 5)

See exercise 1 for how to create expressions and how to make sure they really work.

Exercise 4: Use the same setup as in exercise 3. Then create an expression that deletes the ith position from str. Clearly this expression creates a shorter string than the given one; contemplate which values you may choose for i.

2.1.4 The Arithmetic of Images

Images represent symbolic data somewhat like strings. To work with images, use the "2htdp/image" teachpack. Like strings, you used DrRacket to insert images wherever you would insert an expression into your program, because images are values just like numbers and strings.

Your programs can also create an image with primitive operations on images. These primitive operations come in three flavors. The first kind concerns the creation of images:
  • circle produces a circle image from a radius, a mode string, and a color string;

  • ellipse produces an ellipse from two radii, a mode string, and a color string;

  • line produces a line from two points and a color string;

  • rectangle produces a rectangle from a width, a height, a mode string, and a color string;

  • text produces a text image from a string, a font size, and a color string; and

  • triangle produces an upward-pointing equilateral triangle from a size, a mode string, and a color string.

These functions should really have an obvious meaning. All you need to know is that mode strings means either "solid" or "outline" and color strings are strings such as "orange", "black", etc. Play with these functions; enter (ellipse 10 20 "solid" "green") into DrRacket’s interaction window and see what happens.

BSL comes with two more image creation functions and both are fun. The first is star, which produces a star image. To do so, it needs the number of points, the size of the inner circle, the size of the outer circle, a mode string and a color string. Try (star 12 "solid" "green"). The second one is regular-polygon, which creates a regular polygon from a number of sides inscribed in a circle of a given radius, using a mode string and a color string. Look up its documentation and experiment.

The second kind of functions on images concern image properties:
  • image-width determines the width of a given image in terms of pixels;

  • image-height determines the height of an image;

A proper understanding of the third kind of image primitives—functions that compose images—requires the introduction of one new idea: the anchor point. An image isn’t just a single pixels; it consists of many pixels. Specifically, each image is like a photograph, i.e., a rectangle of pixels. One of these pixels—the upper left one—is the designated anchor point. When you use an image primitive to compose two images, the composition happens with respect to the anchor points unless you specify some other point explicitly:

  • overlay places all the images to which it is applied on top of each other, using the default anchor point for each.

  • overlay/xy is like overlay but accepts two numbers—x and ybetween two image arguments. It shifts the second image by x pixels to the right and y pixels down —all with respect to the images’ anchor points. Of course, the image is shifted left for a negative x and up for a positive y.

  • overlay/align is like overlay but accepts two strings that shift the anchor points to other parts of the rectangles. There are nine different positions overall; experiment with all possibilities!

The "2htdp/image" teachpack comes with many other primitive functions for combining images. As you get familiar with image processing, you will want to read up on those. For now, we introduce three more because they are important for creating animated scenes and images for games:
  • empty-scene creates an framed rectangle of a specified with and height;

  • place-image places an image into a scene at a specified position. If the image doesn’t fit into the given scene, it is appropriately cropped.

  • add-line consumes an scene, four numbers, and a color to draw a line of that color into the given image. Again, experiment with it to find out how the four arguments work together.

Copy and paste the image into your DrRacket.

Exercise 5: Add the following line to the definitions area:

(define cat )

Create an expression that computes the area of the image. See exercise 1 for how to create expressions and how to make sure they really work.

Exercise 6: Use the picture primitives to create the image of a simple automobile.

Exercise 7: Use the picture primitives to create the image of a simple boat.

Exercise 8: Use the picture primitives to create the image of a simple tree.

2.1.5 The Arithmetic of Booleans

We need one last kind of primitive data before we can design programs: Boolean values. There are only two kinds of Boolean values: true and false. Programs use Boolean values for representing decisions or the status of switches.

Calculating with Boolean values is simple, too. In particular, BSL programs get away with three operations: or, and, and not. These operations are kind of like addition, multiplication, and negation for numbers. Of course, because there are only two Boolean values, it is actually possible to demonstrate how these functions work in all possible situations:
It shouldn’t come as a surprise that or and and may be used with more than two Boolean values.

Finally, there is more to or and and than these explanations suggest, but to explain the extra bit requires another look at mixing up data in nested expressions.

Exercise 9: Add the following two lines to the definitions area of DrRacket:
(define b1 true)
(define b2 false)
Create an expression that computes whether b1 is false or b2 is true. So in this particular case, the answer is false. (Why?)

See exercise 1 for how to create expressions and how to make sure they really work. How many possible combinations of true and false can you think of for associating with b1 and b2?

2.1.6 Mixing It Up with Booleans

One important use of Boolean values concerns calculations with many different kinds of data. We know from the prologue that BSL programs may name values via definitions. For example, we could start a program like this

(define x 2)

and then compute its inverse:

(define inverse-of-x (/ 1 x))

This works fine, as long as we don’t edit the program and change x to 0.

This is where Boolean values come in, in particular conditional calculations. First, the primitive function = determines whether two (or more) numbers are equal. If so, it produces true, otherwise false. Second, there is kind of BSL expression that we haven’t mentioned so far: the if expression. It uses the word “if” as if it were a primitive function; it isn’t. The word “if” is followed by three expressions, separated by blank spaces (that includes tabs, line breaks, etc). Naturally the entire expression is enclosed in parentheses. Here is an example:

(if (= x 0) 0 (/ 1 x))

This if expression contains the subexpressions (= x 0), 0, and (/ 1 x). The evaluation of this expression proceeds in two steps:
  • The first expression is always evaluated. Its result must be a Boolean.

  • If the result of the first expression is true, then the second expression is evaluated; otherwise the third one. Whatever their results are they are also the result of the entire if expression.

You can experiment with if expressions in the interactions area:
> (if (= x 0) 0 (/ 1 x))

0.5

And you can reason out the result of this interaction yourself. Since x is 2, it is not equal to 0. Hence, (= x 0) produces the result false, meaning the third expression is evaluated and its result becomes the result of the entire if expression.

Now imagine you edit the definition so that it looks like this:

(define x 0)

What do you think

(if (= x 0) 0 (/ 1 x))

evaluates to in this context? Why?

In addition to =, BSL provides a host of other comparison primitives. Explain what the following four comparison primitives determine about numbers: <, <=, >, >=.

Strings aren’t compared with = and its relatives. Instead, you must use string=? or string<=? or string>=? if you are ever in a position where you need to compare strings. While it is obvious that string=? checks whether the two given strings are equal, the other two primitives are open to interpretation. Look up their documentation, or experiment with them, guess, and then check in the documentation whether you guessed right.

Images also come with one comparison operation: image=?. Like string=?, image=? compares two images for equality. You may wonder why it is ever necessary to compare strings with each other. So imagine a program that deals with traffic lights. It may use the strings "green", "yellow", and "red". This kind of program may contain a fragment such as this:
(define current-color ...)
 
(define next-color (if (string=? "green" current-color) "yellow" ...))
The dots here aren’t a part of the program of course. Replace them with a string that refers to a color. It should be easy to imagine that this fragment deals with the computation that determines which light bulb is to be turned on next and which one should be turned off.

The next few chapters introduce better expressions than if to express conditional computations and, most importantly, systematic ways for designing them.

Exercise 10: Add the following line to the definitions area:

(define cat )

Create an expression that computes whether the image is "tall" or "wide". An image should be labeled "tall" if its height is larger or equal to its width; otherwise it is "wide". See exercise 1 for how to create expressions and how to make sure they really work; as you experiment, replace the image of the cat with rectangles of your choice to ensure you know the expected answer.

Now try the following modification. Create an expression that computes whether a picture is "tall", "wide", or "square".

2.1.7 Predicates: Know Thy Data

Remember the expression (string-length 42) and its result. Actually, the expression doesn’t have a result; it signals an error. DrRacket lets you know about errors via red text in the interactions area and high-lighting of the expression. This way of marking errors is particular good when you use this expression (or its relatives) deeply nested within some other expression:

(* (+ (string-length 42) 1) pi)

Experiment with this expression by entering it into both DrRacket’s interactions area and in the definitions area (plus clicking on RUN).

Of course, you really don’t want such error-signaling expressions in your program. And usually, you don’t make such obvious mistakes as adding true to "hello world". It is quite common, however, that programs deal with variables that may stand for either a number or a string:
(define in ...)
 
(string-length in)
A variable such as in can be a place holder for any value, including a number, and this value then shows up in the string-length expression.

One way to prevent such accidents is to use a predicate, which is a function that consumes a value and determines whether or not it belongs to some class of data. For example, the predicate number? determines whether the given value is a number or not:
> (number? 4)

true

> (number? pi)

true

> (number? true)

false

> (number? "fortytwo")

false

As you see, the predicates produce Boolean values. Hence, when predicates are combined with conditional expressions, programs can protect expressions from misuse:
(define in ...)
 
(if (string? in) (string-length in) ...)

Every class of data that we introduced in this chapter comes with a predicate: string?, image?, and boolean?. Experiment with them to ensure you understand how they work.

Furthermore, programming languages classify numbers just as mathematics teachers do. In BSL, numbers are classified in two different directions. The first you may know from middle school or high school: integer?, rational?, real?, and complex?, even if you don’t know the last one. Evaluate (sqrt -1) in the interactions area and take a close look at the result. Your mathematics teacher may have told you that one doesn’t compute the square root of negative numbers. Truth is that in mathematics and in BSL, it is acceptable to do so, and the result is a so-called complex number. Don’t worry, though, complex numbers—while useful—play no role in this book. The second direction concerns the degree of exactness that we have mentioned before. There are two predicates: exact? and inexact?, and there are even functions that convert exact numbers into inexact ones, and vice versa. For now, just keep in mind that there are two kinds of numbers. Later, we are going to discuss the nature of numbers in some detail.

Exercise 11: Add the following line to the definitions area of DrRacket:

(define in "hello")

Then create an expression that converts whatever in represents to a number. For a string, it determines how long the string is; for an image, it uses the area; for a number, it decrements the number, unless it is already 0 or negative; for true it uses 10 and for false 20.

See exercise 1 for how to create expressions and how to make sure they really work.

Exercise 12: Now relax, eat, sleep, and then tackle the next chapter.

2.2 Functions and Programs

As far as programming is concerned, arithmetic is half the game. The other half is “algebra.” Of course, our notion of “algebra” relates to the school notion of algebra just as much as the notion of “arithmetic” from the preceding chapter relates to the ordinary notion of grade-school arithmetic. What we do mean is that the creation of interesting programs involves variables and—at least abstractly—functions. Once we can deal with functions, writing programs is within reach, though we need to understand that functions, like people, can collaborate (though following mathematics, we don’t call it “collaboration” but composition).

2.2.1 Functions

From a high-level perspective, a program is a function. A program, like a function in mathematics, consumes inputs, and it produces outputs. In contrast to mathematical functions, programs work with a whole variety of data: numbers, strings, images, and so on. Furthermore, programs may not consume all of the data at once; instead a program may incrementally request more data or not, depending on what the computation needs. Last but not least, programs are triggered by external events. For example, a scheduling program in an operating system may launch a monthly payroll program on the last day of every month. Or, a spreadsheet program may react to certain events on the keyboard with filling some cells with numbers.

Definitions: While many programming languages obscure the relationship between programs and functions, BSL brings it to the fore. Every BSL programs consists of definitions, usually followed by an expression that involves those definitions. There are two kinds of definitions:

Like expressions, function definitions in BSL come in a uniform shape:
  • write “(define (”,

  • write down the name of the function,

  • ... followed by one or more variables, separated by space and ending in “)”,

  • write down an expression,

  • write down “)”.

Just like an expression, the shape of a definition consists of pairs of balanced opening and closing parentheses, mingled with text.

Here are some silly examples:

Before we explain why these examples are silly, we need to explain what function definitions mean. Roughly speaking, a function definition introduces a new operation on data; put differently, it adds an operation to our vocabulary if we think of the primitive operations as the ones that are always available. Like a primitive operation, a defined operation consumes inputs. The number of variables determines how many inputs—also called arguments or parametersa function consumes. Thus, f is a one-argument function, which we also refer to as a unary function. In contrast, g is a two-argument function, also dubbed binary, and h is a ternary or three-argument function. The expression—often referred to as the function bodydetermines the output.

The examples are silly because the expressions inside the functions do not involve the variables. Since variables are about inputs, not mentioning them in the expressions means that the function’s output is independent of their input. We don’t need to write functions or programs if the output is always the same.

Variables aren’t data, but they represent data. For example, a variable definition such as

(define x 3)

says that x always stands for 3. The variables in a function header, i.e., the variables that follow the function name, are placeholders for unknown pieces of data in the function body. Consider the following fragment of a definition:

(define (ff a) ...)

Its function header is (ff a), meaning ff consumes one piece of input, and the variable a is a placeholder for this input. Of course, at the time we define a function, we don’t know what its input(s) will be. Indeed, the whole point of defining a function is that we can use the function many times on many different inputs.

In short, the function bodies of useful functions refer to the function parameters. A reference to a function parameter is really a reference to the piece of data that is the input to the function. If we complete the definition of ff like this
(define (ff a)
  (* 10 a))
we are saying that the output of a function is ten times its input. Presumably this function is going to be supplied with numbers as inputs, because it makes no sense to multiply images or Boolean values or strings with 10.

For now, the only remaining question is how a function obtains its inputs. And to this end, we need to turn to the notion of applying a function.

Applications: A function application puts defined functions to work and it looks just like the applications of a primitive operation:
  • write “(”,

  • write down the name of a defined function f,

  • write down as many arguments as f consumes, separated by some space, and

  • write down “)”.

With this bit of explanation, you can now experiment with functions in the interactions area just as we suggested you experiment with primitives to find out what they compute. The following four experiments, for example, confirm that f from above produces the same value no matter what input it is applied to:
> (f 1)

1

> (f 2)

1

> (f "hello world")

1

> (f true)

1

Evaluate (f (circle 3 "solid" "red")) in the interactions area to show that not even images as inputs change f’s behavior. Here is what happens when the function is applied to too few or too many arguments:
> (f)

procedure f: expects 1 argument, given 0

> (f 1 2 3 4 5)

procedure f: expects 1 argument, given 5: 1 2 3 4 5

It signals an error that is just like those you see when you apply a primitive to the wrong number of arguments:
> (+)

procedure +: expects at least 2 arguments, given 0

Evaluating a function application proceeds in three steps. First, DrRacket determines the values of the argument expressions. Second, it checks that the number of arguments and the number of function parameters (inputs) are the same. If not, it signals an error. Finally, if the number of actual inputs is the number of expected inputs, DrRacket computes the value of the body of the function, with all parameters replaced by the corresponding argument values. The value of this computation is the value of the function application.

Here is a sample calculation for f:
(f (+ 1 1))
=
(f 2)
=
1
It is time to explore the stepper in DrRacket. Enter the definition of ff into the definitions area, followed by the expression (ff (+ 1 1)). Then click the STEPPER. When the stepper window comes up, use the NEXT STEP and watch how the stepper performs the same calculations as we do. In general, the interactions area computes what an expression evaluates to while the stepper displays how this evaluation proceeds. Of course, because x does not occur in the body of f, replacing all occurrences of x with 2 produces just the function body, which is 1. For ff, whose parameter is a and whose body is (* 10 a), we get a very different kind of calculation:
(ff (+ 1 1))
=
(ff 2)
=
(* 10 2)
=
20

Functions don’t have to be applied at the prompt in the interactions area. It is perfectly acceptable to use function applications nested within an expression:
> (+ (ff 3) 2)

32

> (* (ff 4) (+ (ff 3) 2))

1280

Using our rules of computation, these results are predictable. Here is the calculation for the first expression:
(+ (ff 3) 2)
=
(+ (* 10 3) 2)
=
(+ 30 2)
=
32
Naturally, we can reuse the result of this calculation to determine the result of the second expression:
(* (ff 4) (+ (ff 3) 2))
=
(* (* 10 4) (+ (ff 3) 2))
=
(* 40 (+ (ff 3) 2))
=
(* 40 32)
=
1280
In short, programs compute in the same way as students in middle school and high school: via substitution and via arithmetic.

This description also generalizes to functions that process strings, Boolean values, images, and other forms of data. Recall the following “law” of string arithmetic:
(string-append "hello" " " "world")
=
"hello world"
Now suppose we define a function that creates the opening of a letter:
(define (opening first last)
  (string-append "Dear " first ","))
An experiment in the interactions area yields the expected result:
> (opening "Matthew" "Krishnamurthi")

"Dear Matthew,"

Most importantly, though, our laws of evaluating expressions explain why this result is the proper one:
(opening "Matthew" "Krishnamurthi")
=
(string-append "Dear " "Matthew" ",")
=
"Dear Matthew,"

To summarize, this section introduces the notation for function applications—the way of putting functions to work—and the mechanism for evaluating function applications and expressions in general. Our key insight is that substitution of argument values for parameters plus basic laws about primitive operations empower us to predict how a function or an expression compute their results—a critical ability if we wish to understand what our programs really do and not just blindly hope for the best.

Exercise 13: Define a function that consumes two numbers, x and y, and that computes the distance of point (x,y) to the origin. See exercise 1 for ideas.

Exercise 14: Define the function cube-volume, which accepts the length of a side of a cube and computes its volume. If you have time, consider defining cube-surface, too.

Exercise 15: Define the function string-first, which extracts the first character from a non-empty string. Don’t worry about empty strings.

Exercise 16: Define the function string-last, which extracts the last character from a non-empty string. Don’t worry about empty strings.

Exercise 17: Define the function bool-imply. It consumes two Boolean values, call them b1 and b2. The answer of the function is true if b1 is false or b2 is true. Note: Logicians call this imply and often they use the symbol => for this purpose. While BSL could define a function with this name, we avoid the name because it is too close to the comparison operations for numbers <= and >=, and it would thus easily be confused. See exercise 9.

Exercise 18: Define the function image-area, which computes the area of a given image. Note: The area is also the number of pixels in the picture. See exercise 5 for ideas.

Exercise 19: Define the function image-classify, which consumes an image and produces "tall" if the image is taller than it is wide, "wide" if it is wider than it is tall, or "square" if its width and height are the same. See exercise 10 for ideas.

Exercise 20: Define the function string-join, which consumes two strings and appends them with "_" in the middle. See exercise 2 for ideas.

Exercise 21: Define the function string-insert, which consumes a string and a number i and which inserts "_" at the ith position of the string. Assume i is a number between 0 and the length of the given string (inclusive). See exercise 3 for ideas. Also ponder the question whether string-insert should deal with empty strings.

Exercise 22: Define the function string-delete, which consumes a string and a number i and which deletes the ith position from str. Assume i is a number between 0 (inclusive) and the length of the given string (exclusive). See exercise 4 for ideas. Again consider the question whether string-delete can deal with empty strings.

2.2.2 Composing Functions

A program rarely consists of a single function definition and an application of that function. Instead, a typical program consists of a “main” function or a small collection of “main event handlers.” All of these use other functions—built-in primitives as well as functions that you define—and turn the result of one function application into the input for another. In analogy to middle school mathematics, we call this way of defining functions composition, and we call these additional functions auxiliary functions or helper functions.

Consider a program for filling in letter templates. It consumes the first and last name of the addressee, an opening with the letter body, and a closing:
(define (letter fst lst signature-name)
  (string-append
    (opening fst)
    "\n"
    (body fst lst)
    "\n"
    (closing signature-name)))
 
(define (opening fst)
  (string-append "Dear " fst ","))
 
(define (body fst lst)
  (string-append
   "we have discovered that all people with the last name "
   "\n"
   lst " have won our lottery. So, " fst ", "
   "\n"
   "hurry and pick up your prize."))
 
(define (closing signature-name)
  (string-append
   "Sincerely,"
   "\n"
   signature-name))
Now enter these four definitions into the definitions area and click on RUN. You can then evaluate expressions in the interactions area and create small letters:
> (letter "Matthew" "Krishnamurthi" "Felleisen")

"Dear Matthew,\nwe have discovered that all people with the last name \nKrishnamurthi have won our lottery. So, Matthew, hurry \nand pick up your prize.\nSincerely,\nFelleisen"

Note how the result is a long string that contains occurrences of "\n". These substrings represent the newline character within a string. Once the string is written to a file, it is broken into actual lines wherever "\n" occurs.

In general, when a problem refers to distinct tasks of computation, a program should consist of one function per task and a main function that puts it all together. We formulate this idea as a simple slogan:

Define one function per task.

The advantage of following this slogan is that you get reasonably small functions, each of which is easy to comprehend, and whose composition is easy to understand. Later, we see that creating small functions that work correctly is much easier than creating one large function. Better yet, if you ever need to change a part of the program due to some change to the problem statement, it tends to be much easier to find the relevant program parts when it is organized as a collection of small functions.

Here is a small illustration of this point with a sample problem:

Sample Problem: Imagine the owner of a movie theater who has complete freedom in setting ticket prices. The more he charges, the fewer the people who can afford tickets. In a recent experiment the owner determined a precise relationship between the price of a ticket and average attendance. At a price of $5.00 per ticket, 120 people attend a performance. Decreasing the price by a dime ($.10) increases attendance by 15. Unfortunately, the increased attendance also comes at an increased cost. Every performance costs the owner $180. Each attendee costs another four cents ($0.04). The owner would like to know the exact relationship between profit and ticket price so that he can determine the price at which he can make the highest profit.

While the task is clear, how to go about it is not. All we can say at this point is that several quantities depend on each other.

When we are confronted with such a situation, it is best to tease out the various dependencies one at a time:
  1. The problem statement also specifies how the number of attendees depends on the ticket price. Computing this number is clearly a separate task and thus deserves its own function definition:

    (define (attendees ticket-price)
      (+ 120 (* (/ 15 0.1) (- 5.0 ticket-price))))
  2. The revenue is exclusively generated by the sale of tickets, meaning it is exactly the product of ticket price and number of attendees:

    (define (revenue ticket-price)
      (*  (attendees ticket-price) ticket-price))
  3. The costs consist of two parts: a fixed part ($180) and a variable part that depends on the number of attendees. Given that the number of attendees is a function of the ticket price, a function for computing the cost of a show also consumes the price of a ticket and uses it to compute the number of tickets sold with attendees:

    (define (cost ticket-price)
      (+ 180 (* 0.04 (attendees ticket-price))))
  4. Finally, profit is the difference between revenue and costs:

    (define (profit ticket-price)
      (- (revenue ticket-price)
         (cost ticket-price)))

    Even the definition of profit suggests that we use the functions revenue and cost. Hence, the profit function must consume the price of a ticket and hand this number to the two functions it uses.

These four functions are all there is to the computation of the profit, and we can now use the profit function to determine a good ticket price.

Exercise 23: Determine the potential profit for the following ticket prices: $1, $2, $3, $4, and $5. Which price should the owner of the movie theater choose to maximize his profits? Determine the best ticket price down to a dime.

Here is an alternative version of the same program, given as a single function definition:
(define (profit price)
  (- (* (+ 120
           (* (/ 15 0.1)
              (- 5.0 price)))
        price)
     (+ 180
        (* 0.04
           (+ 120
              (* (/ 15 0.1)
                 (- 5.0 price)))))))
Enter this definition into DrRacket and ensure that it produces the same results as the original version for $1, $2, $3, $4, and $5. A single look should suffice to show how much more difficult it is to comprehend this one function compared to the above four.

Exercise 24: After studying the cost structure of a show, the owner discovered several ways of lowering the cost. As a result of his improvements, he no longer has a fixed cost. He now simply pays $1.50 per attendee.

Modify both programs to reflect this change. When the programs are modified, test them again with ticket prices of $3, $4, and $5 and compare the results.

2.2.3 Programs

You are ready to create simple programs. On the surface, a program is just a bunch of function definitions. From a different perspective, namely the one of invoking or launching a program, however, there are at least two distinct kinds of programs:
  • batch programs, which consist of one main function, which uses auxiliary functions, which in turn use additional auxiliary functions, and so on. To launch a batch program means to call the main function on some inputs and to wait for its output.

  • interactive programs, which consists of several main functions, and an expression that informs the computer which of the functions takes care of which input and which of the functions produces output. Naturally, all of these functions may use auxiliary functions.

There are other kinds of programs, and you will get to know those as you continue to study computer science.

In this section we present some simple examples of both batch programs and interactive programs. Before we do so, however, we need one more ingredient: variable definitions.

Global Constants: The prologue demonstrates that good programmers want to define constants in addition to functions. To define a constant means to give a name to a value. In BSL, such a definition has the following shape:
  • write “(define ”,

  • write down the name of the variable,

  • ... followed by a space and an expression,

  • write down “)”.

The name of a constant is a global variable while the definition is called a variable definition. We tend to call the expression in a variable definition (the) right-hand side (of the definition).

As also illustrated in the prologue, constants come in all shapes and forms: numbers, images, strings, and so on. Here are some simple examples,
; temperature (in deg F) when water freezes:
(define FREEZING 32)
 
; useful to compute the area of a disk:
(define ALMOST-PI 3.14)
 
; a blank line:
(define NL "\n")
 
; an empty scene:
(define MT (empty-scene 100 100))
The first two are numeric constants, the last two are strings and images. We use upper-case letters for global constants by convention, because it ensures that no matter how large the program is, the readers of our programs can easily distinguish such variables from others.

All functions in a program may refer to these global variables. A reference to a variable is just like using the corresponding constants. The advantage of using variable names instead of constants is that a single edit of a constant definition affects all uses. For example, we may wish to add digits to ALMOST-PI or enlarge an empty scene:
(define ALMOST-PI 3.14159)
 
; an empty scene:
(define MT (empty-scene 200 800))

Our sample definitions employ literal constants on the right hand side even though a variable definition allows arbitrary expressions. Here is how a programmer can use the extra power. Suppose a program needs to deal with an image of some size and its center:
(define WIDTH 100)
(define HEIGHT 200)
 
(define MID-WIDTH (/ WIDTH 2))
(define MID-HEIGHT (/ HEIGHT 2))
The first two definitions are conventional. The latter two introduce so-called computed constants, i.e., variables whose values are not just literal constants but the results of some calculations.

Batch Programs: As mentioned, a batch program consists of one main function, which performs all the computations. On rare occasions, a program is just this one function. Most of the time, though, the main function employs numerous auxiliary functions, which in turn may also use other functions.

Once programs are created, you want to use or launch them. For a batch program, to launch means to invoke the main function. Recall the letter program from the chapter on Composing Functions. We launched this program once, with the inputs "Matthew", "Krishnamurthi", and "Felleisen". Of course, programs are useful because you can launch them for many different inputs, and this is true for letter, too:
> (letter "Robby" "Flatt" "Felleisen")

"Dear Robby,\nwe have discovered that all people with the last name \nFlatt have won our lottery. So, Robby, hurry \nand pick up your prize.\nSincerely,\nFelleisen"

> (letter "Christopher" "Columbus" "Felleisen")

"Dear Christopher,\nwe have discovered that all people with the last name \nColumbus have won our lottery. So, Christopher, hurry \nand pick up your prize.\nSincerely,\nFelleisen"

> (letter "ZC" "Krishnamurthi" "Felleisen")

"Dear ZC,\nwe have discovered that all people with the last name \nKrishnamurthi have won our lottery. So, ZC, hurry \nand pick up your prize.\nSincerely,\nFelleisen"

Programs are even more useful if you can retrieve the input from some file on your computer and deliver the output to some other file. The name batch program originates from programs in the early days of computing when a program read an entire file and created some other file, without any other intervention.

You can produce some batch programs with the "batch-io" teachpack, which adds two functions to our vocabulary: of task:
  • read-file, which reads the content of an entire file as a string, and

  • write-file, which creates a file from a given string.

When you evaluate these expressions in DrRacket, you need to save the definitions area in a file first, because write-file and read-file work only in the same directory as your program. For example, (write-file "sample.dat" "212") would create a file with the number

212

in it and nothing else, not one extra character. Conversely, evaluating (read-file "sample.dat") immediately afterwards produces the string "212":
> (write-file "sample.dat" "212")

"sample.dat"

> (read-file "sample.dat")

"212"

The result true for the use of write-file is just an acknowledgment that the creation of the file from the string happened. The function produces false if the file exists; furthermore it replaces its content with the given string.

In the context of our letter-writing program, experiment with the following expression in the interactions area:
(write-file "Matthew-Krishnamurthi.txt"
            (letter "Matthew" "Krishnamurthi" "Felleisen"))
Doing so deposits the string that our letter function produces into the file, though with the substring "\n" interpreted as a newline:

Dear Matthew,

we have discovered that all people with the last name

Krishnamurthi have won our lottery. So, Matthew, hurry

and pick up your prize.

Sincerely,

Felleisen

From here it is a short step to a simple letter writing program. Here is a main function:
(define (main fst last signature-name)
  (write-file (string-append fst "-" last ".txt")
              (letter fst last signature-name)))
It uses its first two arguments to create a file name for the write-file function. To create the actual letter it uses all three arguments. You can now use main to write many different letters, each of which is deposited in a separate file.

This first batch program requires users to actually open DrRacket and to apply the function main to three strings. With read-file, we can do even better, namely we can construct batch programs that do not rely on any DrRacket knowledge from their users.

Let us illustrate the idea with a simple program just to see how things work. Suppose we wish to create a program that converts a temperature measured on the Fahrenheit thermometer into a Celsius temperature. Don’t worry, this question isn’t a test about your physics knowledge (though you should know where to find this kind of knowledge); here is the conversion formula:

Naturally in this formula f is the Fahrenheit temperature and c is the Celsius temperature. Translating this into BSL is straightforward:

(define (f2c f)
  (* 5/9 (- f 32)))

Recall that 5/9 is a number, a rational fraction to be precise, and more importantly, that c depends on the given f, which is what the function notation expresses.

Launching this trivial program in the interactions area works as usual:
> (f2c 32)

0

> (f2c 212)

100

> (f2c -40)

-40

But suppose we wish to use this function as part of a program that reads the Fahrenheit temperature from a file, converts this number into a Celsius temperature, and then creates another file that contains the result.

From here, the rest is about composing functions. Here is the main function, which we call convert just to clarify that main functions don’t have to be called main:
(define (convert in out)
  (write-file out
    (number->string
      (f2c
        (string->number (read-file in))))))
Since this convert function composes a large number of “helper” functions, let us step through its body carefully:
  • the function convert consumes two filenames: in for the file where the Fahrenheit temperature is found and out for where we want the Celsius result;

  • (read-file in) retrieves the content of the file called in as a string;

  • string->number turns it into a number;

  • f2c interprets the number as a Fahrenheit temperature and converts it into a Celsius temperature;

  • number->string consumes this Celsius temperature and turns it into a string;

  • which write-file out ... places in the file named out.

Remember, if the file called out already exists, the result of (convert in out) is false; otherwise it is true.

We understand that this long list of explanation might look overwhelming. The average function composition in a pre-algebra course involves two functions, possibly three. In contrast to programs, however, these mathematical exercises are useless and accomplish little. The convert function is the main function of a program and you can use it to accomplish something real. For example,
> (convert "sample.dat" "out.dat")

"out.dat"

You can also create or edit the file "sample.dat" in a file editor. Just be careful that the editor doesn’t add a newline or any other invisible characters. launches the program for the input file named "sample.dat", which we created above, and leaves the result in a file called "out.dat".

(define (convert in out)
  (write-file out
    (number->string
      (f2c
        (string->number (read-file in))))))
 
(define (f2c f)
  (* 5/9 (- f 32)))
 
(convert "sample.dat" "out.dat")

Figure 6: Converting Fahrenheit temperatures into Celsius

In addition to running the batch program, you should also step through the computation. Make sure that the file "sample.dat" exists and contains just a number, then click the STEP button. Doing so opens another window in which you can peruse the computational process that the call to the main function of a batch program triggers. In this case, the process follows the above outline, and it is quite instructive to see this process in action.

With the choice of a menu entry, DrRacket can also produce a so-called executable, a stand-alone program like DrRacket itself. Specifically, choose the entry Create Executable from the Racket menu, and DrRacket will place a package—labeled convert—into the same folder as your program, which you may then distribute to your friends (who use the same kind of computer). They can install this program and they can run it—without knowing anything about DrRacket, as long as they create a file called "sample.dat" in the same folder where they installed the program. After the program is run, they can find the result in the file "out.dat".

Interactive Programs: No matter how you look at it, batch programs are old-fashioned and somewhat boring. Even if businesses have used them for decades to automate useful tasks, interactive programs are what people are used to and prefer over batch programs. Indeed, in this day and age, people mostly interact with programs via a keyboard and a mouse, that is, events such as key presses or mouse clicks. Furthermore, interactive programs can also react to computer-driven events, e.g., the fact that the clock has ticked or that a message has arrived from some other computer.

Launching interactive programs requires more work than launching a batch program. Specifically, an interactive program designates some function as the one that takes care of keyboard events, another function as the one that presents pictures, a third function for dealing with clock ticks, etc. Put differently, there isn’t a main function that is launched; instead there is an expression that tells the computer how to handle interaction events and the evaluation of this expression starts the program, which then computes in response to user events or computer events.

In BSL, the "universe" teachpack provides the mechanisms for specifying connections between the computer’s devices and the functions you have written. The most important mechanism is the big-bang expression. It consists of one required sub-expression, which must evaluate to some piece of data, and a number of optional clauses, which determine which function deals with which event.

The following examples don’t work and need to be revised because big-bang now requires a to-draw clause precisely to avoid this problem.

The simplest big-bang expression is this:
If you evaluate this expression in DrRacket, be sure to have your mouse near the STOP because the evaluation doesn’t stop on its own

Here is another big-bang expression:
(big-bang 100
          (on-tick sub1)
          (stop-when zero?))
It contains two clauses, telling the computer to apply the BSL primitive operation sub1 every time the clock ticks and to check the result with another BSL primitive, zero?. The evaluation of the expression is to stop if this second application produces true. And indeed, when you request the evaluation of this expression in the interactions area, it actually stops and produces 0. Other than that, nothing happens though.

For the third example, we add one clause to the second example:
(big-bang 100
          (on-tick sub1)
          (to-draw render)
          (stop-when zero?))
Like the second example, this expression specifies that sub1 is used for every clock tick and zero? is used to test the result of the application. In addition it says that the result of using sub1 is turned into a scene with render.

Unlike sub1 and zero?, render isn’t a part of BSL, meaning we must define it. To do so, we should at least know to what it is applied. For now it suffices to know that it is applied to a number. Given this much information, one reasonable definition for render is this:
(define (render t)
  (text (number->string t) 12 "red"))
This function converts the given number into a string and then creates an image from this string (with a 12-point, red font).

Copy this definition of render and the third big-bang example into the definitions area of DrRacket Then click RUN, and observe a separate window that counts down from 100 to 0. At that point, the evaluation stops and a 0 appears in the interactions area.

In order to understand the evaluation of big-bang expressions in general, let us look at a schematic one: Beyond on-tick, to-draw, and stop-when clauses, a big-bang expression may also contain on-key and on-mouse clauses. They are obviously associated with the keyboard and the mouse of your computer, and they mostly function just like the on-tick clause. For now we ignore them, but you should know that there is more to big-bang than we discuss here.
(big-bang cw0
          (on-tick tock)
          (to-draw render)
          (stop-when end?))
We assume that the functions tock, render, and end? are either BSL primitive operations or defined functions.

An explanation of this schematic expression must start with is the first, required sub-expression. The value of this first expression is installed as a world, specifically the current world. Furthermore, this big-bang expression tells the computer to apply the function tock to the current world whenever the clock ticks. The result of this application—whatever it is—becomes the next current world, a relationship that the following table concisely summarizes:

clock tick

1

2

3

4

5

...

current world (cw)

cw0

cw1

cw2

cw3

cw4

...

(tock cw)

cw1

cw2

cw3

cw4

cw5

...

Each current world is turned into an image with an application of render and this series of images is displayed in a separate window. Finally, the function end? is used to inspect each current world. If the result is true, the evaluation of the big-bang expression is stopped; otherwise it continues.

Coming up with big-bang expressions for interactive programs demands a different skill, namely, the skill of systematically designing a program. Indeed, you may already feel that these first two chapters are somewhat overwhelming and that they introduced just too many new concepts. To overcome this feeling, the next chapter takes a step back and explains how to design programs from scratch, especially interactive programs.

2.3 How to Design Programs

The first few chapters of this book show that learning to program requires some mastery of many concepts. On the one hand, programming needs some language, a notation for communicating what we wish to compute. The languages for formulating programs are artificial constructions, though acquiring a programming language shares some elements with acquiring a natural language: we need to understand the vocabulary of the programming language; we need to figure out its grammar; and we must know what “phrases” mean.

On the other hand, when we are learning to program, it is critical to learn how to get from a problem statement to a program. We need to determine what is relevant in the problem statement and what we can ignore. We need to understand what the program consumes, what it produces, and how it relates inputs to outputs. We must know, or find out, whether the chosen language and its libraries provide certain basic operations for the data that our program is to process. If not, we might have to develop auxiliary functions that implement these operations. Finally, once we have a program, we must check whether it actually performs the intended computation. And this might reveal all kinds of errors, which we need to be able to understand and fix.

In his book “The Mythical Man-Month” Fred Brooks describes and contrasts these forms of programming on the first pages. In addition to “garage programming” and a “programming product,” he also recognizes “component programming” and “systems programming.” This book is about the “programming products;” our next two books will cover “components” and “systems” design. All this sounds rather complex and you might wonder why we don’t just muddle our way through, experimenting here and there, and leaving it all alone when the results look decent. This approach to programming, often dubbed “garage programming,” is common and succeeds on many occasions; on some it is even the foundation for a start-up company. Nevertheless, the company cannot sell the results of the “garage effort” because they are usable only by the programmers themselves. These programs are like the first two batch programs we wrote in the preceding chapter.

In practice, a good program must come with a short write-up that explains what it does, what inputs it expects, and what it produces. Ideally, it also comes with some assurance that it actually works. Best of all the program should be connected to the problem statement in such a way that a small change to the problem statement is easy to translate into a small change to the program. Software engineers call this a “programming product.”

The word “other” also includes older versions of the programmer who usually cannot recall all the thinking that the younger version put into the production of the program.

All this extra work is necessary because programmers don’t create programs for themselves. Programmers write programs for other programmers to read, and on occasion, people run these programs to get work done. The reason is that most programs are large, complex collections of collaborating functions, and nobody can write all these functions in a day. So programmers join projects, write code, leave projects, and others take over their work. One part of the problem is that the programmer’s customers tend to change their mind about what problem they really want solved. They usually have it almost right, but more often than not, they get some details wrong. Worse, complex logical constructions such as programs almost always suffer from human errors; in short, programmers make mistakes. Eventually someone discovers these errors and programmers must fix them. They need to re-read the programs from a month ago, a year ago, or twenty years ago and change them.

Exercise 25: Research the “year 2000” problem and what it meant for programmers.

In this book, we present a design recipe that integrates a step-by-step process with a way of organizing programs around problem data. For the readers who don’t like to stare at blank screens for a long time, this design recipe offers a way to make progress in a systematic manner. For those of you who teach others to design program, the design recipe is a device for diagnosing a novice’s difficulties. For yet others, the design recipe may just be something that they can apply to other areas, say medicine, journalism, or engineering, because program design isn’t the right choice for their careers. Then again, for those of you who wish to become real programmers, the design recipe also offers a way to understand and work on existing programs—though not all programmers use a method like the design recipe to come up with programs. The rest of this chapter is dedicated to the first baby steps into the world of the design recipe; all of the following chapters refine the design recipe in one way or another.

2.3.1 Designing Functions

Information and Data: The purpose of a program is to describe a computation, a process that leads from collection of information to another. In a sense, a program is like the instruction a mathematics teacher gives to grade school students. Unlike a student, however, a program works with more than numbers; it calculates with navigation information, looks up a person’s address, turns on switches, or processes the state of a video game. All this information comes from a part of the real world—often called the program’s domain in computer science—and the results of a program’s computation are turned into information in this domain.

One insight from this concise description is that information plays a central role. Think of information as facts about the program’s domain. For a program that deals with a furniture catalog, a “table with five legs” or a “square table of two by two meters” are pieces of information. A game program deals with a different kind of domain, where “five” might refer to the number of pixels per clock tick that some objects travels on its way from one part of the screen to another. Or, a payroll program is likely to deal with “five deductions” and similar phrases.

For a program to process information, it must turn it into some form data, i.e., values in the programming language; then it processes the data; and once it is finished, it turns the resulting data into information again. A program may even intermingle these steps, acquiring more information from the world as needed and delivering information in between. You should recall that we apply the adjective “batch” to the plain programs and the others are called “interactive.”

We use BSL and DrRacket so that you do not have to worry about the translation of information into data. In DrRacket’s BSL you can apply a function directly to data and observe what it produces. As a result, we avoid the serious chicken-and-egg problem of writing functions that convert information into data and vice versa. For simple kinds of information designing such program pieces is trivial; for anything other than trivial information, you should know about parsing—the technology of analyzing text and separating it into its grammatical pieces—and that immediately requires (a lot of) expertise in program design.

BSL and DrRacket cleanly separate these tasks so that you can focus on designing the “core” of programs and, when you have enough expertise with that, you can learn to design the rest. Indeed, real software engineers have come up with the same idea and have a fancy name for it, model-view-control (MVC), meaning a program should separate its information processing view from the data processing model. Of course, if you really wish to make your programs process information, you can always use the "batch-io" teachpack to produce complete batch programs or the "universe" teachpack to produce complete interactive programs. As a matter of fact, to give you a sense of how complete programs are designed, this book and even this chapter provide a design recipe for such programs.

Figure 7: From information to data, and back

Given the central role of information and data, program design must clearly start with the connection between them. Specifically, we—the programmers—must decide how to use our chosen programming language to represent the relevant pieces of information as data and how we should interpret data as information. Figure 7 displays this idea abstractly, but let us also make it concrete.

Suppose you are designing a program that consumes information in the form of numbers and that also produces numbers. Then choosing a representation and an interpretation means to understand what a piece of data such as 42 means in the domain:
  • 42 may refer to the number of pixels from the top margin in the domain of images;

  • 42 may denote the number of pixels per clock tick that a simulation or game object moves;

  • 42 may mean a temperature, on the Fahrenheit or Kelvin scale for the domain of physics;

  • 42 may specify the size of some table if the domain of the program is a furniture catalog; or

  • 42 could just count the number of chars a batch program has read.

The key is to know how to go from numbers as information to numbers as data and vice versa.

The word “class” is a popular computer science substitute for the word “set.” In analogy to other set theory mathematics, we also say a value is some element of a class.

Since this knowledge is so important for everyone who reads the program, we often write it down in the form of comments, which we call data definitions. The purpose of a data definition is two-fold. On one hand, it names a class or a collection of data, typically using a meaningful word. On the other hand, it informs readers how to create elements of this class of data and how to decide whether some random piece of data is an element of this collection.

Here are some data definitions for the above examples:
; Distance is a Number.
; interp. the number of pixels from the top margin of a canvas
 
; Speed is a Number.
; interp. the number of pixels moved per clock tick
 
; Temperature is a Number.
; interp. degrees Fahrenheit
 
; Length is a Number.
; interp. the length in centimeters
 
; Count is a Number.
; interp. the number of characters in a string.
Note how a data definition includes comments on how to interpret each piece of data in the chosen class of data. While these comments may appear to be superfluous in these simple cases, they are become critical when data definitions become complex.

At this point, we know only a few forms of data (numbers, strings, images, and Boolean values) and we deal with just those. For now, picking a data representation for your function’s input and output data is as simple as choosing one of those four classes of data, though keep in mind that you need to understand how to go from the domain of information to the world of data and back. Also, starting with the next chapter, this book introduces other forms of data and situations where the design of data representations becomes a complex task.

The Process: Once you understand how to represent input information as data and to interpret output data as information, the design of an individual function proceeds according to a straightforward process:
  1. Write down a signature, a purpose statement, and a function header.

    A function signature (but always shortened to signature here) is a BSL comment that tells the readers of your design how many inputs your function consumes, from what collection of data they are drawn, and what kind of output data it produces. Here are three examples:

    • for a function that consumes one string and produces a number:

    • for a function that consumes a temperature and a Boolean and that produces a string:
      Recall that we have a data definition for Temperature.

    • for a function that consumes a number, a string, and an image and that produces an image:

    A purpose statement is a BSL comment that summarizes the purpose of the function in a single line. If you are ever in doubt about a purpose statement, write down the shortest possible answer to the question

    what does the function compute?

    Every reader of your program should understand what your functions compute without having to read the function itself.

    A multi-function program should also come with a purpose statement. Indeed, good programmers write two purpose statements: one for the reader who may have to modify the code and another one for the person who wishes to use the program but not read it.

    Finally, a header is a simplistic function definition, also called a stub. Pick a parameter per input data class in the signature; the body of the function can be any piece of data from the output class. The following three function headers match the above three signatures:
    Our parameter names somehow reflect what kind of data the parameter represents. In other cases, you may wish to use names that suggest the purpose of the parameter.

    When you formulate a purpose statement, it is often useful to employ the parameter names to clarify what is computed. For example,
    ; Number String Image -> Image
    ; add s to img, y pixels from top, 10 pixels to the left
    (define (add-image y s img)
      (empty-scene 100 100))

    At this point, you can click the RUN button and experiment with the function. Of course, the result is always the same value, which makes these experiments quite boring.

  2. Illustrate the signature and the purpose statement with some functional examples. To construct a functional example, pick one piece of data from each input class from the signature and determine what you expect back.

    Suppose you are designing a function that computes the area of a square. Clearly this function consumes the length of the square’s side and that is best represented with a (positive) number. The first process step should have produced something like this:

    ; Number -> Number
    ; compute the area of a square whose side is len
    (define (area-of-square len) 0)

    Add the examples between the purpose statement and the function header:

    ; Number -> Number
    ; compute the area of a square whose side is len
    ; given: 2, expect: 4
    ; given: 7, expect: 49
    (define (area-of-square len) 0)
  3. The third step is to take inventory, i.e., to understand what are the givens and what we do need to compute. For the simple functions we are considering right now, we know that they are given data via parameters. While parameters are placeholders for values that we don’t know yet, we do know that it is from this unknown data that the function must compute its result. To remind ourselves of this fact, we replace the function’s body with a template.

    For now, the template contains just the parameters, e.g.,
    ; Number -> Number
    ; compute the area of a square whose side is len
    ; given: 2, expect: 4
    ; given: 7, expect: 49
    (define (area-of-square len)
      (... len ...))
    The dots remind you that this isn’t a complete function, but a template, a suggestion for an organization.

    The templates of this section look boring. Later, when we introduce complex forms of data, templates become interesting, too.

  4. It is now time to code. In general, to code means to program, though often in the narrowest possible way, namely, to write executable expressions and function definitions.

    To us, coding means to replace the body of the function with an expression that attempts to compute from the pieces in the template what the purpose statement promises. Here is the complete definition for area-of-square:
    ; Number -> Number
    ; compute the area of a square whose side is len
    ; given: 2, expect: 4
    ; given: 7, expect: 49
    (define (area-of-square len)
      (sqr len))

    To complete the add-image function takes a bit more work than that:
    ; Number String Image -> Image
    ; add s to img, y pixels from top, 10 pixels to the left
    ; given:
    ; 5 for y,
    ; "hello" for s, and
    ; (empty-scene 100 100) for img
    ; expected:
    ; (place-image (text "hello" 10 "red") 10 5 (empty-scene 100 100))
    (define (add-image y s img)
      (place-image (text s 10 "red") 10 y img))
    In particular, the function needs to turn the given string s into an image, which is then placed into the given scene.

  5. The last step of a proper design is to test the function on the examples that you worked out before. For now, click the RUN button and enter function applications that match the examples in the interactions area:
    > (area-of-square 2)

    4

    > (area-of-square 7)

    49

    The results must match the output that you expect; that is, you must inspect each result and make sure it is equal to what is written down in the example portion of the design. If the result doesn’t match the expected output, consider the following three possibilities:
    1. You miscalculated and determined the wrong expected output for some of the examples.

    2. Alternatively, the function definition computes the wrong result. When this is the case, you have a logical error in your program, also known as a bug.

    3. Both the examples and the function definition are wrong.

    When you do encounter a mismatch between expected results and actual values, we recommend that you first re-assure yourself that the expected result is correct. If so, assume that the mistake is in the function definition. Otherwise, fix the example and then run the tests again. If you are still encountering problems, you may have encountered the third, rather rare situation.

2.3.2 Finger Exercises

The first few of the following exercises are almost copies of previous exercise. The difference is that this time they used the word “design” not “define,” meaning you should use the design recipe to create these functions and your solutions should include the relevant pieces. (Skip the template; it is useless here.) Finally, as the title of the section says these exercises are practice exercises that you should solve to internalize the process. Until you internalize the design process, you should never skip a step; it leads to easily-avoided errors and unproductive searches for the causes of errors. There is plenty of room left in programming for complex errors. We have no need to waste our time on silly errors.

Exercise 26: Design the function string-first, which extracts the first character from a non-empty string. Don’t worry about empty strings.

Exercise 27: Design the function string-last, which extracts the last character from a non-empty string.

Exercise 28: Design the function image-area, which computes the area of a given image. Note: The area is also the number of pixels in the picture.

Exercise 29: Design the function string-rest, which produces a string like the given one with the first character removed.

Exercise 30: Design the function string-remove-last, which produces a string like the given one with the last character removed.

2.3.3 Domain Knowledge

It is natural to wonder what knowledge it takes to code up the body of a function. A little bit of reflection should tell you that this step demands a good knowledge about the domain of the program, also known as domain knowledge. Indeed, there are two forms of domain knowledge:
  1. Knowledge from external domains such as mathematics, music, biology, civil engineering, art, etc. Because programmers cannot know all of the application domains of computing, they must be prepared to understand the language of a variety of application areas so that they can discuss problems with domain experts. This language is often that of mathematics, but in some cases, the programmers must create a language as they work through problems with domain experts.

  2. And knowledge about the library functions in the chosen language. When your task is to translate a mathematical formula involving the tangent function, you need to know or guess that your chosen language comes with a function such as BSL’s tan. When, however, you need to use BSL to design image-producing functions, you should understand the possibilities of the "2htdp/image" teachpacks.

Since you can never anticipate in which area you work or with which programming language you must work, it is imperative that you have a solid understanding of the full possibilities of whatever computer languages are around, good, and popular. Otherwise some domain expert with half-baked programming knowledge takes over your job, too.

You can recognize problems that demand domain knowledge from the data definitions that you work out. As long as the data definitions use the data classes that exist in the chosen programming language, the definition of the function body (and program) mostly relies on expertise in the domain. Later, when the book introduces complex forms of data, the design of functions demands deep knowledge in computer science.

2.3.4 From Functions to Programs

Not all programs consist of a single function definition. Some require several functions, for many you also want to use constant definitions. No matter what, it is always important to design each function of a program systematically, though both global constants and the presence of auxiliary functions change the design process a bit.

When you have defined global constants, your functions may use those global constants to compute the results from the given data. In some sense, you should add these global constants to your template, because they belong to the inventory of things that may contribute to the definition. Adding global constants to templates, however, can quickly make those templates look messy. In short, keep global constants in mind when you define functions.

The issue with multi-function programs is complex. On one hand, the design of interactive functions automatically demands the design of several functions. On the other hand, even the design of batch programs may require dealing with several different tasks. Sometimes the problem statement itself suggests different tasks; other times you will discover the need for auxiliary functions as you are in the middle of designing some function.

For all cases, we recommend keeping around a list of “desired functions” or a wish list.The term “wish list” in this context is due to Dr. John Stone. Each entry on a wish list should consist of three things: a meaningful name for the function, a signature, and a purpose statement. For the design of a batch program, put the main function on the wish list and start designing it. For the design of an interactive program, you can put the event handlers, the stop-when function, and the scene-rendering function on the list. As long as the list isn’t empty, pick a wish and design the function. If you discover during the design that you need another function, put it on the list. When the list is empty, you are done.

2.3.5 On Testing

Testing quickly becomes a labor-intensive chore. While it is easy to run tests and discover syntactic errors (clicking the RUN button does this) and run-time errors (the application of a primitive operation to the wrong kind of data), comparing the result of an interaction with the expected result is tiresome. For complex programs, you will tend to write lots of examples and tests and you will have to compare complex (large) values. If you haven’t thought so, you will soon think that this is burdensome and perform sloppy comparisons.

At the same time, testing is a major step to discover flaws in a program. Sloppy testing quickly leads to functions with hidden problems, also known as bugs. Buggy functions then stand in the way of progress on large systems that use these functions, often in multiple ways.

For these reasons—and others to be discussed—it is critical to mechanize tests and not perform them manually in the interactions area. BSL in the DrRacket environment includes a testing facility, as many programming systems do.

To introduce this testing facility, we take a second look at the function that converts temperatures in Fahrenheit to Celsius temperatures from Programs. Here is the definition, reformulated according to the design recipe:
; Number -> Number
; convert Fahrenheit temperatures to Celsius temperatures
; given 32, expected 0
; given 212, expected 100
; given -40, expected -40
(define (f2c f)
  (* 5/9 (- f 32)))
Testing the function’s examples requires three interactions and three comparisons between two numbers each. You can formulate these tests as follows and add them to the definitions area in DrRacket:
(check-expect (f2c -40) -40)
(check-expect (f2c 32) 0)
(check-expect (f2c 212) 100)
When you now click the RUN button, you see a report from BSL that the program passed all three tests—and you had nothing else to do.

In addition to getting tests run automatically, the check-expect forms show another advantage when tests fail. To see how this works, change one of the above tests so that the result is wrong, e.g.,

(check-expect (f2c -40) 40)

When you now click the RUN button, an additional window pops up. The window’s text explains that one of three tests failed. For the failed test, the window displays three pieces: the actual value, i.e., the result of the function call (-40); the expected value (40); and a hyperlink to the text of the failed test case.

; Number -> Number
; convert Fahrenheit temperatures to Celsius temperatures
 
(check-expect (f2c -40) -40)
(check-expect (f2c 32) 0)
(check-expect (f2c 212) 100)
 
(define (f2c f)
  (* 5/9 (- f 32)))

Figure 8: Testing in BSL

You can place check-expect specifications above or below the function definition that they test. When you click RUN, DrRacket collects all check-expect specifications and evaluates them after all function definitions have been added to the “vocabulary” of operations. The above figure shows how to exploit this freedom to combine the example and test step. Instead of writing down the examples as comments, you can translate them directly into tests. When you’re all done with the design of the function, clicking RUN performs the test. And if you ever change the function for some reason—to improve its look or to add a piece of functionality—the next click re-tests the function.

Last but not least, check-expect also works for images. That is, you can and should test image-producing functions. Say you wish to design the function render, which places the image of a car, dubbed CAR, into a background scene, named BACKGROUND. For the design of this function, you may formulate the following tests:
(check-expect (render 50) )
(check-expect (render 200) )
That is, you could use an image editor to create images of the expected results. Alternatively, you could formulate expressions—possibly in the interactions area of DrRacket—that compute the expected results:
(check-expect (render 50) (place-image CAR 50 Y-CAR BACKGROUND))
(check-expect (render 200) (place-image CAR 200 Y-CAR BACKGROUND))
Doing so also helps you figure out how to express the function. Both styles are useful, though in this book we prefer the second one.

Because it is so useful to have DrRacket conduct the tests and not to check everything yourself manually, we immediately switch to this style of testing for the rest of the book. This form of testing is dubbed unit testing though BSL’s unit testing framework is especially tuned for novice programmers. One day you will switch to some other programming language, and one of your first tasks will be to figure out its unit testing framework.

2.3.6 Designing World Programs

The "universe" teachpack supports the construction of some interactive programs. Specifically, you can use the "universe" teachpack to construct so-called world programs, i.e., interactive programs that deal with clock ticks, mouse clicks, and key strokes. In order to interact with people, world programs also create images that DrRacket displays in a graphical canvas.

While the previous chapter introduces the "universe" teachpack in an ad hoc way, this section demonstrates how the design recipe helps you create world programs. The first section provides some basic knowledge about big-bang, the major construct for wiring up world programs. The second section extends this knowledge to deal with mouse clicks and key strokes. Once you have digested this terminology, you are ready to design world programs. The last section is the beginning of a series of exercises, which run through a couple of chapters in this book; take a close look and create your own favorite virtual pet.

Describing Worlds: A raw computer is a nearly useless piece of physical equipment, often called hardware because you can touch it. This equipment becomes useful once you install software, and the first piece of software is usually installed on a computer is an operating system. It has the task of managing the computer for you, including connected devices such as the monitor, the keyboard, the mouse, the speakers, and so on. When you press a key on the keyboard, the operating system runs a function that processes the key stroke. We say that the key stroke is an key event, and the function is an event handler. Similarly, when the clock ticks, the operating system runs an event handler for clock ticks and, when you perform some action with the mouse, the operating system launches the event handler for mouse clicks.

Naturally, different programs have different needs. One program may interpret key strokes as signals to control a nuclear reactor; another passes them to a word processor. To make one and the same computer work on these radically different tasks, programs install event handlers. That is, they tell the operating system to use certain functions for dealing with clock ticks and other functions for dealing with mouse clicks. If a program doesn’t need to handle key strokes, the program says nothing about key events and the operating system ignores them.

DrRacket is a small operating system and the big-bang form from the "universe" teachpack is your means to install event handlers. Specifically, the various clauses in a big-bang expression inform DrRacket which function should deal with which kind of event. Take this expression, for example:
(big-bang w0
          (on-tick cth)
          (on-key keh)
          (on-mouse meh)
          ...)
The three clauses inform DrRacket that
  • the function cth mentioned in the on-tick clause is for handling clock ticks;

  • keh, specified in the on-key clause, deals with events triggered by key strokes; and

  • meh is for processing mouse events.

Once the big-bang expression is evaluated, DrRacket acts just like a real operating system, watching the clock, the key board, and the mouse for events and dispatching to your designated BSL functions.

The key question is what arguments DrRacket supplies to your event handlers for key strokes, mouse clicks, and clock ticks and what kind of results it expects from these event handlers. Like a real operating system, DrRacket gives these functions access to the current state of the world. For key events and mouse events, DrRacket also supplies information about these events.

The initial state is the value of w0, because our big-bang expression says so. It also is the state that DrRacket hands to the first event handling function that it uses. DrRacket expects that this event handling function produces a new state of the world, and DrRacket keeps this result around until the second event happens. Here is a table that describes this relationship among worlds and event handling functions:

event:

big-bang

e1

e2

e3

e4

current world:

w0

w1

w2

w3

w4

clock tick:

(cth w0)

(cth w1)

(cth w2)

(cth w3)

(cth w4)

key stroke:

(keh w0 ...)

(keh w1 ...)

(keh w2 ...)

(keh w3 ...)

(keh w4 ...)

mouse click:

(meh w0 ...)

(meh w1 ...)

(meh w2 ...)

(meh w3 ...)

(meh w4 ...)

Although you might imagine that two events happen at the same time, DrRacket arranges all events in a linear sequence. The first row enumerates the events since DrRacket started evaluating the big-bang expression, while the second row presents the current world—a result of processing the event. The big-bang column thus says that w0 becomes the current world as a result of a big-bang event. Each of the remaining rows tabulates what the functions cth, keh, and meh produce when applied to the current world. Only one of these results is chosen, however, as the current world—depending on which event actually takes place.

So suppose e1 is the key stroke "a", e2 and e3 are clock ticks, and e3 is a mouse event. Then
  1. w1 is the result of (keh w0 "a"), i.e., the fourth cell in the e1 column;

  2. w2 is the result of (cth w1), i.e., the third cell in the e2 column;

  3. w3 is (cth w2), i.e., again the third cell in the e3 column; and

  4. (meh w3 90 100 "button-down") produces w4 assuming e4 is the mouse event “button down” taking place at the position (90,100).

Because event handlers are just function, you can also compute w4 via function composition:

w4 == (meh (cth (cth (keh w0 "a"))) 90 100 "button-down")

Composing event handlers is truly important when it comes to testing world programs. On occasion, programmers introduce mistakes into their creations that are only revealed during the composition of several components. At that point, it is convenient when we can simply compose event handlers and predict their outcome.

In short, the sequence of events determines in which order you traverse the above tables of possible worlds to arrive at the one and only one current world for each time slot. Note that DrRacket does not touch the current world; it merely safeguards it and passes it to event handling functions when needed.

Designing Worlds: Now that you understand how big-bang works, you can focus on the truly important problem of designing world programs. As you might guess, the design starts with the data definition for the states of the “world.” To this end we assume that you have a problem statement and that you are able to imagine what the world program may display in various situations.

Let us make this concrete with a sample problem:

Sample Problem: Design a program that moves a car across the world canvas, from left to right, at the rate of three pixels per clock tick.

For this kind of problem statement, it is indeed easy to imagine a series of images that the program would display:

Pay special attention as to what changes from image to image.

Assuming you have read the problem statement carefully, and that you understand how the world changes and what stays the same, the design of the world program proceeds in three big steps:
  1. For all those properties of the world that remain the same, introduce constant definitions. In BSL, we capture constants via global variable definitions. For the purpose of designing worlds, we distinguish between two kinds of constants:
    1. “physical” constants, which describe general attributes of objects in the domain, such as the speed or velocity of an object, its color, its height, its width, its radius, etc. Of course these constants don’t really refer to physical facts, but many are analogous to physical aspects of the real world.

      In the context of the car animation from the previous section, the WHEEL-RADIUS constant is a “physical” constant, and its definition may look like this:
      (define WHEEL-RADIUS 5)
      (define WHEEL-DISTANCE (* WHEEL-RADIUS 5))
      (define BODY-LENGTH (+ WHEEL-DISTANCE (* 6 WHEEL-RADIUS)))
      (define BODY-HEIGHT (* WHEEL-RADIUS 2))
      Note how some constants are computed from others.

    2. graphical constants, which are images that the program uses to create the scenes that appear on the canvas.

      Here are some graphical constant definitions:
      (define WHL (circle WHEEL-RADIUS "solid" "black"))
      (define BDY
        (above
          (rectangle (/ BODY-LENGTH 2) (/ BODY-HEIGHT 2)
                     "solid" "red")
          (rectangle BODY-LENGTH BODY-HEIGHT "solid" "red")))
      (define SPC (rectangle WHEEL-DISTANCE 1 "solid" "white"))
      (define WH* (beside WHL SPC WHL))
      (define CAR (underlay/xy BDY WHEEL-RADIUS BODY-HEIGHT WH*))
      Graphical constants are usually computed, and the computations tend to involve the physical constants. To create good looking images, you need to experiment. But, keep in mind that good images are not important to understand this book; if you have fun creating them, feel free to spend time on the task. We are happy with simple images.

  2. Those properties that change over time or in reaction to other events make up the current state of the world. Your task is to render the possible states of the world, i.e., to develop a data definition that describes all possible states of the world. As before, you must equip this data definition with a comment that tells readers how to represent world information as data and how to interpret data as information in the world.

    For the running example of an animated car, it should be obvious that the only thing that changes is its distance to the left (or right) border of the canvas. A distance is measured in numbers, meaning the following is an adequate data definition for this example.

    ; CarState is a Number
    ; the number of pixels between the left border and the car
    An alternative is of course to count the number of clock ticks that have passed and to use this number as the state of the world. We leave this design variant to an exercise.

  3. Once you have a data representation for the state of the world, you need to decide which kind of interactions you wish to use for which kind of transitions from one world state to another. Depending on what you decide you need to design several or all of the following four functions:

    1. if your world should react to clock ticks:

    2. if your world should react to key strokes:

    3. if your world should react to mouse clicks:

    4. if you want the world to be rendered to the canvas:

  4. Last but not least, you need to define a main function that puts it all together. Unlike all other functions, a main function for world programs doesn’t demand design. As a matter of fact, it doesn’t require testing. Its sole reason for existing is that you can run your world program conveniently once all tests for the event handling functions are completed.

    Here is our proposed main function for the sample problem:
    ; main : CarState -> CarState
    ; launch the program from some initial state
    (define (main ws)
       (big-bang ws (on-tick tock) (to-draw render)))
    The definition assumes that you have named the clock tick handler tock and the draw function render.

    In other words, the desire to design an interactive program dictates several initial entries for your wish list. Later we introduce additional entries so that you can also design world programs that deal with key presses and mouse events. After you have designed the event handling functions, you can launch an interactive program with a big-bang expression:

    (main SomeCarState)

When the evaluation of the big-bang expression is terminated (for now, close the world canvas), it returns the current state of the world.

A note on names: Naturally, you don’t have to use the name “CarState” for the class of data that represents the states of the world; any name will do as long as you use it consistently for the signatures of the big-bang functions. Also, you don’t have to use the names tock, render, or end?; you can name these functions whatever you like, as long as you use the same names when you write down the clauses of the big-bang expression.

A note on design: Even after settling on the data definition, a careful programmer shouldn’t be completely happy. The image of the car (and a car itself) isn’t just a mathematical point without width and height. Thus, to write “the number of pixels from the left margin” is an ambiguous interpretation statement. Does this statement measure the distance between the left margin and the left end of the car? Its center point? Or even its right end? While this kind of reflection may seem far-fetched, it becomes highly relevant and on occasion life-critical in some programming domains. We ignore this issue for now and leave it to BSL’s image primitives to make the decision for us.

Good programs establish a single point of control for all aspects, not just the graphical constants. Several chapters deal with this issue.

Exercise 31: Good programmers ensure that an image such as CAR can be enlarged or reduced via a single change to a constant definition. We started the development of our car image with a single plain definition:

(define WHEEL-RADIUS 5)

All other dimensions of the car and its pieces are based on the wheel’s radius. Changing WHEEL-RADIUS from 5 to 10 “doubles” the size of the car image, and setting it to 3 reduces it. This kind of program organization is dubbed single point of control, and good design employs single point of control as much as possible.

Now develop your favorite image of a car; name the image CAR. Remember to experiment and make sure you can re-size the image easily.

The rest of this section demonstrates how to apply the third design step to our sample problem. Since the car is supposed to move continuously across the canvas, and since the problem statement doesn’t mention any other action, we have two functions on our wish list: tock for dealing with a clock tick and render for creating an image from the state of the world.

As mentioned in the design recipe, the signature for the clock-tick handler is dictated by what we named the class of data that represents the state of the world. It does need a purpose statement and a header:
; CarState -> CarState
; the clock ticked; move the car by three pixels
(define (tock ws) ws)
Since the state of the world represents the distance between the left margin of the canvas and the car, and since the car moves at three pixels per clock tick, a concise purpose statement combines these two facts into one. This makes it also easy to create examples and to define the function:
; CarState -> CarState
; the clock ticked; move the car by three pixels
; example:
; given: 20, expected 23
; given: 78, expected 81
(define (tock ws) (+ ws 3))

The last design step calls for tests to confirm that the examples work as expected. So we click the RUN button and test:
> (tock 20)

23

> (tock 78)

81

Since the results are as expected, the design of tock is finished.

Our second entry on the wish list specifies a function that translates the state of the world into a scene:
; CarState -> Image
; place the car into a scene, according to the given world state
(define (render ws) (empty-scene 300 50))
As for tock, the signature is dictated by the choice of CarState in our data definition. The purpose statement tells us what the function produces from the given world state, i.e., a scene with a car. The function header is obviously not computing what the purpose statement demands, but this is acceptable for a header.

To make examples for a rendering function, we suggest arranging a table like this:

ws =

50

ws =

100

ws =

150

ws =

200

It lists the given world states in the first column, and in the second column you see the desired scenes. For your first few rendering functions, you may just wish to draw this images by hand.

Even though this kind of image table is intuitive and explains what the running function is going to display—a moving car—it does not explain how the function creates this result. To get from here to there, we recommend writing down expressions that create the images in a third column of the table:

ws =

50

(place-image
  CAR
  50 Y-CAR
  BACKGROUND)

ws =

100

(place-image
  CAR
  100 Y-CAR
  BACKGROUND)

ws =

150

(place-image
  CAR
  150 Y-CAR
  BACKGROUND)

ws =

200

(place-image
  CAR
  200 Y-CAR
  BACKGROUND)
The capitalized names refer to the obvious constants: the image of a car, its fixed y coordinate, and the background scene, which is probably empty.

This extended table suggests a pattern for the formula that goes into the body of the render function:
; CarState -> Image
; place the car into a scene, according to the given world state
(define (render ws)
  (place-image CAR ws Y-CAR BACKGROUND))
All you have to do from here is create the car image, an image (start with an empty scene), and determine at which y coordinate you want to place it.

And then, you just need to test, which means evaluating expressions such as (render 50), (render 100), (render 150), and (render 200) and making sure that the resulting image is what you want. Naturally, this is somewhat more difficult than checking that a number is what you want. For now, we and you need to rely on your eyes, which is why doing so is called an eyeball test. In the penultimate subsection, we return to the testing issue in general and the one for images in particular.

Exercise 32: Finish the sample exercise and get the program to run. That is, assuming that you have solved the exercise of creating the image of a car, define the constants Y-CAR and BACKGROUND. Then assemble the appropriate big-bang expression. When your program runs to your satisfaction, add a tree to scenery. We used
(define tree
  (underlay/xy (circle 10 'solid 'green)
               9 15
               (rectangle 2 20 'solid 'brown)))
to create a tree-like shape. Also add a clause to the big-bang expression that stops the animation when the car has disappeared on the right side of the canvas.

Exercise 33: Start with the following data definition:
; AnimationState is a Number
; interp. the number of clock ticks since the animation started
Like the original data definition, this one also equates the states of the world with the class of numbers. Its interpretation, however, explains that the number means something entirely different.

Design functions tock and render and develop a big-bang expression so that you get once again an animation of a car traveling from left to right across the world’s canvas.

How do you think this program relates to the animate function from Prologue: How to Program?

Exercise 34: Use the data definition from the preceding exercise to design a program that moves a red dot according to a sine wave.

Of Mice and Characters: Before you design world programs that deal with key strokes and mouse events, it is a good idea to practice with small, nearly trivial examples to understand what the event handlers can and cannot compute. We start with a simple problem concerning key strokes:

Sample Problem: Design a program that keeps track of all key strokes. The program should display the accumulated key strokes as a red text in the 11 point font.

The problem looks easy and suggests a relative straightforward data definition for representing the state of the world:
; AllKeys is a String.
; interp. the sequence of keys pressed since
; big-bang created the canvas
Just to emphasize that names don’t matter when used consistently, we don’t use WorldState this time. The second sentence of the problem statement also spells out some physical and graphical constants:
; physical constants:
(define WIDTH 100)
(define HEIGHT 50)
 
; graphical constant:
(define MT (empty-scene WIDTH HEIGHT))

From the data definition and the desire to deal with key events, we get a wish list with two functions:
  • the function remember to manage key strokes:
    ; AllKeys String -> AllKeys
    ; add ke to ak, the state of the world
    (define (remember ak ke) ak)

  • the function show to display the current state of the world:
    ; AllKeys -> Image
    ; render the string as a text and place it into MT
    (define (show ak) MT)

Here we spelled out a complete purpose, signature, and header for each function.

Next we need to pick one of the two wishes and finish it. We choose remember and move to example-tests:
; AllKeys String -> AllKeys
; add ke to ak, the state of the world
 
(check-expect (remember "hello" " ") "hello ")
(check-expect (remember "hello " "w") "hello w")
 
(define (remember ak ke) ak)
One interpretation for the examples is that some user has entered the word "hello" so far and next presses the space bar, which is represented as the string " "; furthermore, if the state of the world is ever "hello " and the user presses the “w” key, the next state of the world should be "hello w". Both example-tests say that the state of the world represents the key strokes seen so far as a string, when read from left to right; the next key stroke is added at the end. Thus, the examples suggest the obvious function definition:
; AllKeys String -> AllKeys
; add ke to ak, the state of the world
 
(check-expect (remember "hello" " ") "hello ")
(check-expect (remember "hello " "w") "hello w")
 
(define (remember ak ke)
  (string-append ak ke))
Copy the code to DrRacket, click on RUN, and thus confirm that the example-tests work fine.

The design of the remaining function, render, is equally straightforward, mostly because the problem almost dictates the examples:
; AllKeys -> Image
; render the string as a text and place it into MT
 
(check-expect
 (show "hello") (place-image (text "hello" 11 "red") 10 20 MT))
(check-expect
 (show "mark") (place-image (text "mark" 11 "red") 10 20 MT))
 
(define (show ak)
  (place-image (text ak 11 "red") 10 20 MT))
The key to the example is that by writing down expressions that construct the desired output, we can easily guess what the function should look like. Notice, though, the whimsically chosen positions of the text; nothing in the problem statement dictates this. We simply pick something and stick to it. If this were a for-money project, you would ask your customer of course if the program works as imagined before you move on to add other features.

As we have said before, tests exist to reveal flaws; they can’t show that the program truly works. For that, you must run the program and experiment, and eventually you must allow the expected user to play with intermediate designs. So here is how you launch this interactive program:

(big-bang "" (on-key remember) (to-draw show))

This opens the world canvas, enabling you to press keys and see them reflected there:

Even though it isn’t immediately obvious, we choose to press the “h” key. The first image shows the empty canvas, the second one reflects the key event.

Exercise 35: Key event handlers are also applied to strings such as "\t" (the tab key) and "\r". Appearances are deceiving, however. These strings consists of a single character and remember therefore adds them to the end of the current world state. Read the documentation of on-key and change remember so that it also ignores all special one-character strings.

Let us look at a program that interacts with the mouse. The figure below displays the simplest such program, i.e., an interactive program that just records where the mouse events occur via small dots. It is acceptable to break the rule of separating data representations and image rendering for such experimental programs, whose purpose it is to determine how newly introduced things work. It ignores what kind of mouse event occurs, and it also ignore the first guideline about the separation of state representation and its image. Instead the program uses images as the state of the world. Specifically, the state of the world is an image that contains red dots where a mouse event occurred. When another event is signaled, the clack function just paints another dot into the current state of the world.

; AllMouseEvts is an element of Image.
 
; graphical constants
(define MT (empty-scene 100 100))
 
; clack : AllMouseEvts Number Number String -> AllMouseEvts
; add a dot at (x,y) to ws
 
(check-expect
 (clack MT 10 20 "something mousy")
 (place-image (circle 1 "solid" "red") 10 20 MT))
 
(check-expect
 (clack (place-image (circle 1 "solid" "red") 1 2 MT) 3 3 "")
 (place-image (circle 1 "solid" "red") 3 3
              (place-image (circle 1 "solid" "red") 1 2 MT)))
 
(define (clack ws x y action)
  (place-image (circle 1 "solid" "red") x y ws))
 
; show : AllMouseEvts -> AllMouseEvts
; just reveal the current world state
 
(check-expect (show MT) MT)
 
(define (show ws)
  ws)

Figure 9: A mouse event recorder

To play with this program, copy it into the definitions area of DrRacket and click RUN. In the interactions area, evaluate

(big-bang (empty-scene 100 100) (to-draw show) (on-mouse clack))

This opens the world’s canvas and allows you to move the mouse and to record its movement. Doing so produces a canvas that looks roughly like this:

What this picture reveals is that a computer and its operating system do not track every single move of your mouse. Instead they sample mouse events sufficiently often and tell your program about those sample events. Usually these samples are sufficient.

Normal interactive programs don’t ignore the kind of mouse events that takes place. Just like the key event tracker above, they inspect the string and compute different results, depending on what kind of string they received. Designing such programs requires a bit more knowledge about BSL and a bit more insight into design than we have presented so far. And the next chapter introduces all this.

2.3.7 Virtual Pet Worlds

The purpose of this exercise section is to create the first two elements of a virtual pet game. It starts with just a display of a cat that keeps walking across the screen. Of course, all the walking makes the cat unhappy and its unhappiness shows. Like all pets, you can try petting, which helps some, or you can try feeding, which helps a lot more.

So let’s start with an image of our favorite cat:

Copy the cat image and paste it into DrRacket, then give the image a name with define.

Exercise 36: Design a “virtual cat” world program that continuously moves the cat from left to right, by three pixels at a time. Whenever the cat disappears on the right it should re-appear on the left.

Exercise 37: Improve the cat animation with a second, slightly different image:

Adjust the rendering function so that it uses one cat image or the other based on whether x coordinate is odd. Read up on odd? in the help desk.

Exercise 38: Our virtual pet game will need a gauge to show how happy the cat is. If you ignore the cat, it becomes less happy. If you pet the cat, it becomes happier. If you feed the cat, it becomes much, much happier. We feed the cat by pressing the down arrow key, and we pet it by pressing the up arrow key.

This program is separate from the cat world program in the first two exercises. Do not integrate this program with the cat program of the previous exercise; you don’t know enough yet. If you think you want to make the cat and the happiness gauge play together read the next section. Design a world program that maintains and displays a “happiness gauge” over time. With each clock tick, happiness decreases by -0.1, starting with 100, the maximum score; it never falls below 0, which is the minimum happiness score.

To show the level of happiness, we use a scene with a solid, red rectangle with a black frame. For a happiness level of 0, the red bar should be gone; for a happiness level of 100, the bar should go all the way across the scene.

2.4 Intervals, Enumerations, etc.

Thus far you have four choices for data representation: numbers, strings, images, and Boolean values. For many problems this is enough, but there are many more for which these four collections of data in BSL (or different ones in different programming languages) don’t suffice. Put differently, programming with just the built-in collections of data is often clumsy and therefore error prone.

At a minimum, good programmers must learn to design programs with restrictions of these built-in collections. One way to restrict is to enumerate a bunch of elements from a collection and to say that these are the only ones that are going to be used for some problem. Enumerating elements works only when there is a finite number of them. To accommodate collections with “infinitely” many elements, we introduce intervals, which are collections of elements that satisfy a specific property.

Infinite may just mean “so large that enumerating the elements is entirely impractical.”

Defining enumerations and intervals means distinguishing among different kinds of elements. To distinguish in code requires conditional functions, i.e., function that choose different ways of computing results depending on the value of some argument. Both Many Ways to Compute and Mixing It Up with Booleans illustrate with examples how to write such functions. In both cases, however, there is no design system; all you have is some new construct in your favorite programming language (that’s BSL) and some examples on how to use it.

In this chapter, we introduce enumerations and intervals and discuss a general design strategy for these forms of input data. We start with a second look at the cond expression. Then we go through three different scenarios of distinct subclasses of data: enumerations, intervals, and itemizations, which mix the first two. The chapter ends with a section on the general design strategy for such situations.

2.4.1 Conditional Computations

Recall the extremely brief introduction to conditional expressions in Prologue: How to Program. Since the cond expression is by far the most complex expression form this book uses, let us take a close second look at the general shape:
(cond
  [ConditionExpression1 ResultExpression1]
  [ConditionExpression2 ResultExpression2]
  ....
  [ConditionexpressionN ResultExpressionN])
Like all expressions, a cond expression starts with ( and ends with ). Like all expressions except for function application, a cond is marked with a keyword, in this case of course the word cond. Following the keyword, a programmer writes as many cond-lines as needed; each cond line consists of two expressions, enclosed in opening and closing brackets: [ and ]. The use of brackets isn’t necessary; it is a convention to make cond-lines stand out. Put differently, it is perfectly acceptable to use pairs of [ and ] in place of pairs of ( and ) and vice versa.

Here is a function definition that uses a conditional expression:
(define (next traffic-light-state)
  (cond
    [(string=? "red" traffic-light-state) "green"]
    [(string=? "green" traffic-light-state) "yellow"]
    [(string=? "yellow" traffic-light-state) "red"]))
Like the mathematical example in Prologue: How to Program, this example illustrates the convenience of using cond expressions. In many problem contexts, a function must distinguish several different situations. With a cond expression, you can use one line per possibility and thus remind the reader of the code of the different situations from the problem statement.

A note on pragmatics: Contrast cond expressions with if expressions from Mixing It Up with Booleans. The latter distinguish one situation from all others. As such, if expressions are just much less suited for multi-situation contexts; they are best used when all we wish to say is "one or the other." We therefore always use cond for situations when we wish to remind the reader of our code that some distinct situations come directly from data definitions, i.e., our first analysis of problem statements. For other pieces of code, we use whatever construct is most convenient.

When the conditions get too complex in a cond expression, you occasionally wish to say something like "in all other cases." For these kinds of problems, cond expressions permit the use of the else keyword for the very last cond line:
(cond
  [ConditionExpression1 ResultExpression1]
  [ConditionExpression2 ResultExpression2]
  ....
  [else DefaultResultExpression])
If you make the mistake of using else in some other cond line, BSL in DrRacket signals a mistake:
> (cond
    [(> x 0) 10]
    [else 20]
    [(< x 10) 30])

cond: found an else clause that isn't the last clause in

its cond expression

Just like in the real world, BSL respects grammatical rules above anything else. If the grammar isn’t correct, it makes no sense to figure out what an expression means.

Imagine designing a function that, as part of a game-playing program, computes some good-bye sentence at the end of the game. You might come up with a definition like this one:

; PositiveNumber is a Number greater or equal to 0.
 
; PositiveNumber -> String
; compute the reward level from the given score s
(define (reward s)
  (cond
    [(<= 0 s 10)
     "bronze"]
    [(and (< 10 s) (<= s 20))
     "silver"]
    [(< 20 s)
     "gold"]))
(define (reward s)
  (cond
    [(<= 0 s 10)
     "bronze"]
    [(and (< 10 s) (<= s 20))
     "silver"]
    [else
     "gold"]))
To formulate the last condition for the function on the left, you must "calculate" that
together mean

(< 20 s)

While the calculation looks simple in this case, it is easy to make small mistakes and to introduce bugs into your program. It is therefore better to formulate the function definition as shown on the right, if you know that you want the exact opposite—called a complementof all previous conditions in a cond.

2.4.2 How It Works

From reading the Many Ways to Compute and Mixing It Up with Booleans, you roughly know how DrRacket evaluates conditional expressions. Let us recapitulate the idea a bit more precisely for cond expression. Re-consider this definition
(define (reward s)
  (cond
    [(<= 0 s 10) "bronze"]
    [(and (< 10 s) (<= s 20)) "silver"]
    [else "gold"]))
The function reward consumes a numeric score (positive number) and produces a color string.

If you just look at the cond expression it is impossible to know which of the three cond clauses is going to be used. And that is the point of a function of course. The function deals with many different inputs here: 2, 3, 7, 18, 29, etc. For each of these inputs, it may have to proceed in a different manner. Differentiating the different classes of inputs is the purpose of the cond expression.

Take for example

(reward 3)

You know that DrRacket replaces function applications with the function’s body after substituting the argument for the parameter:
(cond
  [(<= 0 3 10) "bronze"]
  [(and (< 10 3) (<= 3 20)) "silver"]
  [else "gold"])
For a cond expression, DrRacket evaluates one condition at a time. For the first one that evaluates to true it replaces the cond with the result expression:
(cond
  [(<= 0 3 10) "bronze"]
  [(and (< 10 3) (<= 3 20)) "silver"]
  [else "gold"])
= (cond
  [true "bronze"]
  [(and (< 10 3) (<= 3 20)) "silver"]
  [else "gold"])
= "bronze"
In this particular example, the first condition—which the substitution turned into an ordinary expression in the arithmetic of Boolean values—evaluates to true because 0 is less than 3 and 3 is less than 10.

Here is a second example:
(reward 21)
= (cond
    [(<= 0 21 10) "bronze"]
    [(and (< 10 21) (<= 21 20)) "silver"]
    [else "gold"])
= (cond
    [false "bronze"]
    [(and (< 10 21) (<= 21 20)) "silver"]
    [else "gold"])
= (cond
    [(and (< 10 21) (<= 21 20)) "silver"]
    [else "gold"])
Note how the first condition evaluated to false now, and as mentioned in Many Ways to Compute the entire cond clause is dropped. The rest of the calculation proceeds as expected:
...
= (cond
    [(and (< 10 21) (<= 21 20)) "silver"]
    [else "gold"])
= (cond
    [(and true (<= 21 20)) "silver"]
    [else "gold"])
= (cond
    [(and true false) "silver"]
    [else "gold"])
= (cond
    [false "silver"]
    [else "gold"])
= (cond
    [else "gold"])
= "gold"
Like the first condition, the second one also evaluates to false, though in many steps, and so the calculation proceeds to the third cond line. The else tells DrRacket to just replace the entire cond expression with the result expression from this clause.

Exercise 39: Enter the definition of reward and the application (reward 18) into the Definitions area of DrRacket and use the stepper to find out how DrRacket evaluates applications of the function.

2.4.3 Enumerations

Not all strings represent mouse events. If you looked in HelpDesk when the last section introduced the on-mouse clause for big-bang, you found out that only six strings are used to notify programs of mouse events:
; A MouseEvt is one of these strings:
;  "button-down"
;  "button-up"
;  "drag"
;  "move"
;  "enter"
;  "leave"
The interpretation of these strings is quite obvious. One of the first two strings shows up when the computer user clicks the mouse button or releases it. In contrast, the third and fourth are about moving the mouse and possibly holding down the mouse button at the same time. Finally, the last two strings represent the events of a mouse moving over the edge of the canvas, either going into the canvas from the outside or vice versa.

More importantly, the data definition for representing mouse events as strings looks quite different from the data definitions we have seen so far. It is called an enumeration. It should not come as a surprise either that enumerations are common. Here is a simple one:
; A TrafficLight shows one of three colors:
;  "red"
;  "green"
;  "yellow"
; interp. each element of TrafficLight represents which colored
; bulb is currently turned on
We call it simplistic because it does not include the "off" state, the "blinking red" state, or the "blinking yellow" state. It is a simplistic representation of the states that a traffic light can take on. Unlike others, this data definition also uses a slightly different phrase to explain what a term (TrafficLight) means but this is an inessential difference.

Programming with enumerations is mostly straightforward. When a function’s input is a class of data whose description spells out its elements on a case-by-case basis, the function should distinguish just those cases and compute the result on a per-cases basis. For example, if you wanted to define a function that computes the next state of a traffic light, given the current state as an element of TrafficLight, you would come up with a definition like this one:
; TrafficLight -> TrafficLight
; given state s, determine the next state of the traffic light
 
(check-expect (traffic-light-next "red") "green")
 
(define (traffic-light-next s)
  (cond
    [(string=? "red" s) "green"]
    [(string=? "green" s) "yellow"]
    [(string=? "yellow" s) "red"]))
Because the data definition for TrafficLight consists of three distinct elements, the traffic-light-next function naturally distinguishes between three different cases. For each case, the result expression is just another string, the one that corresponds to the next case.

Exercise 40: If you copy and paste the above function definition into the definitions area of DrRacket and click RUN, DrRacket highlights two of the three cond lines. This coloring tells you that your test cases do not cover all possible cases. Add enough cases to make DrRacket happy.

Exercise 41: Design a function that renders the state of a traffic light as a solid circle of the appropriate color. When you have tested this function sufficiently, enter a big-bang expression that displays your traffic light and that changes its state on every clock tick.

The main ingredient of an enumeration is that it defines a collection of data as one of a number of pieces of data. Each item of the sequence explicitly spells out which piece of data belongs to the class of data that we name. Usually, the piece of data is just shown as is; on some occasions, the item of an enumeration is an English sentence that describes a finite number of elements of pieces of data with a single phrase.

Here is an important example:
; A 1String is a string of length 1,
; including " " (the space bar), "\t" (tab),
; "\r" (return), and "\b" (backspace).
You know that such a data definition is proper if you can describe all of its elements with a BSL test. In the case of 1String, you can find out whether some string s belongs to the collection with

(= (string-length s) 1)

An alternative way to check that you have succeeded is to enumerate all the members of the collection of data that you wish to describe:
; A 1String is one of:
;  "q"
;  "w"
;  "e"
;  "r"
;  "t"
;  "y"
; ...
In this case you can see that it suffices to go along the key board and to express all keys as strings.

You know where to find the rest of this data definition. The data definition for representing key strokes is a realistic example that employs this same technique. Here is an excerpt:
; A KeyEvent is one of:
;  a single-character string, i.e., a string of length 1
;  "left"
;  "right"
;  "up"
;  "down"
;  ...
The first item in this enumeration describes the same bunch of strings that 1String describes. The clauses that follow enumerate strings for special key events, such as pressing one of the four arrow keys or releasing a key.

If you were asked to design an interactive program with a function for key events, your function should have the following outline:
; TrafficLight KeyEvent -> ...
(define (handle-key-events w ke)
  (cond
    [(= (string-length ke) 1) ...]
    [(string=? "left" ke) ...]
    [(string=? "right" ke) ...]
    [(string=? "up" ke) ...]
    [(string=? "down" ke) ...]
    ...))
Again the body of your function consists of a cond expression and for each line in the enumeration of the data definition, your function contains one cond line. The condition in the first cond line identifies the KeyEvents identified in the first clause of the enumeration, the second cond clause corresponds to the second data enumeration clause, and so on.

; Position is a Number.
; interp. distance between the left periphery and the ball
 
; Position KeyEvent -> Position
; compute the next location of the ball
 
(check-expect (nxt 13 "left") 8)
(check-expect (nxt 13 "right") 18)
(check-expect (nxt 13 "a") 13)
(define (nxt p k)
  (cond
    [(= (string-length k) 1) p]
    [(string=? "left" k) (- p 5)]
    [(string=? "right" k) (+ p 5)]
    [else p]))
(define (nxt p k)
  (cond
    [(string=? "left" k) (- p 5)]
    [(string=? "right" k) (+ p 5)]
    [else p]))

Figure 10: Conditional functions and special enumerations

When programs rely on data definitions that are defined by a programming language (such as BSL) or its libraries (such as the "universe" teachpack), it is common that they use only a part of the enumeration. To illustrate this point, let us look at a representative problem.

Sample Problem: Design an interactive program that moves a red dot left or right on a horizontal line in response keystrokes on the "left" or "right" arrow key.

Figure 10 presents two solutions to this problem. The function on the left is organized according to the basic idea of using one cond line per clause in the data definition of the input, here KeyEvent. In contrast, the right-hand side displays a function that uses the three essential lines: two for the keys that matter and one for everything else. The re-ordering is appropriate because only two of the cond-lines are relevant and they can be cleanly separated from other lines. Naturally, this kind of re-arrangement is done after the function is designed properly.

2.4.4 Intervals

Imagine yourself responding to the following sample design task:

Sample Problem: Design a program that simulates the landing of a UFO.

After a bit of thinking, you should come up with a program like in the figure below. Read the data definition and the function definitions carefully before you read on.

; WorldState is a Number
; interp. height of UFO (from top)
 
; constants:
(define WIDTH 300)
(define HEIGHT 100)
(define CLOSE (/ HEIGHT 3))
 
; visual constants:
(define MT (empty-scene WIDTH HEIGHT))
(define UFO
  (overlay (circle 10 "solid" "green")
           (rectangle 40 2 "solid" "green")))
 
; WorldState -> WorldState
; compute next location of UFO
 
(check-expect (nxt 11) 14)
 
(define (nxt y)
  (+ y 3))
 
; WorldState -> Image
; place UFO at given height into the center of MT
 
(check-expect (render 11)
              (place-image UFO (/ WIDTH 2) 11 MT))
 
(define (render y)
  (place-image UFO (/ WIDTH 2) y MT))
 
; run program run
; WorldState -> WorldState
(define (main y0)
  (big-bang y0 (on-tick nxt) (to-draw render)))

Figure 11: Landing a UFO

Before you release this "game" program, however, you may wish to add the display of status line to the canvas:

Sample Problem: The status line should say "descending" when the UFO’s height is above one third of the height of the canvas. It should switch to "closing in" below that. And finally, when the UFO has reached the bottom of the canvas, the status should notify the player that the UFO has "landed."

You are free to use appropriate colors for the status line.

In this case, we don’t have a finite enumeration of distinct elements or distinct subclasses of data. After all conceptually the interval between 0 and HEIGHT (for some number greater than 0) contains an infinite number of numbers and a large number of integers. Therefore we use intervals to superimpose structure on the generic data definition, which just uses "numbers" to describe the class of coordinates.

An interval is a description of a class of (real or rational or integer) numbers via boundaries. The simplest interval has two boundaries: left and right. If the left boundary is to be included in the interval, we say it is a closed on the left. Similarly, a right-closed interval includes its right boundary. Finally, if an interval does not include a boundary, it is said to be open at that boundary.

Pictures of, and notations for, intervals use brackets for closed boundaries and parentheses for open boundaries. Here are four pictures of simple intervals:

Exercise 42: Determine the integers that each of the four intervals contains.

The interval concept helps us formulate a data definition that captures the revised problem statement better than the "numbers" based definition:
; constants:
(define CLOSE (/ HEIGHT 3))