lexical-binding
Emacs 24 が release されて直後に途中まで書いてからほったらかしておいたら、大部経ってしまった。
前半あたりはたぶん 24.1 最後あたりは 24.3 でやった結果。
Emacs の lexical scope について、いくつか思いつくところを試していきます。
(symbol-plist 'lexical-binding) => (safe-local-variable booleanp variable-documentation 585535)
(symbol-function 'lexical-binding) => Symbol's function definition is void: lexical-binding
(symbol-value 'lexical-binding) => nil
(local-variable-p 'lexical-binding) => nil
(setq lexical-binding t) (local-variable-p 'lexical-binding) => t
なので lexical-binding は
(make-variable-buffer-local 'lexical-binding)
された感じの変数。
lexical-binding が non-nil のバッファで lambda 式を評価すると closure になる。(みたい)
(let ((b 2)) (let ((f (lambda (a) (+ a b)))) (funcall f 1))) => 3
↑これはよくある例
(let ((b 2)) ((lambda (a) (+ a b)) 1)) => (void-variable b) |< ↑これが予想外の結果だった 同様のことをやるとしたら、こんな感じになるかな。上のと同様であんまり意味がない例だけど。 >|lisp| ((closure ((b . 2) . t) (a) (+ a b)) 1) => 3
んーと。lambda が右辺値として評価された後じゃないと closure にならないってことかな?
(fset 'hoge (let ((a 1)) (lambda (b) (+ b (incf a)))))
(hoge 20) => 22 (hoge 20) => 23
(symbol-function 'hoge) => (closure ((a . 3) t) (b) (+ b (incf a)))
elisp での `closure' とはなんぞ?
(symbol-plist 'closure) => nil (symbol-function 'closure) => Symbol's function definition is void: closure (closure ((a . 3) t) (b) (+ b (incf a))) => Symbol's function definition is void: closure ((closure ((a . 3) t) (b) (+ b (incf a)))) => Wrong number of arguments: (((a . 3) t) (b) (+ b (incf a))), 0
おそらく何の変哲もない symbol だと思う。
((closure ((a . 3) t) (b) (+ b (incf a))) 20) => 24 ((closure ((a . 3) t) (b) (+ b (incf a))) 20) => 24
(closure (lexbind-variables ???) 引数 関数の実体) みたいな感じ?
lexbind-variables を変えてみる。
((closure ((a . 300) t) (b) (+ b (incf a))) 20) => 321
??? の部分 (この場合は t) はなんなんだろう? nil に変えてみる。
((closure ((a . 3) nil) (b) (+ b (incf a))) 20) => 24
結果だけみると t のときとかわらん。
しかたないので後でソースみる
既存の dynamic binding な仕組みとの関係。
(defvar hogehoge 3) (let ((hogehoge 5)) (let ((f (lambda (a) (+ a hogehoge)))) (funcall f 1))) => 6
(let (hogelambda) (let ((hogehoge 5)) (setq hogelambda (lambda (a) (+ a hogehoge)))) (funcall hogelambda 1)) => 4 hogelambda => (closure (t) (a) (+ a hogehoge))
hogehoge が closure の変数からなくなってしまう。defvar されてるからだろうな。
(makunbound 'hogehoge) (let (hogelambda) (let ((hogehoge 5)) (setq hogelambda (lambda (a) (+ a hogehoge)))) (funcall hogelambda 1)) => Symbol's value as variable is void: hogehoge (let (hogelambda) (let ((hogehoge 5)) (setq hogelambda (lambda (a) (+ a hogehoge)))) hogelambda) (closure ((hogelambda closure #1 (a) (+ a hogehoge)) t) (a) (+ a hogehoge))
ん?変数セルを unbound しても closure の変数に入らない。
と、いうことは intern してあるだけで lexical scope にならないってことかな?
(unintern "hogehoge") (intern "hogehoge") (let (hogelambda) (let ((hogehoge 5)) (setq hogelambda (lambda (a) (+ a hogehoge)))) (funcall hogelambda 1)) => Symbol's value as variable is void: hogehoge
だめだ。違う。
さて、試行してる最中に不審な挙動に気付いた。 emacs -q でも試したから間違いない。
↓これだけ書いた lexical-binding t なバッファを作りましょう。
(unintern "hogehoge") しときましょう。`defvar' の form は評価しないでください。
(let (hogelambda) (let ((hogehoge 5)) (setq hogelambda (lambda (a) (+ a hogehoge)))) (funcall hogelambda 1)) ;; これは評価しない (defvar hogehoge 3) (let (hogelambda) (let ((hogehoge 5)) (setq hogelambda (lambda (a) (+ a hogehoge)))) (funcall hogelambda 1))
一つ目と二つ目の let フォームの内容は同じですが結果は異なります。
これは酷い。これ知らないで lexical scope な elisp 書き散らしてるときは、はまることもありそう。
defvar form 評価後なら問題ないので、さすがにはまることは稀だろうけど、頭の片隅には入れておきたい。
試した限りでは defvar じゃなくても defcustom, defconst あたりは同じみたい。なかなかベタですな。
で、えーと、まとめると、評価する form の前に、未評価な defvar 系 form + 該当する symbol があるときは lexical scope な変数とならない。 ということだろうか。
text 検索してるだけのような気がするので defvar が if の中にあるときとかどうなるのだろう。文字列の中にあるときは?
そういえば、この挙動については随分前にどこかで読んだような気もする。頭の片隅にも入ってなかったやん。
長くなってきて息切れしたので続きはまた今度。あるいは書き足すかも。
TODO: closure form の中にある引数の意味。
TODO: defvar form の中はどこまで評価されるのか。string の中だとどうなる? (eval-sexp-add-defvars)
TODO: C-x C-e のときだけかもしれない。
TODO: lambda 式の定義。 lexical-binding のときはどこにいく?