About the book

There are 2 types of recursions: recursive processes and iterative processes. Iterative processes consume the same amount of memory of a loop construct in an imperative language.

you can represent data purely as functions, by:

  • cheating, using closures and lambdas.
  • creepy Church tricks.

OO

Say you want a class C. So, you define a function called C. Within it, you can define more functions, known only to C, and store data, and make that data accessible only to the functions defined within C.

Say you want an object c. You can write C to return a dispatch function (that you also write), that takes the name of the procedure you want, and returns that procedure.

And, since you wrote the dispatch function, you can put whatever you want in it; access control, for example.

Complexity

Sometimes the authors are very picky about complexity (implement queue operations in O(1)), and sometimes they just don't give a damn about it (implement a hash table, and don't worry if each key lookup takes O(n)).

Projects

It is amazing how far can you get with so little, if you have a proper design. The authors build, with remarkably few language elements, remarkably few code, and remarkably few concepts, things like:

  • An object system (through closures.)
  • A small CAS that does derivation.
  • A binary circuit simulator.

A thing I don't like is that most of the design is presented in a top-down fashion, and it all magically works (of course) and the pieces of each element do fit properly. I do think that such approach to designing systems is the best most of the time (let's pretend I have these elements, I'll interconnect them in these ways, and then I'll worry about building those elements I dreamt of), but the process of devising which elements to use is simply magical most of the time, in the way it is presented in the book.

May it is just me complaining about the author's experience with the subject.

Modularity

You can reuse code through module construction, not through classes.

Attempting to reuse code using classes forces the person inheriting the class to put up with lots of stuff like its state and how it must be constructed, called, and so on. It forces the user to consider the state of the thing he is using.

In that regard, pure functions are the best way to achieve code reuse.

Besides, tell me, how many times have you used a library by inheriting from some class it contains?

I;m not saying that doesn't happen, only that it is not frequent; it is not a proper reuse mechanism. Not in the way modules and referentially transparent functions are.

When you see that a given function can be used by many clients, you abstract it away and have those clients to use the new function. You don't inherit from a random class that does God only knows what when initialized.

Timeline of introduced Scheme Features

What you are allowed to use at what moment:

Definition of constants, 1.1.2

(define size 2)

Definition of functions, 1.1.4

(define (square x) (* x x))

Conditionals using cond, 1.1.6

(cond (<predicate1> <expression1>)
      (<predicate2> <expression2>)
      ...
      (else <expressionN>))

Conditionals using if, 1.1.6

(if <predicate> <expression-if-true> <expression-if-false>)

and, or, not, 1.1.6

(and <e1> ... <eN>)
(or <e1> ... <eN>)
(not <e>)

function definitions inside functions, 1.1.8

(def (f x)
  ...
  (def (g y)
    ...)
  ...
  ...)

Text printing, 1.2.6

(newline)
(display <expression>)