programs that use numbers
----------| add sub mul div |------------
generic arithmetic package
--| add-rat |-+-| add-comp |-+---| + |---
| sub-rat | | | sub-comp | | | - |
| mul-rat | | | mul-comp | | | * |
| div-rat | | | div-comp | | | / |
| |
rational | complex | ordinary
arithmetic | arithmetic | arithmetic
|------+-------|
| rect | polar |
-----------------------------------------
list structure &
primitive machine arithmetic
(define (add x y) (apply-generic 'add x y)) (define (sub x y) (apply-generic 'sub x y)) (define (mul x y) (apply-generic 'mul x y)) (define (div x y) (apply-generic 'div x y))
to install the package for ord numbers (get and put are assumed but not implemented):
(define (install-scheme-number-package)
(define (tag x)
(attach-tag 'scheme-number x))
(put 'add '(scheme-number scheme-number)
(λ (x y) (tag (+ x y))))
(put 'sub '(scheme-number scheme-number)
(λ (x y) (tag (- x y))))
(put 'mul '(scheme-number scheme-number)
(λ (x y) (tag (* x y))))
(put 'div '(scheme-number scheme-number)
(λ (x y) (tag (/ x y))))
(put 'make 'scheme-number
(λ (x) (tag x)))
'done)
users can create ordinary nums with this:
(define (make-scheme-number n)
((get 'make 'scheme-number) n))
see book for complete listing of
install-rational-package and install-complex-package.
how to combine one type with another?
we could add this to the complex package
(define (add-complex-to-schemenum z x)
(make-from-real-imag (+ (real-part z) x)
(imag-part z)))
(put 'add '(complex scheme-number)
(λ (z x) (tag (add-complex-to-schemenum z x))))
this is operational but is cumbersome
COERCION
in the general situation of completely unrelated operations acting on completely unrelated types, implementing explicit cross-type operations is clumsy but also the only way.
BUT we can do better by doing type coercion
here's one:
(define (scheme-number->complex n)
(make-complex-from-real-imag (contents n) 0))
these coercions can be logged in one of those tables:
(put-coercion 'scheme-number 'complex scheme-number->complex)
heres an impl:
(define (apply-generic op . args)
(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)))
(let ((t1->t2 (get-coercion type1 type2))
(t2->t1 (get-coercion type2 type1)))
(cond (t1->t2
(apply-generic op (t1->t2 a1) a2))
(t2->t1
(apply-generic op a1 (t2->t1 a2)))
(else
(error "no method for these types"
(list op type-tags))))))
(error "no method for these types"
(list op type-tags)))))))
HIERARCHIES OF TYPES
wow… getting dangerously close to inheritance here
hierarchies can be linear (easier to engineer) or else a tree of types