#:g1: setfとメソッドコンビネーションについて掘り下げる

Posted 2018-12-22 10:49:25 GMT

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

これまで、メソッドコンビネーションで19記事、setfで17記事程書いてきましたが、もう書くことがないです。

setfでメソッドコンビネーション活用について書けば、一挙両得なのでは!、と考えこのネタでひとつ考えてみたいと思います。

setfとメソッドコンビネーション

まず、setfでメソッドコンビネーションの活用が成立する為には、どう考えてもスロットのライタである必要があります。

(defclass foo ()
  ((x :initarg :x :accessor x :writer (setf x/))))

(defvar *foo* (make-instance 'foo :x 42))

(x *foo*) → 42

(setf (x/ *foo*) 69) (x *foo*) → 69

クラスのスロット定義で、スロットに対して複数のアクセサ総称関数が定義可能ですが、setfが関係するのは、:accessorか、:writterオプションになります。

defclassで定義するアクセサは、デフォルトではstandardになることが規格で定まっています。

そのため、メソッドコンビネーションを指定したい場合は、defclassでアクセサは作らず、別途定義することになると思われます。

試してみましょう

(defgeneric (setf x-list) (val o)
  (:method-combination list))

(defmethod (setf x-list) list (val (o foo)) (setf (c2mop:slot-value-using-class (class-of o) o 'x) val) val)

(defclass bar (foo) ())

(defmethod (setf x-list) list (val (o bar)) (setf (c2mop:slot-value-using-class (class-of o) o 'x) val) val)

(defvar *bar* (make-instance 'bar :x 42))

(x *bar*) → 42

(setf (x-list *bar*) 69)(69 69)

(x *bar*) → 69

上記では、listメソッドコンビネーションを実行し代入の返り値をリストにして返しています。

これは、standardメソッドコンビネーション以外で、役に立ちそうな例を考えるのが難しい……。

setfに関しては、実行前後のフック(daemonメソッドコンビネーション)か、乗っ取り(around)があれば十分そうですね(即ちstandardメソッドコンビネーション)

通常は、同一の値を同一スロットに複数回書き込む挙動は求められていないということからもprogn等で有用なことはできなさそうです。

MOPで

(defclass foo ()
  ((x :initarg :x :accessor x :accessor-method-combination progn)))

のように書けるようにすることも検討してみましたが、これも微妙

一応役に立つ例も紹介

メソッドコンビネーションのカスタマイズの方向で書いてしまいましたが、standardsetfの組み合わせであれば、値の設定前後のフックとして有用な使い方が可能です。

(defmethod (setf x/) :after (val (foo foo))
  (write-line "こんにちは"))

(setf (x/ *foo*) 42) ▻ こんにちは → 42

(defmethod (setf x/) :before (val (foo foo)) (check-type val integer))

(setf (x/ *foo*) 'foo) >>> error

(defmethod (setf x/) :around (val (foo foo)) nil)

(setf (x/ *foo*) 'foo) → nil

まとめ

リーダーもを含めたアクセサ全般と、メソッドコンビネーションの組み合わせでは何か有用なことも可能かもしれませんが、setfのライタ限定となると、デフォルトのstandardが必要にして十分だった、ということがわかりました。


HTML generated by 3bmd in LispWorks 7.0.0

comments powered by Disqus