Search code examples
functional-programmingpolymorphismschemesicp

Why can't we assimilate the predicates number? and same-variable? into the data-directed dispatch?


In exercise 2.73 from section 2.4.3, the following code is shown:

(define (deriv exp var)
   (cond ((number? exp) 0)
         ((variable? exp) (if (same-variable? exp var) 1 0))
         (else ((get 'deriv (operator exp)) (operands exp)
                                            var))))
(define (operator exp) (car exp))
(define (operands exp) (cdr exp))

which makes use of data-directed dispatch, assuming that for each operator op that one can possibly find in the input exp, an appropriate function f has been installed in a table via put 'deriv op f.

The first question about this exercise is the following

a. […] Why can't we assimilate the predicates number? and variable? into the data-directed dispatch?

where the two predicates refer to the "old" implementation of deriv, which uses the following instead of the else branch above:

        ((sum? exp)
         (make-sum (deriv (addend exp) var)
                   (deriv (augend exp) var)))
        ((product? exp)
         (make-sum
           (make-product (multiplier exp)
                         (deriv (multiplicand exp) var))
           (make-product (deriv (multiplier exp) var)
                         (multiplicand exp))))
        ; more rules can be added here
        (else (error "unknown expression type -- DERIV" exp))))

My question is why can't I?

The proposed implementation of operator and operands is simple and assumes that an operator is present and that is the first in the list representing the expression exp, so I can't hope to make the "assimilation" without changing them.

But what if I do change them?

I could define them like so,

(define (operator e)
  (cond ((pair? e) (car e))
        ((number? e) 'number)
        (else 'variable)))

(define (operands e)
  (cond ((pair? e) (cdr e))
        (else e))) ; maybe `e` -> `cons e '()`, not sure

deciding that if the expression e is not a pair, then it is its own operands, the operator being the literal 'number or 'variable as appropriate.

Then I could install the needed procedures under 'deriv-'number and 'deriv-'variable.


The procedures put and get are defined in 3.3.3, and I'm trying not to step ahead of myself, so I've not yet looked into that, hence I haven't tried testing my hypothesis as I described above.


Solution

  • You could write that, but you haven't really assimilated into the data-directed approach. You've just moved the special-casing for number? and variable? into a different function. I think the text is asking you why you can't uniformly dispatch on operator without any special cases, and the answer is because your representation does not define an operator for numbers or variables.

    If I were going to try to make this representation more uniform, I would define a constructor make-number such that (make-number 6) yields something tagged as a number, such as '(number . 6), and likewise for variables. Then you could install handlers for those tags, and just look at the operator of each datum, because each datum will really have an operator.