#:g1: マクロ展開でもメソッドコンビネーション

Posted 2018-12-18 15:32:33 GMT

Lisp メソッドコンビネーション Advent Calendar 2018 18日目 》

メソッドコンビネーションでD風の契約プログラミングの続きを書こうと思いましたが、準備が間に合わないので、思い付きでマクロでもメソッドコンビネーションというネタを書きます。

ANSI Common LispにはAdviceはありませんが、処理系拡張でdefadviceのようなものでadviceを定義することが可能だったりします。
adviceは関数だけでなく、マクロやメソッドにも付けられたりしますが、LispWorksでマクロのadvice定義を利用した例は、こんな感じです。

(defmacro mydefun (name (&rest args) &body body)
  `(defun ,name (,@args) ,@body))

(defadvice (mydefun inline :around) (form env) (destructuring-bind (def name args &body body) form (declare (ignore def args body)) `(progn (declaim (inline ,name)) ,(call-next-advice form env))))

(mydefun foo (n) n) ;===> (PROGN (DECLAIM (INLINE FOO)) (DEFUN FOO (N) N))

標準では、マクロの展開関数は関数なので拡張できないのですが、メソッドにして、好きなメソッドコンビネーションを指定してしまえば良い!ということで、マクロ展開メソッドでメソッドコンビネーションです。

雑ですが、こんな感じに書いてみました。

(defmacro defmacro* (name-&-method-qualifiers (&rest args) &body body)
  (destructuring-bind (name &rest method-qualifiers)
                      (if (consp name-&-method-qualifiers) 
                          name-&-method-qualifiers
                          (list name-&-method-qualifiers))
    (let ((form (gensym "form-"))
          (env (gensym "env-"))
          (xn (or (get name :expander)
                  (setf (get name :expander) 
                        (make-symbol (string name))))))
      `(eval-when (:compile-toplevel :load-toplevel :execute)
         (defmethod ,xn ,@method-qualifiers (,form ,env)
           (macrolet ((call-next-expander (&rest args)
                        `(call-next-method ,@args)))
             (destructuring-bind (,name ,@args)
                                 ,form
               (declare (ignore ,name))
               ,@body)))
         (setf (macro-function ',name)
               #',xn)))))

deという名前でdefunのエイリアス的なものを定義してみる

(defmacro* de (name (&rest args) &body body)
  `(defun ,name (,@args) ,@body))

(de foo (n) (list n n)) ===> (defun foo (n) (list n n))

de周囲をeval-whenで囲みたくなった

(defmacro* (de :around) (name (&rest args) &body body)
  `(eval-when (:compile-toplevel :load-toplevel :execute)
     ,(call-next-expander)))

(de foo (n) (list n n))
===>
(eval-when (:compile-toplevel :load-toplevel :execute)
  (defun foo (n) (list n n)))

deの定義は全部無効にしたくなった

(defmacro* (de :around) (name (&rest args) &body body)
  nil)

(de foo (n) (list n n)) ===> nil

雑なのでメソッドの良さを全然活かせてないですが、使いようによっては便利なこともあるかも、と少しだけ思いました。


HTML generated by 3bmd in LispWorks 7.0.0

comments powered by Disqus