SICP-2.82 solution
(define (apply-generic op . args)
(let* ((type-tags (map type-tag args))
(proc (get op type-tags)))
(if proc
(apply proc (map contents args))
; if no operation found, we try to coerce the arguments to the type of
; each one of them, starting from the first
(let* ((get-coercion-procs
; get-coercion-procs gives a list of coercion procedures
; targeting one specific type, if possible
(lambda (target-type)
(map
(lambda (arg-type)
(if (eq? arg-type target-type)
; if the types are the same, return the identity function
identity
; if not, get the coercion function. can be null
(get-coercion arg-type target-type)))
type-tags))))
(define (type-tags-iter type-tags)
(if (not (empty? type-tags))
(let (coercion-procs (get-coercion-procs (car type-tags)))
(if (not (member? null coercion-procs))
; successful coercion
(apply-generic op (map apply coercion-procs args))
; failed to coerce everything. next round
(type-tags-iter (cdr type-tags))))
; if type-tags is empty, we have tried the type of the last argument
(error "No method for these types"
(list op type-tags))))
(type-tags-iter type-tags)))))
This method is not general enough when the arguments do have a common ancestor but the ancestor type does not show up in the arguments, thus we can miss it.
An example can be (triangle kite) in figure 2.26. They can both be coerced to polygon but we'll miss it.