values への setf
defsetf とか define-setf-method とかの使い方はよく知らんのだけど、まぁ何とかなるかなーと思ってた。世の中そんなに甘くなかった。
define-setf-method
(setf (access-fn args) new-value)
とゆー風に呼び出される access-fn への setf を定義する define-setf-method は
(define-setf-method access-fn <lambda-list> ...)
で定義するんだけど、setf 呼び出しの args が define-setf-method の
の5つを多値で返すことになってる。
実際に setf 式を評価(展開)すると
define-setf-method で定義された関数を呼び出して返ってきたこの5つのリストやら式やらが、setf の展開形として最終的にはこんな感じで使われる。access-form がなにに使われるのかはわからない。
(let* ((temp-var-1 value-form-1) (temp-var-2 value-form-2) ... (store-var new-value)) storing-form)
とゆーわけで、(setf (values ...) new-value) をどーにかしようとしても、setf の展開形は new-value を let* で受け取ってるんで2つ目以降は捨てられてしまう。dead end
setf.l
define-setf-method の store-var がリストになっているのは、将来多値に対応することができるように、とゆー理由だと聞いたことがあるような気がしたので、対応させてみる。
最終的に
(let* ((temp-var-1 value-form-1) (temp-var-2 value-form-2) ...) (multiple-value-bind (store-vars) new-value storing-form))
になればおkなはず。
まず setf-expand-1 の最後
(if (or vars stores) `(let* ,(mapcar #'list vars vals) (multiple-value-bind ,stores ,(car newvalue) ,store-form))) store-form)))))
get-setf-method 内で stores が複数あったらエラー投げちゃってくれてるのでやめれ。
optimize-setf-method がなぜか stores の cdr を捨ててしまってくれやがる。この関数は元の setf-expand-1 だと (let* ((var val) ...) ...) の val が定数や変数(symbol)だったら、いちいち var に束縛するんじゃなくて store-form に埋め込んでしまえ、ということをしてるようだ。はんはん、なるほど。
めんどいので stores/newvalues は触らずそのまま返すようにした。以下は、ちゃんとやる気になったときのためのメモ:
- newvalues は (<多値を返す式>) なので、cdr は常に nil(car しかない)なはず
- 回すなら stores の各要素と (cdar newvalues) の各要素
- (car newvalues) が '(values ...) なら
ついでに、というかその前に
- setf-expand-1 で問答無用で multiple-value-bind にしてるが、そもそも stores が1つだけなら元の let* で無問題。
(define-setf-method values (&rest places) (let ((syms (mapcar #'gensym places))) (values nil nil syms (cons 'setf (mapcan #'list places syms))))) => (setf values) (setf (values foo bar baz) (values 1 2 3)) => 3 (list foo bar baz) => (1 2 3) (setf (values foo bar baz) (values-list '(x y z))) => z (list foo bar baz) => (x y z)
おk