変更されるべきではないLispオブジェクトがいくつかあります。たとえばLisp式"aaa"
では文字列を生成しますが、そのコンテンツを変更するべきではありません。いくつかのオブジェクトは変更できません。たとえばある数値を計算して新たな数値を作成できたとしても、Lispは既存の数値を変更する操作を提供しません。
その他のLispオブジェクトはmutable(可変)オブジェクトで、副作用をともなう破壊的な操作を通じて値を変更しても安全です。たとえばマーカーを別のポイントを指すマーカーに移動することにより、既存のマーカーを変更することができます。
数値は変更不可でマーカーはすべてmutableだとしても、mutableと非mutableのメンバーをもつタイプがいくつかあります。これらのタイプにはコンス、ベクター、文字列が含まれます。たとえば"cons"
と(symbol-name
'cons)
は変更するべきではない文字列を生成しますが、(copy-sequence
"cons")
と(make-string 3
?a)
は後からaset
を呼び出すことを通じて変更可能なmutable文字列を生成します。
mutableオブジェクトは評価される式の一部となったときにmutableであることを止めます。たとえば:
(let* ((x (list 0.5)) (y (eval (list 'quote x)))) (setcar x 1.5) ;; プログラムはこれを行うべきではない y)
作成時にリスト(0.5)
がmutableでも、それはeval
に与えられたのでsetcar
を通じて変更するべきではありません。変更するべきではないオブジェクトが後からmutableになることは決してないので逆はあり得ません。
変更するべきではないオブジェクトの変更をプログラムが試みた際の動作は未定義です。Lispインタープリターがエラーをシグナルするかもしれず、クラッシュしたり他の方法で予測不能な振る舞いを引き起こすかもしれません2。
プログラムの一部として類似した定数が出現する際には、既存の定数やそのコンポーネントの再利用によってLispが時間やスペースを節約できるかもしれません。たとえば(eq
"abc"
"abc")
はインタープリターが文字列リテラル"abc"
の1つのインスタンスを作成したらt
、2つのインスタンスを作成したらnil
をリターンします。したがってこの最適化使用の有無に関わらず機能するようにLispプログラムを記述する必要があります。
これはCommon LispやCのような言語が定数にたいして指定する挙動であり、プログラムによるimmutableオブジェクト変更の試みにエラーのシグナルを要求するJavaScriptやPythonのようなインタープリターとは異なる。理想的にはEmacs Lispインタープリターは後者を目指して進化するであろう。