sicp-ex-2.81



<< Previous exercise (2.80) | Index | Next exercise (2.82) >>


meteorgan

  
  
  
 ;;a 
 apply-generic calls itself recursively on coerced types, so it goes into infinite recursion. 
  
 ;;b 
 Louis's code can't work. apply-generic just works as it is. 
  
 ;;c 
 (define (apply-generic op . args) 
   (define (no-method type-tags) 
     (error "No method for these types" 
       (list op type-tags))) 
  
   (let ((type-tags (map type-tag args))) 
     (let ((proc (get op type-tags))) 
       (if proc 
           (apply proc (map contents args)) 
           (if (= (length args) 2) 
               (let ((type1 (car type-tags)) 
                     (type2 (cadr type-tags)) 
                     (a1 (car args)) 
                     (a2 (cadr args))) 
                 (if (equal? type1 type2) 
                   (no-method type-tags) 
                   (let ((t1->t2 (get-coercion type1 type2)) 
                         (t2->t1 (get-coercion type2 type1)) 
                         (a1 (car args)) 
                         (a2 (cadr args))) 
                     (cond (t1->t2 
                            (apply-generic op (t1->t2 a1) a2)) 
                           (t2->t1 
                            (apply-generic op a1 (t2->t1 a2))) 
                           (else (no-method type-tags)))))) 
               (no-method type-tags)))))) 

meteorgan's answer to 87a is partially correct.

For scheme-numbers, it depends on which implementation of `(type-tag)` is being used. If (type-tag) is defined like so:

 (define (type-tag datum) 
     (if (pair? datum) 
         (car datum) 
         (error "Bad tagged datum"))) 

Then the program will exit with an error when apply-generic is called for the first time (it attempts to apply type-tag to untagged scheme primitives). However, if (type-tag) is able to handle scheme primitives like so:

 (define (type-tag datum) 
     (cond ((number? datum) 'scheme-number) 
           ((pair? datum) (car datum)) 
           (else (error "Bad tagged datum")))) 

Then the program will loop recursively forever coercing scheme numbers into themselves and then calling apply-generic on the coerced data, as meteorgan says.

For 87b, I think that Louis is correct in the sense that "something had to be done about coercion with arguments of the same type" because even though (apply-generic) will exit with an error if no coercion is found, that doesn't stop our users from adding same-type coercion operators to our table (Louis already did it once). I do agree with meteorgan, that although something had to be done, the thing that Louis did is the wrong thing. I think apply-generic would be "more correct" if it was modified to exit with an error BEFORE attempting any same-type coercions. We could also add code to the `put` procedure which would make it exit with an error if a user attempted to `put` in a same-type coercion, but that would be extremely difficult (maybe impossible) and also adversely affect the expressiveness of our language.

atomik's correction to part a is wrong, it does infinite loop. One, the question is asking about complex numbers, not scheme numbers: "What happens if we call exp with two complex numbers as arguments?". Two, the claim about the error due to an "untagged scheme primitive" is irrelevant - if the first `type-tag` is installed in the system, then users must tag their numbers when calling generic functions. If it's the second `type-tag`, users don't have to tag their numbers. Simple as that.