How to live with dynamic coverage of Emacs Lisp?

I learned Clojure before and really love the language. I also love Emacs and hacked some simple things with Emacs Lisp. There is one thing that prevents me from mentally doing something more significant with Elisp. This is the concept of dynamic reach. I'm just afraid of it, as it smells so alien to me and smells like semi-global variables.

So, with variable declarations, I don’t know which things are safe and which are dangerous. From what I understand, the variables set using setq fall under dynamic scaling (right?) What about the variables? I read somewhere that let allows you to make a lexical definition, but somewhere else I read that let vars also be dynamically covered.

I am most worried that my code (using setq or let) accidentally breaks some variables from the platform or third-party code that I call, or after such a call, my local variables are accidentally confused. How can i avoid this?

Are there a few simple rules that I can simply follow and know exactly what is happening with the area without being bitten in some strange, hard to debug way?

+49
scope emacs lisp elisp
Sep 24 '10 at 10:10
source share
10 answers

It's not so bad.

Major problems may arise with "free variables" in functions.

(defun foo (a) (* ab)) 

In the above function a is a local variable. b is a free variable. On a dynamically linked system such as Emacs Lisp, b will be checked at runtime. There are currently three cases:

  • b not defined -> error
  • b - local variable associated with some function call in the current dynamic region β†’ takes this value
  • b - global variable -> accept this value

Now the problems may be as follows:

  • the associated value (global or local) is obscured by a function call, possibly undesirable
  • undefined variable is NOT shaded -> error on access
  • global variable is NOT shaded -> selects a global value that may be undesirable

In Lisp with a compiler, compiling the specified function may cause a warning about the presence of a free variable. These will usually be Common Lisp compilers. The interpreter will not provide this warning as soon as it sees the effect at runtime.

Tip

  • make sure you don't accidentally use free variables
  • make sure that global variables have a special name, so they are easy to find in the source code, usually *foo-var*

Do not write

 (defun foo (ab) ... (setq c (* ab)) ; where c is a free variable ...) 

Record:

 (defun foo (ab) ... (let ((c (* ab))) ...) ...) 

Bind all the variables you want to use, and you want to make sure that they are not connected anywhere else.

Mainly.

Since GNU Emacs version 24 lexical binding is supported in Emacs Lisp. See: Lexing, GNU Emacs Lisp Reference Guide .

+44
Sep 24 '10 at
source share
β€” -

Are there a few simple rules that I can simply follow and know exactly what is happening with the area without being bitten in some strange, hard to debug way?

Read the Emacs Lisp Link , you will have many details like this:

  • Special form: setq [character form] ... This special form is the most common method of changing the value of a variable. Each SYMBOL is assigned a new value, which is the result of the evaluation of the corresponding FORM. The most local existing character binding is changing .

Here is an example:

 (defun foo () (setq tata "foo")) (defun bar (tata) (setq tata "bar")) (foo) (message tata) ===> "foo" (bar tata) (message tata) ===> "foo" 
+13
Sep 24 '10 at 11:34
source share

In addition to the last paragraph of Gilles's answer, here is how RMS claims in favor of dynamic coverage in an extensible system:

Some language developers believe that dynamic binding should be avoided; explicit argument passing should be used instead. Imagine that function A binds the variable FOO and calls function B, which calls function C and C uses the value FOO. Presumably, A should pass the value as an argument to B, which should pass it as an argument to C.

This cannot be done in an extensible system, however, since the author of the system cannot know what the parameters will be. Imagine that functions A and C are part of a user extension, while B is part of a standard system. The FOO variable does not exist on the standard system; This is part of the extension. To use explicit argument passing, you need to add a new argument to B, which means rewriting B and everything that calls B. In the most common case, B is a loop editor command manager called from an awful amount of places.

To make matters worse, C must also be given an additional argument. B does not refer to C by name (C did not exist when B was written). He probably finds a pointer to C in the command dispatch table. This means that the same call that sometimes calls C calls any editor definition command. So, all editing commands must be overwritten in order to accept and ignore the extra argument. From now, none of the source systems is left!

Personally, I believe that if there is a problem with Emacs-Lisp, this is not dynamic coverage as such, but that it is the default value and that it is impossible to achieve lexical coverage without resorting to extensions. In CL, you can use both dynamic and lexical reach, and - in addition to the top level (which is called several deflex implementations) and declared global declared variables - the lexical definition is used by default. In Clojure, you can also use lexical and dynamic scaling.

To specify RMS again:

There is no need for the dynamic scope to be the only scope rule provided, it is just useful to make it available.

+13
Sep 25 '10 at 1:18
source share

As Peter Aytay noted:

Since emacs-24.1 you can enable lexical scaling for each file by setting

 ;; -*- lexical-binding: t -*- 

on top of your elisp file.

+11
Apr 19 '13 at 20:26
source share

