#:g1: MOPを活用するユーティリティを眺める(1): closer to mop/fare-mop

Posted 2013-12-19 15:00:00 GMT

(Metaobject Protocol(MOP) Advent Calendar 2013参加エントリ)

 Metaobject Protocol(MOP) Advent Calendar 2013 20日目です。

 MOP Advent Calendarも20日目ともなると、皆さんもMOP系のユーティリティを眺めたくなってきたかなと思います。
早速、Common Lispのライブラリ管理システムのquicklispに登録されているMOP系のユーティリティを眺めて行きましょう。

quicklispにはどんなものが登録されているのかな

 quicklispに登録のライブラリを"mop"で検索してみると、こんな感じです。

(ql-dist:system-apropos "mop")
;>>  #<SYSTEM closer-mop / closer-mop-20131111-git / quicklisp 2013-11-11>
;>>  #<SYSTEM fare-mop / fare-mop-20130615-git / quicklisp 2013-11-11>
;>>  #<SYSTEM hu.dwim.util.mop / hu.dwim.util-20131111-darcs / quicklisp 2013-11-11>
;>>  #<SYSTEM mop-utils / mop-utils-20120811-http / quicklisp 2013-11-11>
;>>  #<SYSTEM moptilities / moptilities-20110110-http / quicklisp 2013-11-11>
;>>  #<SYSTEM moptilities-test / moptilities-20110110-http / quicklisp 2013-11-11>
;>>  #<SYSTEM nst-mop-utils / nst-4.0.1 / quicklisp 2013-11-11>
;>>  #<SYSTEM xml-mop / xml-mop-20110418-git / quicklisp 2013-11-11>
;>>  
;=>  <no values>

mopを使ったプログラミングに使えそうなユーティリティライブラリが4つ、5つありそうですね。
適当に眺めて行きましょう。

Closer to MOP: closer-mop

 Closer to MOPは、AMOP(The Art of the Metaobject Protocol)の定義のコンパチパッケージです。
何故、このようなパッケージが必要になるのかというと、残念ながらMOPはCommon Lispの標準仕様には含まれなかった為で、各処理系は、AMOPを下敷きにしてはいるものの、それぞれ拡張していたり、仕様に準拠していなかったりします。
この非互換をカバーする目的で作られたライブラリです。
ちなみにCloser to MOPが何かの仕様の名前という訳ではありません。

fare-mop

 fare氏作のMOP系の機能を使ったユーティリティです。
ちょっとしたユーティリティ集なので全部眺めてみましょう。

collect-slots

 名前の通り、オブジェクトのスロットを集めるものです。束縛されていないスロットは無視されます。

(defclass foo ()
  ((x :initform '())
   (y :initform '())
   (z :initform '())))

(fare-mop:collect-slots (make-instance 'foo)) ;=> (X NIL Y NIL Z NIL) (fare-mop:collect-slots (make-instance 'foo) :slots '(x z)) ;=> (X NIL Z NIL)

simple-print-object

 通常インスタンスは、print-objectによって

(print (make-instance 'foo) t)
;=>  #<FOO {1018079513}>

のように印字されますが、スロットの中身も印字するようにしたものです。スロットの取得に上のcollect-slotsが使われていますが、Common Lispでは、印字するためのprint-objectもユーザーが拡張メソッドとなっているのでカスタマイズすることが可能です。

(fare-mop:simple-print-object (make-instance 'foo) t)
;>>  #<FOO (X NIL Z NIL)>
;=>  NIL

(fare-mop:simple-print-object (make-instance 'standard-generic-function) t) ;>> #<STANDARD-GENERIC-FUNCTION (:DEFINITION-SOURCE NIL SB-PCL::PLIST NIL ;>> :DOCUMENTATION NIL SB-PCL::INITIAL-METHODS NIL ;>> :NAME NIL SB-PCL::METHODS NIL :METHOD-CLASS ;>> #<STANDARD-CLASS STANDARD-METHOD> ;>> :METHOD-COMBINATION ;>> #<SB-PCL::STANDARD-METHOD-COMBINATION STANDARD NIL ;>> {1000649613}> ;>> :DECLARE NIL SB-PCL::ARG-INFO ;>> #S(SB-PCL::ARG-INFO ;>> :ARG-INFO-LAMBDA-LIST :NO-LAMBDA-LIST ;>> :ARG-INFO-PRECEDENCE NIL ;>> :ARG-INFO-METATYPES NIL ;>> :ARG-INFO-NUMBER-OPTIONAL NIL ;>> :ARG-INFO-KEY/REST-P NIL ;>> :ARG-INFO-KEYS NIL ;>> :GF-INFO-SIMPLE-ACCESSOR-TYPE NIL ;>> :GF-PRECOMPUTE-DFUN-AND-EMF-P NIL ;>> :GF-INFO-STATIC-C-A-M-EMF #S(SB-PCL::FAST-METHOD-CALL ;>> :FUNCTION # ;>> :PV NIL ;>> :NEXT-METHOD-CALL NIL ;>> :ARG-INFO (2)) ;>> :GF-INFO-C-A-M-EMF-STD-P T ;>> :GF-INFO-FAST-MF-P T) ;>> SB-PCL::DFUN-STATE NIL SB-PCL::%LOCK ;>> #<SB-THREAD:MUTEX "GF lock" (free)> ;>> SB-PCL::INFO-NEEDS-UPDATE NIL)> ;=> NIL (fare-mop:simple-print-object (make-instance 'standard-generic-function) t :slots '(:method-combination) :identity t) ;>> #<STANDARD-GENERIC-FUNCTION (:METHOD-COMBINATION ;>> #<SB-PCL::STANDARD-METHOD-COMBINATION STANDARD NIL ;>> {1000649613}>)> ;=> NIL

必要なスロットのみ表示させることも可能。
さらにsimple-print-object-mixinというものも定義されているので、好みに応じて、

(defclass pp (fare-mop:simple-print-object-mixin)
  ((x :initform 42)
   (y)
   (z)))

(defmethod fare-mop:slots-to-print ((obj pp)) '(x))

(make-instance 'pp) ;=> #<PP (X 42)>

というような利用方法もありです。

remake-object

 オブジェクトからスロットの内容を読み取り、その情報からmake-instanceするというものです。

(fare-mop:remake-object (make-instance 'pp))
;=>  #<PP (X 42)>

まとめ

 今回はCLOS MOPのユーティリティライブラリを少し眺めてみましたが、やはりユーティリティを眺めるのは、他の人の仕事を眺めているようで楽しいですね。
他にも数点あるので何度かに分けて眺めてみましょう。
…連休はもう少しがんばります。

comments powered by Disqus