Built with from Grav and Hugo
The design recipe is a process for developing programs. The intent
is to make your life easier by asking the right questions and performing
the right tasks in the right order.
It’s really tempting, even for experienced programmers, to just jump in
and start writing code. Don’t! The design recipe helps you think
about the problem first so you can use your time efficiently.
You should have the Thrival Guide read by now. It makes sense to
leave the Style Guide until after you’ve finished reading the M03
slides but before you start the assignment.
Some instructors say that this is the most important slide in the entire course.
The communication between you and computer must describe precisely what the program
should do (but maybe not very readable by humans).
“The future” may be as little as five minutes later (depending
on one’s short-term memory), or as much as years later.
Even programs that one expects will never be seen by others
should be written as if they were; it will help in getting them
working properly without wasting time, and one never knows when
something will prove useful to others.
All programs written on assignments and exams are going to be read for
marking purposes; that alone merits careful attention to
A sample Racket comment:
;; Add x twice
(define (twice x) (+ x x))
We won’t define them all because that’s not the point. The point is that there are many possible
goals when developing software.
The goals that will be most important in CS135 are correctness, readability, and testability.
Efficiency will have a minor role towards the end of CS135 and a more major role in CS136.
The focus is on the process while you develop the function.
This is not something you tack on at the end. Tacking it on
at the end turns it into a make-work project. At that point most
of the benefit of the design recipe will have been lost.
Purpose: more English-like.
Contract: more math-like. So far, our functions have only consumed numbers and produce a number.
We’ll soon see different kinds of data. As our data becomes more complex, the contract will take
on an increasingly important role.
The function definition can be divided into two pieces, the header and the body. The header is the
function name, parameters and the define keyword. The body is the expression that computes the result.
We’ll have much more to say about examples and tests in a little bit.
Course personnel will be reluctant to assist students who have problems with code that lacks
a contract, purpose, or examples. Please do your best on them before you seek help.
It’s really common for students to use the following order instead
of the suggested order:
Doing these steps in order is important.
As you read these slides on using the design recipe, think about how you
would apply each step to write a function that sums the integers from 0 to n.
Purpose: The purpose says what the function is supposed to do. That’s
helpful for potential users of your function. But it’s also necessary for
you to write the function. If you don’t have a clear understanding of what
the function is supposed to do, you’ll have a difficult time writing it.
The purpose says what the function does; it doesn’t say how.
The purpose is placed in a comment. Racket comments that take the entire line
(as this one does) traditionally start with two semi-colons. Comments that
appear at the end of a line of code traditionally start with a single semi-colon.
Examples: Examples serve three purposes.
The first purpose is to show the function’s user a typical use of the function. What does
the code look like that uses this function?
The second purpose is a simple test to ensure it does what it is supposed to do. That is,
given a example use of the function (the first purpose), what is the correct
result? We write this as executable code, not a comment, so the computer can
check it for us. More on this in a moment.
The third purpose is to go through the process of finding the answer used for that
simple test. If you can’t
find the answer manually, you surely can’t write a function that tells the
computer how to do it.
Examples don’t need to be big or use large numbers. Usually the smallest non-trivial
examples are the best. They’re easy to work out by hand and easier to verify that they
are correct. But don’t chose examples that deliberately avoid steps in the solution,
Include examples in your program using the built-in function check-expect. It
takes two arguments. When you click “Run”, DrRacket will apply check-expect to
the arguments. If the two arguments evaluate to the same thing, it will simply
print “The test passed” in the interactions pane and go on to execute the rest of the
program. If the arguments evaluate to different values, it will print those values
and stop execution.
Header: The header is the whole function except for the body expression. Most
importantly, it includes the function name and parameter names.
You’ll often be given the name of the function in the assignment specification, although
sometimes you’ll choose the name yourself. In that case:
Likewise, choose parameter names that are meaningful. Names like interest-rate
and student-name are great. Some functions will just consume numbers that don’t have
a specific meaning. In those cases n or i (if an integer) is fine.
Contract: The contract says what type of data the function consumes and what type of
data it produces. The contract always contains an arrow. We’ll often typeset it as shown
in the slide, but you should write it as a dash and greater-than sign (->) in your code.
The left side of the arrow will contain a data type for each parameter. The right side
will contain the data type the function produces. We’ll discuss the possible data types
a little later in this lecture module and add to them as the course progresses. For
now, Num means any number (e.g. 3, 22/7, π, etc.). That is, sum-of-squares consumes
two numbers (one for n1 and one for n2) and produces another number.
Contracts give us an opportunity to carefully think through the data consumed by the
function. Is it really all numbers or only integers? All integers or only non-negative integers?
Contracts may feel trivial now, when we only have a few data types to chose from. As
we add more data types and techniques, the contracts will become more complex and
offer real help in designing our functions. Looking ahead to M14, we’ll eventually
see a contract such as (X Y -> Y) Y (listof X) -> Y.
(X Y -> Y) Y (listof X) -> Y
Note that the contract begins with the name of the function and a colon.
Purpose: Now that the names of the function and its parameters are established, we can
polish the purpose statement. It begins with the name and parameters, mimicing an application
of the function. The parameter names are used in the purpose statement to clarify
Function Body: Finally, we’re ready to write the function body. Hopefully, after
working our a number of examples by hand, this is reasonably easy to do.
Tests: The last step in the design recipe process is to write additional
tests to cover any complexities not covered by the examples. We’ll have more to
say about tests and the relationship between tests and examples a little later.
Tests are usually written after (in time) the function is written. That way the tests
can take into account the specifics of the code. There is a school of thought that says
tests should always be written first.
Tests are written after (on the page) the function.
Small, directed tests make it clear where the problem is when
they fail. With one large test, it may not be clear that all of the
code is exercised (this is not evident now, but will be once we see
conditional expressions) and even if all of the code is exercised,
it is hard to tell where the error is.
Working out the answer to a test “independently” does not necessarily mean
with pencil and paper. It might involve a calculator or spreadsheet or
published examples or …. The point is, you derive the answer without
using the code you’re trying to test.
Implementing check-expect is actually pretty tricky – to the point that it didn’t
exist when the first version of the textbook (the one CS135 is based on) was written.
So the textbook uses testing methods that are now obsolete.
The last parameter to check-within is the tolerance. The example is actually
checking that 1.414 - 0.001 <= (sqrt 2) <= 1.414 + 0.001 is true.
1.414 - 0.001 <= (sqrt 2) <= 1.414 + 0.001
The contract says what kind of data our function consumes and what kind of data it
produces. Right now, the only kind of data we know about are numbers. But even here there
are different kinds of numbers. A function might only work for integers, for example,
and fail for non-integers.
As we saw in the previous video, contracts have the form _____ -> _____ where the
left-hand side describes the data the function consumes and the right-hand side describes
the data the function produces.
_____ -> _____
On the left, give the most general data type for which the function will always work. Suppose
your function works for all Ints, fails for some Nums, and is typically used on Nats. Then
the left-hand side should be Int. It’s not Nat because Int is more general.
On the right, give the least general data type. It’s incorrect to say Int -> Num if
the function only produces integers. In that case, say Int -> Int.
Int -> Num
Int -> Int
Watch a demo of applying the design recipe. This example is more complex than
sum-of-squares and illustrates how to handle a “helper function”.
Problem Statement: Write a function, sum-range, which sums the numbers from a to b.
For example, (sum-range 3 6) should produce 3 + 4 + 5 + 6 or 18.
(sum-range 3 6)
3 + 4 + 5 + 6
Video: Download m03/m03.50_dr_demo
In the sample code, we have no idea what type is associated with the value of
q unless we trace through the evaluation of (mystery-fn 5) or trust its
Errors in a dynamically typed program are only found if the code is executed –
and then only if the right values are used (think division by zero). Sometimes
those errors are found by users, years after the program is “finished”.
In contrast, a statically typed language is processed by the computer (compiled)
before it is executed. The compilation process can find a large class of
errors automatically so they can be fixed before the program is allowed to run.
Note that when we say “all arguments…will obey the contract”, that’s what
we (the course instructors) think the contract is. That might be different
from what you write in your assignment!
Now would be a good time to read the Style Guide. It gives
lots of concrete direction on how to write up your assignments.
Most programming teams have some kind of
style guide that specifies how code is formatted, identifiers chosen, documentation
expectations, techniques to prefer (or avoid), etc. The goal is code that is easier
to read, understand, and maintain.
Everyone on the team is expected to follow to the style guide.
You are part of the CS135 team. It consists, at a minimum, of you, your instructor(s),
and the staff that are marking your assignments and exams. As a member of this
team, you are expected to follow the CS135 style guide.