How to live with Emacs Lisp dynamic scoping?

I've learned Clojure previously and really like the language. I also love Emacs and have hacked some simple stuff with Emacs Lisp. There is one thing which prevents me mentally from doing anything more substantial with Elisp though. It's the concept of dynamic scoping. I'm just scared of it since it's so alien to me and smells like semi-global variables.

So with variable declarations I don't know which things are safe to do and which are dangerous. From what I've understood, variables set with setq fall under dynamic scoping (is that right?) What about let variables? Somewhere I've read that let allows you to do plain lexical scoping, but somewhere else I read that let vars also are dynamically scoped.

I quess my biggest worry is that my code (using setq or let) accidentally breaks some variables from platform or third-party code that I call or that after such call my local variables are messed up accidentally. How can I avoid this?

Are there a few simple rules of thumb that I can just follow and know exactly what happens with the scope without being bitten in some weird, hard-to-debug way?


Solution 1:

It isn't that bad.

The main problems can appear with 'free variables' in functions.

(defun foo (a)
  (* a b))

In above function a is a local variable. b is a free variable. In a system with dynamic binding like Emacs Lisp, b will be looked up at runtime. There are now three cases:

  1. b is not defined -> error
  2. b is a local variable bound by some function call in the current dynamic scope -> take that value
  3. b is a global variable -> take that value

The problems can then be:

  • a bound value (global or local) is shadowed by a function call, possibly unwanted
  • an undefined variable is NOT shadowed -> error on access
  • a global variable is NOT shadowed -> picks up the global value, which might be unwanted

In a Lisp with a compiler, compiling the above function might generate a warning that there is a free variable. Typically Common Lisp compilers will do that. An interpreter won't provide that warning, one just will see the effect at runtime.

Advice:

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

Don't write

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

Write:

(defun foo (a b)
   ...
   (let ((c (* a b)))
     ...)
   ...)

Bind all variables you want to use and you want to make sure that they are not bound somewhere else.

That's basically it.

Since GNU Emacs version 24 lexical binding is supported in its Emacs Lisp. See: Lexical Binding, GNU Emacs Lisp Reference Manual.

Solution 2:

In addition to the last paragraph of Gilles answer, here is how RMS argues in favor of dynamic scoping in an extensible system:

Some language designers believe that dynamic binding should be avoided, and explicit argument passing should be used instead. Imagine that function A binds the variable FOO, and calls the function B, which calls the function C, and C uses the value of FOO. Supposedly 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, because the author of the system cannot know what all the parameters will be. Imagine that the functions A and C are part of a user extension, while B is part of the standard system. The variable FOO does not exist in the standard system; it is part of the extension. To use explicit argument passing would require adding a new argument to B, which means rewriting B and everything that calls B. In the most common case, B is the editor command dispatcher loop, which is called from an awful number of places.

What's worse, C must also be passed an additional argument. B doesn't refer to C by name (C did not exist when B was written). It probably finds a pointer to C in the command dispatch table. This means that the same call which sometimes calls C might equally well call any editor command definition. So all the editing commands must be rewritten to accept and ignore the additional argument. By now, none of the original system is left!

Personally, I think that if there is a problem with Emacs-Lisp, it is not dynamic scoping per se, but that it is the default, and that it is not possible to achieve lexical scoping without resorting to extensions. In CL, both dynamic and lexical scoping can be used, and -- except for top-level (which is adressed by several deflex-implementations) and globally declared special variables -- the default is lexical scoping. In Clojure, too, you can use both lexical and dynamic scoping.

To quote RMS again:

It is not necessary for dynamic scope to be the only scope rule provided, just useful for it to be available.

Solution 3:

Are there a few simple rules of thumb that I can just follow and know exactly what happens with the scope without being bitten in some weird, hard-to-debug way?

Read Emacs Lisp Reference, you'll have many details like this one :

  • Special Form: setq [symbol form]... This special form is the most common method of changing a variable's value. Each SYMBOL is given a new value, which is the result of evaluating the corresponding FORM. The most-local existing binding of the symbol is changed.

Here is an example :

(defun foo () (setq tata "foo"))

(defun bar (tata) (setq tata "bar"))


(foo)
(message tata)
    ===> "foo"


(bar tata)
(message tata)
    ===> "foo"

Solution 4:

As Peter Ajtai pointed out:

Since emacs-24.1 you can enable lexical scoping on a per file basis by putting

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

on top of your elisp file.

Solution 5:

First, elisp has separate variable and function bindings, so some pitfalls of dynamic scoping are not relevant.

Second, you can still use setq to set variables, but the value set does not survive the exit of the dynamic scope it is done in. This isn't, fundamentally, different from lexical scoping, with the difference that with dynamic scoping a setq in a function you call can affect the value you see after the function call.

There's lexical-let, a macro that (essentially) imitates lexical bindings (I believe it does this by walking the body and changing all occurrences of the lexically let variables to a gensymmed name, eventually uninterning the symbol), if you absolutely need to.

I'd say "write code as normal". There are times when the dynamic nature of elisp will bite you, but I've found that in practice that is surprisingly seldom.

Here's an example of what I was saying about setq and dynamically-bound variables (recently evaluated in a nearby scratch buffer):

(let ((a nil))
  (list (let ((a nil))
          (setq a 'value)
          a)
        a))

(value nil)