Strange interaction between lexical binding and defvar in emacs lisp

The following emacs lisp file tells what happens when Alice uses the lexically related local variable foo in her initialization file, and Bob defines foo as a global special variable with defvar in his initialization file, and Alice takes part of Bob's initialization code in his own file initialization, not knowing that foo will become special.

 ;; -*- lexical-binding: t; -*- ;; Alice init file ;; Alice defining alice-multiplier (defun alice-multiplier-1 (foo) (lambda (n) (* n foo))) (defun alice-multiplier-2 (num) (let ((foo num)) (lambda (n) (* n foo)))) ;; Alice using alice-multiplier (print (list :R1 (mapcar (alice-multiplier-1 10) (list 1 2 3)) :R2 (mapcar (alice-multiplier-2 10) (list 1 2 3)))) ;; from Bob code ;; ... (defvar foo 1000) ;; ... ;; Alice using alice-multiplier (print (list :R3 (mapcar (alice-multiplier-1 10) (list 1 2 3)) :R4 (mapcar (alice-multiplier-2 10) (list 1 2 3)))) 

Output:

 (:R1 (10 20 30) :R2 (10 20 30)) (:R3 (10 20 30) :R4 (1000 2000 3000)) 

The results of R1 and R2 are exactly as I expect. The result of R4 is consistent with the defvar documentation, although it might surprise Alice if she doesn't read Bob's code.

  • I find the R3 awesome. Why do R3 like it?

  • Speaking of R4, what can Alice do to protect her foo from others? For example, foo may be a lexical local variable that it uses in its initialization file or in one of its emacs packages, and (defvar foo "something") may be in some packages that it uses, or foo may be one of the new special Variable names introduced by a future version of Emacs. Is there something that Alice can paste into her file that Emacs says. "In this file, foo should always be lexical, even if some code externally uses a special variable with the same name"?

+6
source share
2 answers

What's happening

In terms of โ€œtheoreticalโ€ (schema / generic Lisp), as soon as you turn on lexical bindings, for all practical purposes, alice-multiplier-1 and alice-multiplier-2 identical . Any difference in their behavior is a bug in Emacs Lisp and should be reported as such.

Compiled by

If you put your code (i.e. line 2 defun and ;; -*- lexical-binding: t; -*- ) in a file, emacs-list-byte-compile-and-load it, you can check my requirement by evaluating these 4 forms:

 (disassemble 'alice-multiplier-1) (disassemble 'alice-multiplier-2) (disassemble (alice-multiplier-1 10)) (disassemble (alice-multiplier-2 10)) 

You will see that 3 and 4 are identical, and 1 and 2 differ in one instruction (which should be reported as an error for Emacs-enabled ones, but does not affect the behavior).

Note that none of the disassembly mentions foo , which means that defvar will not affect their behavior.

Things are good!

Interpreted

In fact, the behavior you see is incorrect; the correct result after defvar is

 (:R1 (10000 20000 30000) :R2 (10000 20000 30000)) 

please inform emacs developers about this .

Various???

Yes, defvar does (and should!) Influence the behavior of the interpreted code and does not (and should not!) Influence the behavior of the compiled code.

What you should do

It is not possible to "protect" your foo from declaring special others - except that the prefix "your" characters is denoted by alice- .

However, if you byte-compile a file with the definition alice-multiplier-1 , the compiled file does not even contain foo , and therefore future foo declarations will not affect you.

+4
source

As for the second question, as far as I know, not a single one. But you can still get by. That is, for the adoption of two naming conventions: which I will call yellow and green.

yellow naming convention

All special variables must have yellow names. A yellow name is a name that includes at least one hyphen. For example, hello-world and ga-na-da are yellow names. The official Emacs Lisp manual and byte compiler support this convention.

green naming convention

A green name is a name that is not yellow. For example, helloworld and ganada are green names.

All lexical nonlocal / free variables must have green names. What is a non-local variable? In the body of an anonymous function, alice-multiplier-2 mentions three names, n , foo , num . Of these three, only n has a declaration inside the body of the function (anonymous function). The other two are nonlocal in terms of anonymous function. These are non-local variables.

While Alice and Bob adhere to two naming conventions, all is well. Even if they do not, it is likely that the two people converge in these agreements at the end on their own without mutual communication, even at these stages:

  • Alice and Bob do not adhere to any of the conventions.

  • Since the manual and the byte compiler support the yellow naming convention, there comes a time when both Alice and Bob begin to stick to the yellow convention.

  • Alice accepts a green convention that at least protects her code from yellow special variables by others.

  • Alice and Bob adhere to both agreements.

For exact conditions in which collisions can occur, see Invasion of Special Variables

+1
source

Source: https://habr.com/ru/post/948456/


All Articles