(Almost) All You Need to Know About Variables
This is the start of a series of posts about Emacs Lisp concepts in which I will try to fill some gaps between beginner resources and some more advanced concepts. This post assumes that you are already familiar with common Emacs terminology and know how to read and evaluate basic snippets of Emacs Lisp code. Ideally you should also have heard of variable scope and how it works in some other programming language. The examples also assume you use a fairly recent Emacs version (v.25 or later).
Of course the Elisp manual will tell you everything there is to know but it is designed for people who already know what they are looking for (and it is really great for that). Therefore many people ask for resources explaining Elisp concepts at a higher level and reduce the information to the most useful bits. This is my attempt to bring readers in the position of having a good grasp of the basics so they can use them for their own configuration and have an easier time if they need to look up some detail in the manual.
Global Variables
User options defined via defcustom
and variables defined via defvar
or
defconst
are global. One important aspect about variables declared by
defcustom
or defvar
is that reevaluating them won’t reset the variable if it
is already bound. For example if you establish a binding for my-var
in you
init file like this:
(setq my-var nil)
Then evaluating the following form won’t reset the variable to t
:
(defvar my-var t)
Note that there is one exception: If you evaluate the above declaration via
C-M-x which calls eval-defun
the value will be reset to t
. This way
you can enforce setting the value if you need to. This behavior is intentional:
As you might know many features in Emacs are only loaded on demand (autoloaded).
If the declarations in those files would reset variables to their default value
this would override any settings you made in your init.
User Options
A user option is simply a global variable that was declared with defcustom
.
Unlike variables declared via defvar
such a variable is configurable via the
M-x customize interface. As far as I know most people don’t make
much use of it because it feels clunky and once you know how to set variables in
your init file there is no compelling reason to use it. One detail many users
aren’t aware of is that setting user options via customize might execute code,
which is sometimes used to run additional setup instructions:
(defcustom my-option t
"My user option."
:set (lambda (sym val)
(set-default sym val)
(message "Set %s to %s" sym val)))
If you evaluate the above piece of code and change the value using the customize interface via M-x customize-option RET my-option RET, you will see that the lambda gets called and the message in the echo area tells you the symbol and value of the option.
In case you use setq
in your int file for changing the value of such an option
the setter function will not run. To set such an option correctly via Elisp
you need to use the function customize-set-variable
. Alternatively there are
various versions of csetq
macros people use in their configs to automatically
take care of this (you can use github code search to discover more sophisticated
variants if you like):
(defmacro csetq (sym val)
`(funcall (or (get ',sym 'custom-set) 'set-default) ',sym ,val))
If you are using
use-package, the
:custom
keyword already handles this for you.
After putting the above code in your init file, you can use csetq
to set
variables in a way which respect any existing setter functions. You can prove
this to yourself by watching the message in the echo area when using this macro
to change the option defined above:
(csetq my-option nil)
Dynamic Binding and Lexical Binding
Even if you have already dealt with other programming languages you might never heard of dynamic or lexical binding. This is because most programming languages today use lexical binding and when you learn about variable scope/lookup there is no need to know the difference.
Emacs Lisp is a bit special in this regard because dynamic binding is the default and lexical binding needs to be enabled explicitly. This has historical reasons and in practice you should always enable lexical binding because it is faster and less error prone. To enable it you simply put the following comment line as the first line of your Emacs Lisp file:
;;; -*- lexical-binding: t; -*-
Alternatively you can call M-x add-file-local-variable-prop-line
which will insert this line for you when you choose the variable
lexical-binding
with value t
.
When a file with such a special formatted line gets loaded Emacs will set the variable accordingly, which means the code in that buffer will be loaded with lexical binding enabled. Interactively you can use M-x eval-buffer which will also take the lexical binding setting into account.
Now you know how to enable lexical binding let’s examine what those terms actually mean: With dynamic binding the latest binding established during program execution will be used for variable lookup. You can test this by putting the following code in an empty buffer and execute M-x eval-buffer:
(defun a-exists-only-in-my-body (a)
(other-function))
(defun other-function ()
(message "I see `a', its value is %s" a))
(a-exists-only-in-my-body t)
You may be surprised to see that the lookup of variable a
in other-function
will succeed.
If you retry the preceding example with the special lexical-binding comment at
the top, you will notice that the code will throw a variable is void error
because other-function
does not know about the a
variable. Coming from other
programming languages this is the behavior you would expect.
With lexical binding the scope is defined by the surrounding source code. In addition to performance reasons, experience and time have shown that this behavior should be preferred.
Special Variables and Dynamic Binding
As you might know let
is used to temporary establish local bindings:
(let ((a "I'm a")
(b "I'm b"))
(message "Hello, %s. Hello %s" a b))
Here is the thing: Variables declared via defcustom
, defvar
or defconst
are so called special variables and those will continue to use dynamic binding
regardless if lexical binding is enabled or not:
;;; -*- lexical-binding: t; -*-
(defun some-other-function ()
(message "I see `c', its value is: %s" c))
(defvar c t)
(let ((a "I'm lexically bound")
(c "I'm special and therefore dynamically bound"))
(some-other-function)
(message "I see `a', its values is: %s" a))
To see both messages in the example above you can switch to the *Messages*
buffer using C-h e.
Local variables bound with let
or function arguments will follow the lookup
rules as defined by the lexical-binding
variable, but global variables
defined via defvar
, defconst
or defcustom
can be changed deep down the
call stack for the duration of the let
body!
This behavior allows for convenient adhoc customizations and is often used in Emacs, which isn’t surprising given that Emacs Lisp started out with dynamic binding being the only option. Here is a common example showing how you can temporary write to some read-only buffer:
(let ((inhibit-read-only t))
(insert ...))
Or another often seen example to perform case sensitive searches:
(let ((case-fold-search nil))
(some-function-which-uses-search ...))
Dynamic binding allows changing the behavior of functions in ways which the authors of those functions may have never anticipated. It’s a powerful tool and it is a great feature for a program that is designed and used like Emacs.
There is one caveat you should be aware of: It may happen that you accidentally use a local variable name which is a declared as a special variable elsewhere. One trick to prevent such conflicts is to avoid local variables which have dashes in their name. In my current Emacs session this leaves only a handful of potential conflicting candidates:
(let ((vars ()))
(mapatoms
(lambda (cand)
(when (and (boundp cand)
(not (keywordp cand))
(special-variable-p cand)
(not (string-match "-"
(symbol-name cand))))
(push cand vars))))
vars) ;; => (t obarray noninteractive debugger nil)
Buffer Local Variables
Each buffer can have a local binding for a variable. This means any variable lookup made while this buffer is current will reveal the buffer local value of that variable instead of the default one. Local variables are an important feature in Emacs, for example they are used by major modes to establish their buffer local behavior and settings. To avoid interfering with each other major modes also kill existing local variables when you enter them.
You have already seen a buffer local variable in this post: The special comment
line for lexical-binding
which binds it buffer locally to t
. In Emacs such
buffer local variables defined in special comment lines are also called file
local variables.
Any global variable can be shadowed by a buffer local one. Take for example
the my-var
variable defined in a previous section of this post which you can
set locally like this:
(setq-local my-var t)
;; or (set (make-local-variable 'my-var) t)
my-var
will be local to the buffer which is current when you evaluate the code
above. If you call describe-variable
on it, you will see that the
documentation tells you the local value and the global one. Programmatically you
can check the local value using buffer-local-value
and the default one with
default-value
. To remove the local version you could invoke M-x
kill-local-variable.
Another important property you need to be aware of is that once a variable is
buffer local, any further uses of setq
(while this buffer is current) will
continue to set the local value. To set the default value you would need to use
setq-default
.
Because local variables are meant for buffer customization you will use them most often in mode hooks. A typical example would be something like:
(add-hook 'go-mode-hook
(defun go-setup+ ()
(setq-local compile-command
(if (string-suffix-p "_test.go" buffer-file-name)
"go test -v"
(format "go run %s"
(shell-quote-argument
(file-name-nondirectory buffer-file-name)))))))
This will set the compile command used by M-x compile for go mode buffers.
Another important aspect is that some variables are automatically buffer
local. This means as soon as you setq
such a variable this will establish a
local binding for the current buffer. This feature shouldn’t be used often
because this implicit behavior isn’t nice but if you want you can create such
automatically local variables like this:
(defvar-local my-automatical-local-var t)
;; or (make-variable-buffer-local 'my-automatical-local-var)
The variable indent-tabs-mode
is a built-in example of this. If you would use
setq
in your init file to change the value of this variable that wouldn’t
affect the default value at all. Only the value for the buffer that is current
while loading your init file would be changed. Because of that you need to use
setq-default
to change the default value of indent-tabs-mode
.
Closing Words
I have tried my best to find a balance between details and a more high level overview. Hopefully this helps some souls out there feeling more confident when setting their variables in Emacs. Please leave a comment if you have any questions or suggestions.