Expressions, functions and cases in Racket
Last modified: 2026-11-06 16:28:53 (PST)
Source
Kameron Decker Harris’s Fall 2026 syllabus opens the course with an introduction to functional programming; this chapter, its examples and its exercises are new.
The definition of an expression is on the expressions and algorithms page, and those of a function, an argument and a value are on the functions page. The other data science notes link to both pages.
Remark 1 (Running Racket). This course writes its programs in Racket, a programming language that grew out of Scheme, a dialect of Lisp. Racket comes with DrRacket, the program you use to write and run Racket code, and Lab 1 walks through using it. Each Racket file in this course starts with the line #lang racket. The Racket Guide is the reference for anything these notes do not cover.
Exercise 1 (Operation first) Racket writes \(7 - 2\) as (- 7 2), with the operation first and the parentheses around the whole expression. Guess what each Racket expression returns:
(* (+ 1 2) (- 10 4))(- 20 (* 3 (+ 1 1)))(+ 1 2 3 4)Then write \((2 + 3) \cdot 4 - 1\) in Racket.
Solution 1. Work from the innermost parentheses outward.
(+ 1 2) is 3 and (- 10 4) is 6, so the expression is (* 3 6), which returns 18.(+ 1 1) is 2, so (* 3 (+ 1 1)) is 6, and (- 20 6) returns 14.+ can take any number of operands, and adds them all, so the expression returns 10.The expression \((2 + 3) \cdot 4 - 1\) subtracts 1 from the product of \(2 + 3\) and 4, so in Racket it is (- (* (+ 2 3) 4) 1), which returns 19.
Definition 1 (Function, map, mapping, transformation) A function \(f\) from a set \(A\) to a set \(B\), (Wikipedia contributors 2026b) written \(f : A \to B\), assigns to each element \(a \in A\) exactly one element \(f(a) \in B\).
A function is also called a map (or mapping) or a transformation; on this site the three words mean the same thing, although some authors reserve “transformation” for a function from a set to itself.
Definition 2 (Expression) An expression (Wikipedia contributors 2026a) is a string of symbols, built from constants, variables, operations and function names according to the rules of the notation.
Definition 3 (Denotes) An expression denotes a function when evaluating the expression at each input of the function, with the expression’s variables taking that input, gives the function’s value at that input.
Definition 4 (Racket expression, prefix notation) A Racket expression (Definition 2) is one of:
7;x or +;(f e1 ... en): an expression f for an operation or function, followed by its operands e1, …, en, which are expressions too, all inside one pair of parentheses.Writing the operation before its operands is called prefix notation. Racket needs no precedence rules, because the nesting of the parentheses says which operation comes first: (* (+ 2 3) 4) adds first, and (+ 2 (* 3 4)) multiplies first.
Remark 2 (How Racket evaluates an application). To evaluate an application (f e1 ... en) (Definition 4), Racket evaluates each operand e1, …, en to a value, then applies f to those values. In part 1 of Exercise 1:
Each = replaces one application by its value, and the evaluation ends with a value, which cannot be simplified further.
Solution 2.
(* x x) is \(x^2\) and (* 2 x) is \(2x\).(h 5) returns 15.(h 3), which is \(9 - 6 = 3\), and then applies h to it, so (h (h 3)) is (h 3) again and returns 3.Definition 5 (Argument, input) An argument (or input) of a function \(f : A \to B\) is an element \(a \in A\) that \(f\) is applied to.
Definition 7 (Defining a function in Racket) The Racket definition
defines a function named f with parameters x1, …, xn: names that stand for its arguments (Definition 5). To evaluate an application (f a1 ... an), Racket evaluates the arguments a1, …, an, replaces each parameter in body by the value of its argument, and evaluates the result. The value of the application is the value (Definition 6) of f at those arguments.
Example 1 (Replacing a parameter by its argument) With h from Exercise 2, Definition 7 evaluates (h 5) in four steps:
The first step replaces x by 5 in the body of h, just as computing \(h(5)\) replaces \(x\) by 5 in \(x^2 - 2x\). Each later step replaces one application by its value (Remark 2).
Exercise 3 (Choosing a case) A museum sets its ticket price, in dollars, by age:
Each line in square brackets pairs a test with a value.
(ticket-price 4), (ticket-price 5), (ticket-price 17) and (ticket-price 70) return.Solution 3.
Racket tries the tests in order and returns the value beside the first true one:
(ticket-price 4) returns 0, since \(4 < 5\).(ticket-price 5) returns 8, since \(5 < 5\) is false but \(5 < 18\) is true.(ticket-price 17) returns 8, since \(17 < 18\).(ticket-price 70) returns 12, since neither test is true, so the else line applies.The second case holds only when the first test is false, so its condition is \(5 \le a < 18\), not just \(a < 18\):
\[ p(a) = \begin{cases} 0 & \text{if } a < 5, \\ 8 & \text{if } 5 \le a < 18, \\ 12 & \text{if } a \ge 18. \end{cases} \]
Definition 8 (True and false in Racket) Racket writes the two truth values as #t (true) and #f (false). A comparison such as (< 4 5) or (= 3 7) returns one of them, and and, or and not combine them: (and (< 1 2) (< 3 2)) returns #f.
Definition 9 (if and cond) The expression (if test a b) evaluates test, and returns the value of a if test is true and the value of b if it is false.
The expression
evaluates the tests in order and returns the value of the expression beside the first true one, or the value of an if none is true. Like a function defined by cases in mathematics, each case of a cond applies only when every case before it fails.
if and cond look like applications (Definition 4), but they are not: an application evaluates all of its operands (Remark 2), while if and cond evaluate only the tests they need and the one branch they return. and, or and define are not applications either.
Example 2 (Absolute value with if) The absolute value \[
\mathopen{}\left|x\right|\mathclose{} =
\begin{cases}
-x & \text{if } x < 0, \\
x & \text{otherwise}
\end{cases}
\] has two cases, so one if (Definition 9) is enough:
(absolute -7) returns 7, and (absolute 2) returns 2. Racket already has this function, under the name abs.
Exercise 4 (Same call, different answer) This Python code hands out ID numbers:
next_id() twice in a row. What does each call return?next_id() returns every time it is called?Solution 4.
count from 0 to 1 and returns 1. The second call changes count from 1 to 2 and returns 2.next_id() returned 1 and then 2. What it returns depends on the variable count, which is not an argument and which each call changes.Definition 10 (Side effect) A side effect of a piece of code is any change it makes besides returning a value, such as changing a variable, printing, or writing a file. next_id in Exercise 4 has a side effect: it changes count.
Definition 11 (Pure function) A function in a program is pure (Wikipedia contributors 2026e) if
A pure function computes a mathematical function (Definition 1) of its arguments.
Definition 12 (Functional programming) Functional programming (Wikipedia contributors 2026c) is a style of programming in which a program is built by defining and applying pure functions (Definition 11), rather than by running statements that change variables.
Remark 3 (Why this course uses functional programming). In a program built from pure functions (Definition 11), an expression can be replaced by its value anywhere it appears without changing what the program does. That is what makes evaluation by substitution (Example 1) work, and it is also how algebra works: \(h(5)\) can be replaced by 15 in any formula. So a pure function can be checked with the same reasoning as a formula, and this course proves facts about programs that way.
next_id in Exercise 4 is not pure, so next_id() + next_id() cannot be simplified to 2 * next_id(). On a fresh start, next_id() + next_id() returns 3, from \(1 + 2\), but 2 * next_id() returns 2. Racket can change variables too, with set!, but this course does not use it.
Exercise 5 (A function as an argument) The function twice takes a function f and a value x:
Guess what each expression returns:
(twice add-three 10)(twice h 4), with h from Exercise 2(twice add-three (twice add-three 0))Solution 5. Replace f and x in the body (f (f x)) (Definition 7):
(twice add-three 10) is (add-three (add-three 10)), which is (add-three 13), which returns 16.(twice h 4) is (h (h 4)). Since \(h(4) = 16 - 8 = 8\) and \(h(8) = 64 - 16 = 48\), it returns 48.(twice add-three 0) returns 6, so the whole expression is (twice add-three 6), which returns 12.Definition 13 (Higher-order function) A higher-order function (Wikipedia contributors 2026d) is a function that takes a function as an argument, or returns a function as its value. twice in Exercise 5 is a higher-order function.
Example 3 (map and filter) Racket’s (list 5 7 9) makes the list of 5, 7 and 9, which Racket prints as '(5 7 9). Two built-in higher-order functions (Definition 13) work on lists:
(map f L) applies f to each element of the list L, and returns the list of results. With add-three from Exercise 5, (map add-three (list 5 7 9)) returns '(8 10 12).(filter p L) returns the list of the elements x of L for which (p x) is true. (filter even? (list 5 6 7 8)) returns '(6 8).Neither one changes L; each returns a new list.
Exercise 6 (Applying a function to a list) (apply f L) applies the function f to the elements of the list L as its arguments, so (apply + (list 2 7 6)) is the same as (+ 2 7 6), which returns 15.
(apply max (list 3 9 2)) returns.apply, max and min to define (spread L), which returns the largest element of a nonempty list of numbers minus its smallest element.(spread (list 4 -1 7 2)) returns.Solution 6.
(apply max (list 3 9 2)) is (max 3 9 2), which returns 9.
One answer:
The largest element is 7 and the smallest is \(-1\), so (spread (list 4 -1 7 2)) returns \(7 - (-1) = 8\).
spread needs no recursion: max and min already take any number of arguments, and apply hands each of them the whole list.
Solution 7. (lambda (x) (* x x)) is the function that squares its argument, written in place without a name.
map (Example 3) applies the doubling function to each element, and returns '(2 4 6).pair-with, y is "a", so map applies the function \(x \mapsto (x, \text{"a"})\) to each element, and returns '((1 "a") (2 "a") (3 "a")).Definition 14 (lambda) The Racket expression
is a lambda expression. Its value is the function with parameters x1, …, xn whose value at its arguments is the value of body, found as in Definition 7. The function has no name. The body may use any name defined where the lambda expression appears, as the body of the lambda in pair-with (Exercise 7) uses y.
The definition (define (f x) body) is shorthand for (define f (lambda (x) body)): it gives the name f to the function the lambda expression makes.
Video: Jerry Cain uses lambda with map in Scheme, the language Racket grew out of, in Stanford CS 107 Lecture 22 (Cain 2008).
Remark 4 (Where the name comes from). The word lambda comes from the lambda calculus, a model of computation in which a function is written \(\lambda x.\, e\), the function that takes \(x\) to the value of the expression \(e\). The lambda calculus has only three features: functions, applying a function to an argument, and variables. It has no numbers, strings or loops (Crichton 2019). Applying \(\lambda x.\, e\) to an argument replaces each \(x\) in \(e\) by that argument, as in evaluation by substitution (Example 1). The function in Exercise 7 that squares its argument is \(\lambda x.\, x \cdot x\).
Media: Will Crichton’s notes on the lambda calculus, from Stanford’s CS 242, Programming Languages, go further (Crichton 2019).
Example 4 (A function that returns a function) The function adder takes a number d and returns a function:
(adder 3) returns the function \(x \mapsto x + 3\), which still knows that d is 3 after adder has returned. So adder is a higher-order function (Definition 13), and its value can be used anywhere a function can:
((adder 3) 4) returns 7.(twice (adder 3) 10), with twice from Exercise 5, returns 16.(map (adder 1) (list 5 7 9)) returns '(6 8 10).Definition 15 (let) The Racket expression
evaluates e1, …, en, gives each value its name, and returns the value of body, which can use those names. The names last only inside body.
A let expression is shorthand for applying a lambda expression (Definition 14): it has the same value as ((lambda (name1 ... namen) body) e1 ... en). So (let ([s (+ 1 2)]) (* s s)) and ((lambda (s) (* s s)) (+ 1 2)) both return 9.
Video: Cain uses let to save a result that would otherwise be computed twice, and shows how let becomes a lambda, in CS 107 Lecture 22 (Cain 2008).
Example 5 (Naming a value used twice) The solutions of \(a x^2 + b x + c = 0\) with \(a \ne 0\) and \(b^2 - 4ac \ge 0\) are \[
x = \frac{-b \pm s}{2a}, \qquad \text{where } s = \sqrt{b^2 - 4ac}.
\] Both solutions use \(s\), so a let names it and computes it once:
(roots 1 -3 2) returns '(2 1), since \(x^2 - 3x + 2 = (x - 1)(x - 2)\).
let does not change a variable: inside the body, s stands for one value, as a name does in algebra. So roots is a pure function (Definition 11).
Remark 5 (Scheme and Racket). Stanford’s CS 107, Programming Paradigms, taught Scheme in its Spring 2008 offering, and its lectures are free to watch (Cain 2008). Racket grew out of Scheme, and most of the Scheme code in those lectures runs in Racket with few changes:
car and cdr return the first element of a list and the list of the other elements. Racket accepts them too, but this course uses their clearer names first and rest.#lang racket.cond in square brackets, [test a], where Scheme uses parentheses. Racket accepts both.Video: Jerry Cain introduces Scheme, its lists and its first recursive functions in CS 107 Lecture 19 (Cain 2008).
Remark 6 (Further reading).