1791
defstructのスロットに:read-onlyオプションを付けますが、SBCL等ではスロットに書き込めてしまいます。
他の処理系では、セッターを定義しないことで書き込みをしにくくしているようですが、 なにかもっと良い方法はないのでしょうか。

```

    (defstruct foo (x nil :read-only t) y z)
    
    
    (setf (foo-x (make-foo :x 9)) 8)
    ;sbcl =>  8
    ;!> allegro: `(SETF FOO-X)' is not fbound
    ;!> ecl: The function (SETF FOO-X) is undefined.
    ;!> clisp: FUNCALL: undefined function |(SETF COMMON-LISP-USER:FOO-X)|
    
    
    ;; ※ANSI CL仕様外
    (setf (slot-value (make-foo :x 9) 'x) 8)
    ;=>  8
    
```

エラーを出すようセッターを定義する位しかないのでしょうか。

```

    (defun (setf foo-x) (val foo)
      (declare (ignore val))
      (error "~S: attempt to write a readonly slot: X" foo))
    
    
    ;; ※ANSI CL仕様外
    (defmethod (setf c2mop:slot-value-using-class)
               (val
                (class (eql (find-class 'foo)))
                (obj foo)
                (slotd
                 (eql
                  (find 'x (c2mop:class-slots (find-class 'foo))
                   :key #'c2mop:slot-definition-name))))
      (error "~S: attempt to write a readonly slot: X" obj))
    
```
CC BY-SA 3.0
2014-12-16T07:26:55.753
1703
2014-12-16T15:12:29.350
2014-12-16T08:24:35.213
440
3510
question
lisp common-lisp
Common Lispのdefstructのスロットの:read-onlyオプションについて
sbcl1.2.1では治っているようです。

```

    ; SLIME 2014-08-01
    CL-USER> (defstruct foo (x nil :read-only t) y z)
    FOO
    CL-USER> (defvar foo (make-foo :x 9))
    FOO
    CL-USER> (setf (foo-x foo) 8)
    ; in: SETF (FOO-X FOO)
    ;     (FUNCALL #'(SETF FOO-X) #:NEW1940 #:FOO1941)
    ; ==>
    ;   (SB-C::%FUNCALL #'(SETF FOO-X) #:NEW1940 #:FOO1941)
    ; 
    ; caught STYLE-WARNING:
    ;   undefined function: (SETF FOO-X)
    ; 
    ; compilation unit finished
    ;   Undefined function:
    ;     (SETF FOO-X)
    ;   caught 1 STYLE-WARNING condition
    ; Evaluation aborted on #<UNDEFINED-FUNCTION (SETF FOO-X) {1004B1EF03}>.
    CL-USER> (lisp-implementation-version)
    "1.2.1"
    CL-USER> (lisp-implementation-type)
    "SBCL"
    
```

ただし、スロット直アクセスは出来るようです。

```

    CL-USER> (setf (slot-value foo 'x) 9)
    9
    
```

これを防ぐには、スロット名をインターンされていないシンボル `#:x` にしましょう。
どのパッケージにも属さないシンボルなので、後の参照を不可能にすることが出来ます。

```

    CL-USER> (defstruct bar (#:x nil :read-only t) y z)
    BAR
    CL-USER> (defvar bar (make-bar :x 9))
    BAR
    CL-USER> (setf (slot-value bar '#:x) 7)
    ; Evaluation aborted on #<SIMPLE-ERROR "~@<When attempting to ~A, the slot ~S is missing from the ~
    CL-USER> (setf (slot-value bar 'x) 7)
    ; Evaluation aborted on #<SIMPLE-ERROR "~@<When attempting to ~A, the slot ~S is missing from the ~
              object ~S.~@:>" {1005C844E3}>.
    
```

一方、アクセサ名は現在のパッケージにインターンされるので、参照することが出来ます。
このことは、<http://www.lispworks.com/documentation/HyperSpec/Body/m_defstr.htm> の
:conc-name セクションにて明記されています。

```

    CL-USER> (bar-x bar)
    9
    
```
CC BY-SA 3.0
2014-12-16T15:04:01.980
1791
2014-12-16T15:12:29.350
2014-12-16T15:12:29.350
4051
4051
1703
answer