Skip to content

2.84

; Using the raise operation of Exercise 2.83, modify the apply-generic procedure so 
; that it coerces its arguments to have the same type by the method of successive 
; raising, as discussed in this section. You will need to devise a way to test which 
; of two types is higher in the tower. Do this in a manner that is “compatible” with 
; the rest of the system and will not lead to problems in adding new levels to the 
; tower.

; Source: [https://wizardbook.wordpress.com/2010/12/08/exercise-2-84/](https://wizardbook.wordpress.com/2010/12/08/exercise-2-84/)

(define (apply-generic op . args)

  ; find the highest type level of a list of arguments
  (define (highest-type-level args)
    (if (null? args) 
        0
        (let ((level (type-level (car args)))
              (highest (highest-type-level (cdr args))))
          (if (> level highest)
              level
              highest))))

  ; raise arg to the same level as target-type-level
  (define (raise-to arg target-type-level)
    (define (raise-iter current-arg)   
      (let ((arg-level (type-level current-arg)))
        (cond ((= arg-level target-type-level) current-arg)
              ((< arg-level target-type-level) (raise-iter (apply-generic 'raise current-arg)))
              (else (error "Cannot raise argument to a lower type target" arg target-type-level)))))
    (raise-iter arg))

  ; raise all args to a common type (the highest in the tower of types)
  ; and apply the operator to them 
  (define (apply-with-raised-types args)
    (let ((target-type-level (highest-type-level args)))
      (apply apply-generic 
             op 
             (map (lambda (arg)
                    (raise-to arg target-type-level))
                  args))))

  (let* ((type-tags (map type-tag args))
         (proc (get op type-tags)))
    (if proc 
        (apply proc (map contents args))
        (apply-with-raised-types args))))

; This uses a new top-level generic procedure to find the level of the number type 
; and a new procedure for each of the number types.
(define (type-level z) (apply-generic 'type-level z))
(put 'type-level '(integer) (lambda (x) 1))
(put 'type-level '(rational) (lambda (x) 2))
(put 'type-level '(real) (lambda (x) 3))
(put 'type-level '(complex) (lambda (x) 4))