SICP 問題 2.81(同型の強制型変換は必要か?)

問題

Louis Reasonerは、apply-genericは引数が既に同じ型を持っていても、互いの型へ強制変換を試みるべきだと考えた。そこで彼が考えるには、上に示した強制変換の表に、各型の引数を自分の型へ、「強制変換」させる手続きを置く必要がある。例えば、上に示したscheme-number->complex型変換に加え、彼は

(define (scheme-number->scheme-number n) n)
(define (complex->complex z) z)
(put-coercion 'scheme-number 'scheme-number scheme-number->scheme-number)
(put-coercion 'complex 'complex complex->complex)

としようとする。

a.

Louisの強制型変換手続きが設定されると、apply-genericが型scheme-numberの二つの引数や型complexの二つの引数で、これらの型で表に見つからない手続きに対して呼び出されると、何が起きるか。例えば汎用べき乗演算

(define (exp x y) (apply-generic 'exp x y))

が定義してあり、scheme数パッケージのべき乗の手続き

;次はScheme数パッケージへ追加する。
(put 'exp '(scheme-number scheme-number)
     (lambda (x y) (tag (expt x y)))) ;基本手続きexptを使う

はあるが、他のパッケージにはないとしよう。引数に二つの複素数を持ってexpを呼び出すと何が起きるか

b.

同じ型の引数の強制型変換について何かすべきだというLouisは正しいか。それともこのままapply-genericは正しく働くか。

c.

二つの引数が同じ型を持っていれば、強制型変換を試みないように、apply-genericを修正せよ。

解答

put-coercionとかget-coercionの実装がアレなので脳内だけでやっちゃいたいなぁ。。動きとかわかんねぇし。

a.

メソッドがないよと怒られるのかと思ったら、なんか無限ループする感じ?
(やっぱりput-coercion/get-coercionを実装して試してみるべきか〜?)

b.

無限ループするなら、定義しちゃいけないんじゃない?


c.

こんな感じ?

(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 (= (length args) 2)
	    (let* ((type1 (car type-tags))
		   (type2 (cadr type-tags)))
	      ;同じ型の場合は強制型変換をしないようにする。
	      (if (eq? type1 type2)
		  (error "No method for these types"
                         (list op type-tags))
		  ;異なる型だった場合は変換手続きを取得する
		  (let* ((a1 (car args))
                         (a2 (cadr args))
                         (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))))))