Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 907
auramo Avatar asked Sep 24 '10 10:09

auramo


People also ask

Does Lisp use dynamic scoping?

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).

Is PostScript dynamically scoped?

PostScript, for example -- the language that runs on printers -- is dynamically scoped.

What is Lexically bind?

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.

Is Emacs Lisp compiled or interpreted?

Emacs Lisp has a compiler that translates functions written in Lisp into a special representation called byte-code that can be executed more efficiently.


1 Answers

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.

like image 80
Rainer Joswig Avatar answered Oct 09 '22 06:10

Rainer Joswig