with-slots (2)
引き続き、define-syntax を使った with-slots マクロの定義について。昨日の shiro さんのコメントを受けて修正しつつ、Common Lisp の with-slots の仕様に近づくように修正。以下のサンプルコードを用意。
(define-class <person> () ((name :init-keyword :name) (gender :init-keyword :gender) (age :init-keyword :age))) (define-method initialize ((p <person>) initargs) (next-method) (format #t "[~a] is born.~%" (slot-ref p 'name))) (define (example-1) (with-slots (name gender age) (make <person> :name "Hoge" :gender 'male :age 20) (format #t "~a ~a ~d~%" name gender age))) (define (example-2) (with-slots ((n1 name) (g1 gender)) (make <person> :name "Hoge" :gender 'male) (with-slots ((n2 name) (g2 gender)) (make <person> :name "Fuga" :gender 'female) (format #t "~a-~a, ~a-~a~%" n1 g1 n2 g2)))) (define (example-3) (with-slots ((n name) age) (make <person> :name "Hoge" :age 20) (format #t "~a is ~d years old.~%" n age)))
example-1 は昨日のものと同じ。ただしインスタンス生成は一度だけ行われるかどうかチェック。example-2 は Common Lisp の with-slots のように、束縛するシンボルも指定できるようにしたもの。example-3 は example-1 と example-2 の混合。ただし今回の with-slots ではエラーになってしまうため、今後の課題。
example-1, 2 両方に対応できるよう with-slots マクロはつぎようにしてみた。基本は example-2 に対応する形としておいて、example-1 のようにシンボルが省略された形で使われたときは、スロット名をそのままシンボルとして扱うようにして内部的に with-slots を使うように。
(define-syntax with-slots (syntax-rules () ;; matches with examle-2 ((_ ((var slot) ...) expr body ...) (let ((obj expr)) (let ((var (slot-ref obj 'slot)) ...) body ...))) ;; matches with example-1 ((_ (slot ...) expr body ...) (with-slots ((slot slot) ...) expr body ...))))
これでテスト。
gosh> (example-1) [Hoge] is born. Hoge male 20 #<undef> gosh> (example-2) [Hoge] is born. [Fuga] is born. Hoge-male, Fuga-female #<undef>
正しく動作しているようだ。先に書いたように example-3 はまだ対応できていない。
gosh> (example-3) [Hoge] is born. *** ERROR: object of class #<class <person>> doesn't have such slot: (n name) Stack Trace: _______________________________________ 0 (slot-ref obj '(n name)) [unknown location]
もう一工夫必要そうだ。ちなみに with-slots は Emacs の場合、
(put 'with-slots 'scheme-indent-function 2)
define-syntax は直感的で良いし、パターンマッチングを採用したあたりが面白い。マッチングって以前少しだけ勉強した OCaml を思い出させる。ちなみに、Ocaml は面白いなと思ったのだけど、Scheme を使っているときのような、ノリというかツボというか、うまく表現できない感覚が得られず、結局今は使ってない。計算がむちゃくちゃ速いのはすごくいいなと思うんだけど。