setf
フォームこのセクションでは、setf
が操作できる新たなフォームの定義方法を説明します。
このマクロは単純なケースでsetf
メソッドを簡単に定義することを可能にする。nameは関数、マクロ、スペシャルフォームの名前。nameがそれを更新するための対応するsetter関数をもつなら、このマクロを使用できる(たとえば(gv-define-simple-setter
car setcar)
)。
このマクロは以下のフォームの呼び出しを
(setf (name args…) value)
以下のように変換する。
(setter args… value)
このようなsetf
の呼び出しはvalueをリターンするとドキュメントされている。これはcar
とsetcar
では問題はない。setcar
はそれがセットする値をリターンするからである。setter関数がvalueをリターンしない場合には、gv-define-simple-setter
のfix-return引数に、非nil
値を使用すること。これは以下のようなものに展開される
(let ((temp value)) (setter args… temp) temp)
これで正しい結果がリターンされることが保証される。
このマクロは上述のフォームより複雑なsetf
展開を可能にする。たとえば呼び出すべきシンプルなsetter関数が存在しないときや、もしそれが存在してもplaceフォームとは異なる引数を要求するなら、このフォームを使う必要があるかもしれない。
このマクロは最初にsetf
引数フォーム(value
args…)
をarglistにバインドして、その後bodyを実行することによって、フォーム(setf
(name args…)
value)
を展開する。bodyは割り当てを行うLispフォームをリターンして、最終的にはセットされた値をリターンすること。以下はこのマクロの使用例:
(gv-define-setter caar (val x) `(setcar (car ,x) ,val))
展開をより詳細に制御するためにgv-define-expander
マクロが使用できる。たとえばセット可能なsubstring
は以下の方法で実装できる:
(gv-define-expander substring (lambda (do place from &optional to) (gv-letplace (getter setter) place (macroexp-let2* (from to) (funcall do `(substring ,getter ,from ,to) (lambda (v) (macroexp-let2* (v) `(progn ,(funcall setter `(cl--set-substring ,getter ,from ,to ,v)) ,v))))))))
マクロgv-letplace
はsetf
のような処理を行うマクロを定義するのに有用。たとえばCommon
Lispのincf
マクロは以下の方法で実装できる:
(defmacro incf (place &optional n) (gv-letplace (getter setter) place (macroexp-let2* ((v (or n 1))) (funcall setter `(+ ,v ,getter)))))
getterはplaceの値をリターンするコピー可能な式にバインドされる。setterは式vを受け取り、placeにvをセットする新たな式をリターンする関数にセットされる。bodyはgetterとsetterを介してplaceを操作するEmacs Lisp式をリターンすること。
詳細はgv.elのソースファイルを参照。
この関数はバイトコンパイラーにジェネリック変数obsolete-nameが陳腐化していると警告させる。current-nameがシンボルなら、obsolete-nameのかわりにcurrent-nameを使用するよう告げるメッセージにより警告が行われる。current-nameが文字列なら、それはメッセージであること。whenはその変数が最初に陳腐化するのがいつかを示す文字列(通常はバージョン番号文字列)。
Common Lispに関する注意: Common Lispは関数としての
setf
、すなわち関数名がシンボルではなくリスト(setf name)
であるようなsetf
関数の挙動を指定するために別の方法を定義する。たとえば(defun (setf foo) …)
は、setf
がfoo
に適用されるときに使用される関数を定義する。Emacsはこれをサポートしない。適切な展開が定義されていないフォームにsetf
を使用するとコンパイル時エラーとなる。Common Lispでは後で関数(setf func)
が定義されるのでエラーにならない。