First, elisp has separate bindings for variables and functions, so some dynamic scope errors are irrelevant.

Secondly, you can use setq to set variables, but a set of values ​​does not withstand the exit from the dynamic area in which it is executed. This is not fundamentally different from lexical coverage, with the difference that with the dynamic viewing of setq in the called function, it can affect the value that you see after calling the function.

There's lexical-let , a macro that (essentially) mimics lexical bindings (I suppose it does this by strolling through the body and changing all occurrences of lexically valid variables in the gensymmed name, after all, not to mention the symbol), if you are absolutely necessary.

I would say, "write the code as usual." There are times when the dynamic nature of elisp will bite you, but I have found that in practice this is surprisingly rare.

Here is an example of what I talked about setq and dynamically related variables (recently evaluated in an adjacent buffer from scratch):

 (let ((a nil)) (list (let ((a nil)) (setq a 'value) a) a)) (value nil) 
+10
Sep 24 '10 at 10:41
source share

Everything that was written here is worth it. I would add the following: get to know Common Lisp - if nothing else, read about it. CLTL2 describes lexical and dynamic binding well, like other books. And Common Lisp integrates them well in one language.

If you "get" after some exposure to Common Lisp, then for Emacs Lisp you will become clearer. Emacs 24 makes greater use of lexical coverage than older versions, but the general Lisp approach will still be clearer and cleaner (IMHO). Finally, this is definitely the case when the dynamic area is important to Emacs Lisp for reasons that RMS and others emphasize.

So my suggestion is to find out how it communicates with Common Lisp. Try to forget about the Scheme, if this is your main Lisp mental model - it will limit you to something that helps you understand scope, funargs, etc. In Emacs Lisp. Emacs Lisp, like Common Lisp, "dirty and low"; this is not a diagram.

+8
Aug 20 '11 at 23:14
source share

Dynamic and lexical reach have different types of behavior when a piece of code is used in a different scope than the one on which it was defined. In practice, there are two patterns that cover the most unpleasant cases:

  • A function obscures a global variable, then calls another function that uses this global variable.

     (defvar x 3) (defun foo () x) (defun bar (x) (+ (foo) x)) (bar 0) β‡’ 0 

    This often does not occur in Emacs, because local variables have short names (often single-word), while global variables have long names (often the prefix packagename- ). Many standard functions have names that are attractive for use as local variables, such as list and point , but functions and variables located in separate namespaces are local functions that are not used very often.

  • A function is defined in the same lexical context and is used outside this lexical context, since it is passed to a higher-order function.

     (let ((cl-y 10)) (mapcar* (lambda (elt) (* cl-y elt)) '(1 2 3))) β‡’ (10 20 30) (let ((cl-x 10)) (mapcar* (lambda (elt) (* cl-x elt)) '(1 2 3))) ⇑ (wrong-type-argument number-or-marker-p (1 2 3)) 

    The error is related to using cl-x as the variable name in mapcar* (from the cl package). Note that the cl package uses cl- as a prefix even for its local variables in higher-order functions. This in practice works quite well if you do not use the same variable as the global name and as a local name, and you do not need to write a recursive function of a higher order.

PS Emacs Lisp age is not the only reason why it is dynamically covered. True, in those days, Lisp tended to be dynamic in scope - Scheme and Common Lisp have not yet been adopted. But dynamic scaling is also an advantage in a language aimed at dynamically expanding the system: it allows you to capture more places without much effort. With great power, a great rope comes to hang yourself: you run the risk of accidentally connecting to a place you did not know about.

+5
Sep 25 '10 at 0:18
source share

Other answers provide a good explanation of the technical details on how to work with dynamic coverage, so here are some non-technical tips:

Just do it

I have been practicing Emacs lisp for 15 years and don't know that I have ever been bitten by any problems due to the differences between the lexical / dynamic area.

Personally, I did not find the need to close (I love them, I just do not need them for Emacs). And, as a rule, I try to avoid global variables in general (whether the field of view was lexical or dynamic).

Therefore, I suggest bouncing and writing settings that match your needs / desires, most likely you will not have any problems.

+4
Sep 27 '10 at 19:15
source share

I totally feel your pain. I find that the lack of lexical bindings in emacs is quite annoying - especially not being able to use lexical closures, which seem to be a solution that I think of a lot, coming from more modern languages.

While I have no more advice on working with missing functions that were not previously affected by previous answers, I would like to point out the existence of an emacs branch called `lexbind ', which implements lexical binding in the opposite way. In my opinion, lexical closures in some cases are still a little erroneous, but this branch seems promising.

+2
Sep 24 '10 at 19:52
source share

Just don't do it.

Emacs-24 allows you to use the lexical domain. Just run

(setq lexical-binding t)

or add

;; -*- lexical-binding: t -*-

at the beginning of the file.

+2
04 Sep '14 at 7:22
source share



All Articles