#:g1: allocate-instanceとは

Posted 2020-11-30 16:26:06 GMT

allocate-instance Advent Calendar 2020 1日目の記事です。

Lisp系のニッチなことをテーマにアドベントカレンダーを開催したりしなかったりしていますが、今年は、allocate-instanceをテーマにしてみることにしました。

allocate-instance とは

所謂Common Lisp系のオブジェクトシステム(CLOS)のインスタンスを確保する部分ですが、AMOPでいうとInstance Structure Protocol(以降ISP)辺りの話題となります。
ISPアドベントカレンダーという名前でも良かったのですが、allocate-instanceの方がわかりやすいかなと思ってこっちの名前にしましたが、どっちにしろ参加者が集まりそうにないので五十歩百歩かもしれません。

それはさておき、AMOPのISPの説明を読むと判るように、どちらかといえばスロットのアクセスを基点として、インスタンスの物理的配置までカスタマイズするための構成が説明されています。

今回は、allocate-instanceを基点に考えてみたら面白いかもしれないというチャレンジですが、STKlosのVirtual Slots等は、スロットアクセスを基点に計算をしたりするので、ISP的にはallocate-instanceがなくても良かったりすることもあります。

ちなみにVirtual Slotsの応用は下記の記事等が参考になります。

他、複数オブジェクトをまとめて扱うような操作を実現するのもISPのカスタマイズの一種かなと思います。

allocate-instance の拡張について

スロットアクセスからデータの物理配置までの間のプロトコルをカスタマイズするのに、スロットアクセス側に重きをおく上記Virtual Slotのようなものもあれば、逆にデータ構造側に工夫をしてスロットアクセス側はそれほどカスタマイズしないという構成も考えられます。

古典的な書籍であるThe CLOS PerspectiveにもMOPの話が出てきますが、知識表現のように項目が非常に多いけれど、それぞれの利用頻度は非常に低かったりする場合は、スロットを配列にするのではなく、ハッシュテーブルのようなものの方がメモリ効率が良いだろうというアイデアの一つとして、allocate-instance のカスタマイズが示唆されたりしています。

しかし、この論文でも実際のカスタマイズの詳細については触れられておらず、類似の事例紹介でも大抵はallocate-instance内部で確保するデータ構造をカスタマイズするのではなく、フックを掛けて別のデータオブジェクトにリダイレクトするようなものが殆どのようです。

フックを掛けて別のデータオブジェクトにリダイレクトするようなものとしては文末のコードのような構成が考えられます。 この場合、allocate-instanceをカスタマイズしてはいますが、デフォルトで確保したものは捨てて、別途確保しているという点で無駄なところがあります。

また、類似のものに、オブジェクトのシリアライズやORマッパーの応用がありますが、これらも確保するデータ構造はノーマルなもので、確保時のフックが眼目になります。

今回のアドベントカレンダーは、このように迂回されることが多いallocate-instanceが確保するデータ構造について正面から向き合ってみようというのが大体の主旨です。

(defpackage "e79ba511-fd06-57f8-9038-132961fa529b" (:use :c2cl))

(in-package "e79ba511-fd06-57f8-9038-132961fa529b")

(defvar *hash-slots* (make-hash-table))

(defclass hash-table-representation-class (standard-class) ())

(defmethod allocate-instance ((c hash-table-representation-class) &rest args) (let ((inst (call-next-method))) (setf (gethash inst *hash-slots*) (make-hash-table)) inst))

(defmethod slot-value-using-class ((c hash-table-representation-class) inst (slot-name slot-definition)) (gethash slot-name (gethash inst *hash-slots*) (slot-definition-initform slot-name)))

(defmethod (setf slot-value-using-class) (newvalue (c hash-table-representation-class) inst (slot-name slot-definition)) (setf (gethash slot-name (gethash inst *hash-slots*) (slot-definition-initform slot-name)) newvalue))

(defclass foo () ((a :initform 0 :accessor foo-a) (b :initform 0 :accessor foo-b) (c :initform 0 :accessor foo-c)) (:metaclass hash-table-representation-class))

(defparameter *the-foo* (make-instance 'foo))

(list (foo-a *the-foo*) (foo-b *the-foo*) (foo-c *the-foo*))(0 0 0)

(setf (foo-a *the-foo*) 42 (foo-b *the-foo*) 43) ;=> 43 (list (foo-a *the-foo*) (foo-b *the-foo*) (foo-c *the-foo*))(42 43 0)


HTML generated by 3bmd in LispWorks 7.0.0

comments powered by Disqus