#:g1: Flavorsのメソッドコンビネーションを眺めたり再現してみよう: :daemon-with-override

Posted 2018-12-12 13:48:13 GMT

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

前回に引き続き、メソッドコンビネーション元祖のFlavorsに標準装備されていたメソッドコンビネーションを眺めたり再現してみたりしようと思います。

本当は、:pass-onの再現がしたいのですが、色々間に合ってないので、:daemon-with-overrideの再現でお茶を濁したいと思います。

:daemon-with-overrideは、Common Lispでいうと、:aroundなしのstandardメソッドコンビネーションの前に門番のメソッドがいるような感じですが、こんな感じに定義できます。

(define-method-combination :daemon-with-override ()
  ((before (:before))
   (after (:after))
   (primary ())
   (override (:override)))
  `(or 
    ,@(and override `((call-method ,(car override))))
    (multiple-value-prog1 
        (progn
          ,@(loop :for m :in before :collect `(call-method ,m))
          (call-method ,(car primary) ,(cdr primary)))
      ,@(loop :for m :in (reverse after) :collect `(call-method ,m)))))

細かいオプションは詰めてないですが、これくらいの定義なら空で書けるようになりました。
難解だと思っていたdefine-method-combination構文ですが、10個位定義を書けば、そこそこ覚えられそうですね。

では、使ってみましょう。

(defgeneric foo (x)
  (:method-combination :daemon-with-override))

(defmethod foo ((x T)) x)

(defmethod foo :before ((x T)) (print :before))

(defmethod foo :after ((x T)) (print :after))

(defmethod foo ((x rational)) `(rational ,(call-next-method)))

(defmethod foo ((x integer)) `(integer ,(call-next-method)))

(defmethod foo :override ((x number)) (evenp x))

(foo 8)
→ t 

(foo 9) ▻ :before ▻ :after → (integer (rational 9))

メソッドコンビネーションを展開してみるとこんな感じになります。

(mc-expand #'foo
           :daemon-with-override
           nil
           1)(or (call-method #<standard-method foo (:override) (number) 40201044DB>)
    (multiple-value-prog1
        (progn
          (call-method #<standard-method foo (:before) (t) 402045306B>)
          (call-method
           #<standard-method foo nil (integer) 4130468943>
           (#<standard-method foo nil (rational) 413046892B>
            #<standard-method foo nil (t) 413046961B>)))
      (call-method #<standard-method foo (:after) (t) 4020462243>)))

まとめ

:daemonメソッドコンビネーションに門番がついているというパターンなので、Common Lispでは、:aroundを使えば実現できるパターンですね。

数あるパターンが、Common Lispでは、:aroundとして集約されたという流れが想像できます。


HTML generated by 3bmd in LispWorks 7.0.0

comments powered by Disqus