2.85
; This section mentioned a method for “simplifying” a data object by lowering it in
; the tower of types as far as possible. Design a procedure drop that accomplishes
; this for the tower described in Exercise 2.83. The key is to decide, in some
; general way, whether an object can be lowered. For example, the complex number
; 1.5 + 0i can be lowered as far as real, the complex number 1 + 0i can be lowered
; as far as integer, and the complex number 2 + 3i cannot be lowered at all. Here is
; a plan for determining whether an object can be lowered: Begin by defining a
; generic operation project that “pushes” an object down in the tower. For example,
; projecting a complex number would involve throwing away the imaginary part. Then a
; number can be dropped if, when we project it and raise the result back to the type
; we started with, we end up with something equal to what we started with. Show how
; to implement this idea in detail, by writing a drop procedure that drops an object
; as far as possible. You will need to design the various projection operations and
; install project as a generic operation in the system. You will also need to make
; use of a generic equality predicate, such as described in Exercise 2.79. Finally,
; use drop to rewrite `apply-generic` from Exercise 2.84 so that it “simplifies” its
; answers.
; Source: [https://wizardbook.wordpress.com/2010/12/08/exercise-2-85/](https://wizardbook.wordpress.com/2010/12/08/exercise-2-85/)
; There are 3 changes to make. The first is for apply-generic to use the new drop
; procedure. Note that it doesn’t make sense to drop the result of all generic
; operations, for example predicates, and that drop is only called after any type
; coercion using raise has completed.
(define (apply-generic op . args)
; only certain operations will result in an answer that can be
; projected e.g. it makes no sense to project the answer to zero?
(define (reduce-type x)
(cond ((eq? op 'add) (drop x))
((eq? op 'sub) (drop x))
((eq? op 'mul) (drop x))
((eq? op 'div) (drop x))
(else x)))
; 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
(reduce-type (apply proc (map contents args)))
(apply-with-raised-types args))))
; Second implement drop and the top-level generic project.
(define (project z) (apply-generic 'project z))
(define (drop z)
(if (= (type-level z) 1)
z
(let ((projected (project z)))
(if (equ? z (raise projected))
(drop projected)
z))))
; Third, add type specific project procedures for each type that can project.
; in the rational package
(define (project r)
(make-integer (truncate (/ (numer r) (denom r)))))
(put 'project '(rational) (lambda (x) (project x)))
; in the real package - this is a bit messy because real numbers can be either
; integers, rational or irrational.
(define (project r)
(let ((exact (inexact->exact r)))
(cond ((integer? exact) (make-rational exact 1))
((rational? exact) (make-rational (numerator exact) (denominator exact)))
(else (make-rational (truncate exact) 1)))))
(put 'project '(real) (lambda (x) (project x)))
; in the complex package
(define (project z1)
(make-real (real-part z1)))
(put 'project '(complex) (lambda (x) (project x)))