Lispのオブジェクトの中には、決して変更してはならないものがあります。例えば、Lispの式 "aaa"
は文字列を返しますが、その内容を変更してはいけません。また、変更不可能なオブジェクトもあります。例えば、計算によって新しい数値を作成することはできますが、Lispには既存の数値の値を変更する操作は用意されていません。
その他のLispオブジェクトは可変であり、副作用を伴う破壊的操作によってその値を変更しても安全です。例えば、既存のマーカーを別の場所を指すように移動させることで、マーカーを変更できます。
数値は不変であり、すべてのマーカーは可変ですが、型によってはメンバの中に可変なものとそうでないものが混在しています。こうした型には、コンス、ベクトル、文字列などが含まれます。例えば、"cons" と (symbol-name
'cons) はいずれも変更すべきではない文字列を返しますが、(copy-sequence
"cons") と (make-string 3 ?a)
はいずれも、後続の aset の呼び出しを通じて変更可能な可変文字列を返します。
可変オブジェクトは、評価される式の一部である場合、可変ではなくなります。例えば:
(let* ((x (list 0.5))
(y (eval (list 'quote x))))
(setcar x 1.5) ;; The program should not do this.
y)
リスト (0.5) は作成時に可変であったが、eval に渡されたため、setcar
を介して変更されるべきではなかった。その逆は起こらない。変更されるべきでないオブジェクトが、その後可変になることはない。
プログラムが変更すべきでないオブジェクトを変更しようとした場合、その結果生じる動作は未定義となります。Lispインタプリタがエラーを通知する場合もあれば、クラッシュしたり、その他の予期せぬ挙動を示したりする場合もあります。3
プログラムの一部として類似した定数が現れる場合、Lispインタプリタは既存の定数やその構成要素を再利用することで、時間やメモリを節約することがあります。例えば、インタプリタが文字列リテラル
"abc" のインスタンスを1つだけ作成した場合は (eq "abc" "abc") が t を返し、2つのインスタンスを作成した場合は nil
を返します。Lispプログラムは、この最適化が適用されているかどうかにかかわらず動作するように記述する必要があります。
これは、Common LispやCなどの言語で定数に対して規定されている挙動であり、プログラムが不変オブジェクトを変更しようとした場合にインタプリタがエラーを通知する必要があるJavaScriptやPythonなどの言語とは異なります。理想的には、Emacs Lispインタプリタも後者の方向へ進化していくでしょう。