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
展開が可能になります。例えば、呼び出すための単純なセッター関数がない場合や、関数はあるものの 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)が後で定義される可能性があるため、これはエラーとはなりません。