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?
Dynamic scoping was the norm in versions of Lisp before Common Lisp, and is also used in some older, interpreted languages such as SNOBOL and APL. We can declare a variable as dynamically scoped in Common Lisp using defvar (but not in Scheme/Racket).
PostScript, for example -- the language that runs on printers -- is dynamically scoped.
A name that is lexically bound is looked up only in bindings in the lexical environment of the name – that is, in bindings that enclose the name in the source code. So if “a” is lexically bound, the code above prints “1”, because only binding (1) is in the lexical environment.
Emacs Lisp has a compiler that translates functions written in Lisp into a special representation called byte-code that can be executed more efficiently.
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:
b
is not defined -> errorb
is a local variable bound by some function call in the current dynamic scope -> take that valueb
is a global variable -> take that valueThe problems can then be:
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:
*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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With