dlambda はもともと Common Lisp の伝統的なマクロで書かれているので、見た目は object マクロとは似ていません。ですが、dlambda を scheme の衛生的マクロで書くとそっくりです。(似たような機能なのでそりゃそうなんですが)
以前書いた衛生的マクロ版 dlambda をちょっと書き直して再掲するとこんなの。
(define-syntax dlambda (syntax-rules (else) ((_ (msg (arg ...) body ...) ...) (^ (key . args) (case key ((msg)(apply (^ (arg ...) body ...) args)) ... (else key)) ))))
使い方はこんな感じ。
(define counter (let1 count 0 (dlambda (inc (:optional (n 1))(inc! count n)) (dec (:optional (n 1))(dec! count n))))) (counter 'inc) ;; -> 1 (counter 'inc) ;; -> 2 (counter 'dec 10) ;; -> -6
で、これが プログラミング Gauche に載ってる object マクロ。(P.270)
(define-syntax object (syntax-rules () [(object (ivar ...) (method (arg ...) body ...) ...) (lambda (ivar ...) (lambda (message . args) (case message [(method) (apply (lambda (arg ...) body ...) args)] ...)))] ))
で、こんな感じでオブジェクトっぽいものを作るのに使える。
(define make-count (let1 count 0 (object () (inc (:optional (n 1))(inc! count n)) (dec (:optional (n 1))(dec! count n))))) (define counter (make-count)) (counter 'inc) ;; -> 1 (counter 'inc 10) ;; -> 11 (counter 'dec) ;; -> 10
メソッド名はキーワードでも良いかも。こんな風に。
(define make-count (let1 count 0 (object (:optional step) (:inc (:optional (n step))(inc! count n)) (:dec (:optional (n step))(dec! count n))))) (define counter (make-count 3)) (counter :inc) ;; -> 3 (counter :inc 1) ;; -> 4object マクロの方が初期値を取れる分良いかも。
プログラミング Gauche を読んでたら object マクロが目にとまりました -> LOL の dlambda に似てるなー -> ただそれだけです。
ついでに LOL に載ってる Common Lisp 版の dlambda 。
(defmacro! dlambda (&rest ds) `(lambda (&rest ,g!args) (case (car ,g!args) ,@(mapcar (lambda (d) `(,(if (eq t (car d)) t (list (car d))) (apply (lambda ,@(cdr d)) ,(if (eq t (car d)) g!args `(cdr ,g!args))))) ds))))
LOL の dlambda には defmacro! が必要なので、実際に使うには以下のようになる。伝統的マクロは確かに強力だけど、syntax-rules だと簡単に書けるものもあるので、syntax-rules も結構よくね?
(defun flatten (x) (labels ((rec (x acc) (cond ((null x) acc) ((atom x) (cons x acc)) (t (rec (car x) (rec (cdr x) acc)))))) (rec x nil))) (defun mkstr (&rest args) (with-output-to-string (s) (dolist (a args) (princ a s)))) (defun symb (&rest args) (values (intern (apply #'mkstr args)))) (defun g!-symbol-p (s) (and (symbolp s) (> (length (symbol-name s)) 2) (string= (symbol-name s) "G!" :start1 0 :end1 2))) (defmacro defmacro/g! (name args &rest body) (let ((syms (remove-duplicates (remove-if-not #'g!-symbol-p (flatten body))))) `(defmacro ,name ,args (let ,(mapcar (lambda (s) `(,s (gensym ,(subseq (symbol-name s) 2)))) syms) ,@body)))) (defun o!-symbol-p (s) (and (symbolp s) (> (length (symbol-name s)) 2) (string= (symbol-name s) "O!" :start1 0 :end1 2))) (defun o!-symbol-to-g!-symbol (s) (symb "G!" (subseq (symbol-name s) 2))) (defmacro defmacro! (name args &rest body) (let* ((os (remove-if-not #'o!-symbol-p args)) (gs (mapcar #'o!-symbol-to-g!-symbol os))) `(defmacro/g! ,name ,args `(let ,(mapcar #'list (list ,@gs) (list ,@os)) ,(progn ,@body))))) (defmacro! dlambda (&rest ds) `(lambda (&rest ,g!args) (case (car ,g!args) ,@(mapcar (lambda (d) `(,(if (eq t (car d)) t (list (car d))) (apply (lambda ,@(cdr d)) ,(if (eq t (car d)) g!args `(cdr ,g!args))))) ds))))
0 件のコメント:
コメントを投稿