Racket's Substitution Model
The Substitution Model of Function Application
We won't be talking about this material in class, but it will help you develop your skills at reading Racket by providing a model for what functional programming is about.
Note: I adapted the material on this page from open course material by Dr. Phillip J. Windley at Brigham Young University, with a few modifications.
In order to understand function evaluation, we need a model. For a language such as Java, the evaluation model can be quite complex. However, because of Racket's consistent syntax, and the way it uses functions, our model of evaluation is quite simple. Later on, we will expand it.
Recall that in
the lecture on evaluating expressions
earlier, we determinied that we could evaluate any complex
expression, (operator operand1 operand2 operand3 ....)
,
by doing the following:
- Evaluate the operator. If it is a function...
- Evaluate each of the remaining subexpressions.
- Apply the function to the rest.
This evaluation model is valuable, but not detailed enough. In particular, we haven't discussed how we evaluate names and we don't know what it means to apply something.
The model we will discuss is called the substitution
model, because when we evaluate a name, we substitute
its definition in place of the name. Applying a function
to its parameters is also done using substitution. To apply
a lambda
expression (the only kind of function we
will worry about):
-
Substitute the body of the
lambda
expression for the application (that is, the entire expression including the function and its paramters) - Substitute the parameters into the body on a one-for-one basis with the arguments.
Perhaps the best way to explain the substitution model is with an example. Suppose we are evaluating:
(any-old-func 5)
And that any-old-func
is defined as:
(define any-old-func (lambda (w) (sum-of-squares (- w 3) w)))
We can find the result of evaluating the application
(any-old-func 5)
by following our algorithm. First,
we substitute the definition of any-old-func
for the
name:
( (lambda (w) (sum-of-squares (- w 3) w)) 5)
Next, we apply the leftmost result to all the rest by
substituting the body in place of the application and then
substituting the argument 5
for the formal parameter
w
in the body:
(sum-of-squares (- 5 3) 5)
This expression can be simplified by substituting the value of the second subexpression for the subexpression, giving:
(sum-of-squares 2 5)
Again, we can replace this expression with the body of the
definition for sum-of-squares
:
( (lambda (x y) (+ (square x) (square y))) 2 5)
And then replacing the entire application with the body and
substituting the arguments 2
and 5
for
the formal parameters x
and y
,
respectively:
(+ (square 2) (square 5))
We continue on in this way until we arrive at an answer:
(+ (* 2 2) (square 5)) (+ 4 (square 5)) (+ 4 (* 5 5)) (+ 4 25) 29
Note that in these last steps I have taken some short cuts; there is nothing wrong with this as long as the derivation is clear and you don't confuse yourself. Also, at any step along the way, I could have stopped and entered the current expression into Racket and it would evaluate.
There is nothing special about the order in which we substitute for subexpressions. For example, we could have continued the above example like this:
(+ (square 2) (* 5 5)) (+ (square 2) 25) (+ (* 2 2) 25) (+ 4 25) 29
In fact, we could even do it like this:
(+ (* 2 2) (* 5 5)) (+ 4 25) 29
Let's try to use the substitution model to work through an
application of the compose
function of we saw
earlier.
((compose add2 add2) 3) (((lambda (f g) (lambda (x) (f (g x)))) add2 add2) 3) ((lambda (x) (add2 (add2 x))) 3) (add2 (add2 3)) (add2 ((lambda (n) (+ n 2)) 3)) (add2 (+ 3 2)) (add2 5) ((lambda (n) (+ n 2)) 5) (+ 5 2) 7
This is quite a bit more involved, but hopefully you see what a convenient tool substitution is for analyzing expressions.
A few comments:
- Notice that I ordered the substitution so that the expressions didn't become unwieldy.
- The order of substitution and expansion does not make any difference to the outcome for functions we are defining now. It will, occasionally, later on.
- Substitution is best done using an editor where one can easily cut and paste.
If you learn the substitution model and apply it to analyzing your programs in Racket when your are stuck, you will find that it is a powerful tool for programming.
Questions
Evaluate each of the following using the substitution model:
(square (any-old-func 6)) (sum-of-squares (square 6) (any-old-func 2)) (any-old-func (sum-of-squares (+ 4 5) (+ 2 3))) (square (sum-of-squares (cube (+ (- 5 3) 2)) (sum-of-squares (+ 2 1) (* 3 2)))) ( (compose car cdr) '(1 2 3 4) )