attach-tag, tag-type
and contents
for
this purpose. When a function has several parameters, the list of the
types of its parameters is called its signature.
(put op signature specific-function)
and (get op signature)
for this purpose.
apply-generic
which dispatches a call to a generic function to
the specific instance that matches its parameters:
(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)) (error "No method for these types -- APPLY GENERIC" (list op type-tags))))))If a system includes types with no relation among them, the best we can do to define generic operations that work on combinations of different types is to define a specific instance of the generic operation for each possible signature and to insert it into the table.
This is achieved using entries of the following form:
(define (add-complex-to-number z x) (make-complex-rect (+ (complex-x z) x) (complex-y z)))
It is, however, often the case that relations among types exist. The basic relation is that a type is a supertype of another. In this case we want to exploit the relation to reduce the number of specific operations to implement.
This is done using the mechanism of coercion also known as type casting.
In general, this can be solved by using a table of coercion procedures, that explicitly indicates how an element of type T1 can be converted to be an element of type T2 for every pair of types (T1, T2). Thus, we may need as many as n2-n coercion functions to be able to convert from any type to any other type in a system including n distinct types.
Assuming the existence of the functions (put-coercion t1 t2 coercion-function) and (get-coercion t1 t2) to manage the coercion table, we can now extend apply-generic to make it try to manage coercion automatically: if the two parameters of a generic function are of different types, and no specific instance of the function is found for this signature, apply-generic tries to coerce the two parameters to be of the same type:
(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)))))))Then, let us examine the simplest case where a chain of super-type relations exist among all types in the system. For example, complex is a supertype of real is a supertype of rational is a supertype of integer. In this case, we can reduce the number of coercion functions required in the system.
Problems: