HtDP's Approach To Using Scheme

Scheme uses parenthesized prefix notation; the expression 2+3 is written (+ 2 3). The function double(x) = 2x would be defined by:
(define (double x)
  (* x 2))
Evaluating the expression (double (double 3)) then yields 12. Alternately one could:
(define (quadruple x)
  (double (double x)))

in which case (quadruple 3) would evaluate to 12. DrScheme has a Definitions window into which the definitions are entered, and an Interactions window running an interactive read-evaluate-print loop to enter expressions like (quadruple 3).

This is pretty much the only syntax needed for the first three chapters of HtDP. The syntax for conditional expressions is introduced after that, together with Boolean functions. Here is code for a function which takes the three coefficients of a nondegenerate quadratic equation and returns the number of roots.

(define (how-many a b c)
  (cond 
    [(< (discr a b c) 0 ) 2]
    [(= (discr a b c) 0 ) 1]
    [(> (discr a b c) 0 ) 0]))

(define (discr a b c)
  (- (* b b) (* 4 a c)))

Since there are no (mutable) variables at this point, an expression such as (how-many 1 -4 3) can be evaluated using simple substitution. The semantics of the language are very easy to define. The equivalent of stepping through an imperative program is stepping through the substitutions necessary to evaluate such an expression. The text does not present unadorned code as I have; students are from the beginning shown how to preface definitions with a contract, header, purpose, and examples, and follow the code with a test suite.

HtDP then introduces compound data through a created type called posn, representing a point in two dimensions. After showing how constructors and selectors work (using a graphical application), this is generalized to the struct function for creating compound data. This function is not part of regular Scheme (it is easily defined in full Scheme, but only a small subset language is being used at this point). It automatically sets up constructors and selectors for user-defined structures. Note how OO terminology is being used without heavy emphasis on OO concepts.

The cons function allows construction of structures which contain two references to other structures; this, with its selectors first and rest (renamed from the historic car and cdr) permits the recursive definition of lists (and, later, trees). Here data-directed design is introduced, with a template for recursive procedures on lists that is derived from the definition. This is particularly important in Scheme, because at this level of understanding there is no mutation of objects (assignment) and thus no direct equivalent of a while or for loop. Instead, recursion is used, and it is introduced first not in computations on natural numbers, but on data structures whose definitions are self-referential. After some practice in this setting, a recursive definition of natural numbers leads via data-directed design to recursive functions that compute in an iterative fashion. The student does not need an explanation of stack frames or "invocations" to understand recursion; the substitution model they already know works fine. If a recursive factorial function is defined, then evaluating (factorial 4) by substitution leads to (* 4 (factorial 3)), then (* 4 (* 3 (factorial 2))) and so on. Recursion, instead of being some mysterious magic, is the natural equivalent of iteration.

After discussing more complicated examples, which lead to iterative refinement and the use of helper functions, the book introduces local (another pedagogical definition) to encapsulate helper functions and definitions, leading to discussion of scope and block structure. Together with one of the key features of Scheme and other functional languages -- the notion that functions are values that can be consumed or produced by other functions -- this allows abstraction and polymorphism. At this point a number of mathematical examples (sequences and series, numerical integration and differentiation) can be treated.

One of the strengths of the book is its distinction between structural recursion (that which arises as a result of a data definition) and generative recursion (where a particular input may affect the nature of the recursion). Quicksort is an example of generative recursion. After more examples (Newton's method, backtracking search), the book introduces the notion of vectors (analogous to arrays), which permits a discussion of efficiency, and accumulator-style functions.

In the last third of the book, mutation (assignment) is discussed. This significantly alters the simple semantic model, but prepares students for procedural and object-oriented languages, through the discussion of the encapsulation of local state. At this point a student will be able to make a lateral move into the world of Java, having seen and worked with most of the essential concepts in a syntactically simpler framework.

My section-by section overview of the book is here.