SICP Exercise 2.81 coercion

Exercise 2.81.  Louis Reasoner has noticed that apply-generic may try to coerce the arguments to each other's type even if they already have the same type. Therefore, he reasons, we need to put procedures in the coercion table to "coerce" arguments of each type to their own type. For example, in addition to the scheme-number->complex coercion shown above, he would do:

(define (scheme-number->scheme-number n) n)
(define (complex->complex z) z)
(put-coercion 'scheme-number 'scheme-number
              scheme-number->scheme-number)
(put-coercion 'complex 'complex complex->complex)


a. With Louis's coercion procedures installed, what happens if apply-generic is called with two arguments of type scheme-number or two arguments of type complex for an operation that is not found in the table for those types? For example, assume that we've defined a generic exponentiation operation:

(define (exp x y) (apply-generic 'exp x y))

and have put a procedure for exponentiation in the Scheme-number package but not in any other package:

;; following added to Scheme-number package
(put 'exp '(scheme-number scheme-number)
     (lambda (x y) (tag (expt x y)))) ; using primitive expt


What happens if we call exp with two complex numbers as arguments?

b. Is Louis correct that something had to be done about coercion with arguments of the same type, or does apply-generic work correctly as is?

c. Modify apply-generic so that it doesn't try coercion if the two arguments have the same type.

SOLUTION

The code and tests are here.

Observations

a. The program enters an infinite loop. What happens is: Since there is no procedure defined for exponentiation of two complex numbers, apply-generic tries to coerce one type into the other. In this case, both are complex types and the coercion procedure returns the same type. Again, apply-generic is called with the same arguments and the process repeats itself. So the execution becomes and infinite loop.

b. Louis is not entirely correct. When apply-generic is unable to find a procedure for its two arguments, it tries to coerce one to the other. If there are no procedures for this coercion, then apply-generic will stop with an error message. This is the right behaviour. See below. (By adding the procedures to the table, Louis made matters worse.)

For (exp c1 c2) the interpreter does the following:
Searching op-table for exp, (complex complex)
. . No method for these types (exp (complex complex))

So apply-generic works correctly as is.

c. However, apply-generic can be made more efficient by changing it so that it doesn't try coercion if the two arguments have the same type. See implementation in the code (link above).

Results after making the change asked in part c of the problem:

; Tests

Welcome to DrRacket, version 6.11 [3m].
Language: racket, with debugging; memory limit: 512 MB.
> (exp 4 3)
Searching op-table for exp, (scheme-number scheme-number)
64
> (define c1 (make-complex-from-real-imag 11 12))
Searching op-table for make-from-real-imag, complex
Searching op-table for make-from-real-imag, rectangular
> (define c2 (make-complex-from-real-imag 17 19))
Searching op-table for make-from-real-imag, complex
Searching op-table for make-from-real-imag, rectangular
> (define r1 (make-rational 15 19))
Searching op-table for make, rational
> (exp c1 c2)
Searching op-table for exp, (complex complex)
. . Types are the same. So not trying coercion. No method for these types (exp (complex complex))
> (exp c1 r1)
Searching op-table for exp, (complex rational)
. . No method for these types (exp (complex rational))

Comments

Popular posts from this blog

SICP Exercise 2.56 differentiation rule

SICP Exercise 1.28 (Miller-Rabin Test)

SICP Exercise 4.18 a alternative strategy for interpreting internal definitions