#:g1: incongruent-methodsの紹介

Posted 2014-02-13 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の45日目です。

incongruent-methodsとはなにか

 incongruent-methodsは、Peter von Etter氏作の引数の数によって呼び出されるメソッドが変ったり、パッケージを横断した名前でメソッドが呼び出せたりする変わり種メソッドのライブラリです。

パッケージ情報

パッケージ名incongruent-methods
Quicklisp
プロジェクトページpve1/incongruent-methods · GitHub
Quickdocshttp://quickdocs.org/incongruent-methods

インストール方法

(ql:quickload :incongruent-methods)

試してみる

 どんな関数があるかは、Quickdocsで確認できます。

 incongruentとは、「不調和」や、「一致しない」ことのようですが、CLOSの既存のメソッドの動作とは一風違ったことを表しているのでしょうか。
主な機能は下記の3種ですが、どれもちょっと変っています。

define-incongruent-method

 Common Lispの関数/メソッドはPrologのように引数の数に応じてディスパッチしたりはしませんが、そんな感じの動作をメソッドで実現したものです。

(defclass molito () ())

(define-incongruent-method haman ((x molito)) :haman)

(define-incongruent-method haman (x y) (list :haman x y))

のような定義があったとすると、

(list (haman (make-instance 'molito))
      (haman 1 2))
;=>  (:HAMAN (:HAMAN 1 2))

のように引数の数によって動作が変わります。

 この切り替えの仕組みですが、ベースはapplyで可変引数を受けるようになっていて、その上で、コンパイラマクロによって、引数の数に合せたメソッドを呼び出すようになっているようです。

(swank-backend:compiler-macroexpand '(haman 1))
;=>  (HAMAN/1 1)
;    T

(swank-backend:compiler-macroexpand '(haman 1 2)) ;=> (HAMAN/2 1 2) ; T

(swank-backend:compiler-macroexpand '(haman 1 2 3 4 5 6)) ;=> (HAMAN/6 1 2 3 4 5 6) ; T

 なかなかのアイデアです。

define-shared-method

 incongruent-methodに更に機能を追加したもので、名前が同一のシンボル名ならば同一のメソッドという所まで拡大したものです。

(define-shared-method :dialma (x y)
  :x-y)

(define-shared-method :dialma ((x integer)) :integer)

(define-shared-method dialma ((x symbol)) :symbol)

(define-shared-method #:dialma ((x (eql 0))) :zero)

(fboundp :dialma) ;=> NIL

(fboundp 'dialma) ;=> NIL

(list (imcall :dialma 8) (imcall '#:dialma 'symbol) (imcall 'dialma 0) (imcall 'dialma 1 2)) ;=> (:INTEGER :SYMBOL :ZERO :X-Y)

最早文字列で呼び出せても良さそうですが、文字列では呼び出せない様子。

define-class

CLOSは特定のクラス一つに属することはないものの、メソッドの第一引数は主なクラスになることが多かったりしますが、そういう用途に特化したクラス定義用のマクロです。

(define-class greeting ()
  ((message :accessor message :initform nil :initarg :message)
   (some-slot :accessor some-slot :initform nil))

(greet () (message me))

(greet ((n integer)) (dotimes (m n) (greet me)))

(greet ((n (eql 42))) (some-slot me))

((setf greet) (new) (setf (message me) new)))

(let ((i (make-instance 'greeting :message "Hello"))) (greet i)) ;=> "Hello" (let ((i (make-instance 'greeting :message "Hello"))) (setf (greet i) "Hi") (greet i)) ;=> "Hi"

定義されるメソッドは通常のものかと思いきややはりincongruent-methodになっています。

まとめ

 今回は、incongruent-methodsを紹介してみました。
MOPを使っているのかなと思って中身を眺めてみましたが、殆どがマクロ/コンパイラマクロでの実装でした。
良く考えてみれば、コンパイル時/マクロ展開時に解決できる所なので、当然といえば当然ですね。

comments powered by Disqus