Version 2.7 released


#1

Version 2.7 updates uLisp to use Lexical Scoping, like current dialects of Lisp including Common Lisp and Scheme. Previously uLisp used Dynamic Scoping, like older dialects of Lisp.

ELI5: Lexical Scoping is preferred because it avoids the possibility of obscure errors due to parameter name clashes; perhaps someone can explain it better than this.

Here’s a simple test that detects which type of scoping a Lisp is using:

(let ((lex nil))
  (funcall (let ((lex t)) (lambda () (if lex 'lex 'dyn)))))

It returns lex if the Lisp uses Lexical Scoping and dyn if it uses Dynamic Scoping.

To download the latest version see: Download uLisp.


#2

https://www.emacswiki.org/emacs/DynamicBindingVsLexicalBinding#toc2

To summarize the linked example, look at four nested lines of code, bind, define a function, rebind, call the function, then look at what data the function got either the original or the rebind.

#1 bind A to 1

#2 Define a function that prints the contents of A

#3 bind A to 2

#4 Call the function from #2

Note the above four are supposed to be four nested LETs

Lexical binding looks up and prints 1, dynamic binding looks down and prints 2

Lexical looks upward in the tree of each object and listens only to its ancestors, dynamic looks at the most recent rebinding regardless who or what bound something.

Or rephrased lexical bindings look at their parents bindings to win; dynamic binding maybe better named global because whatever last bound wins.

The obscure errors it can avoid are changes to the kids data don’t mess with the grandparents data, more or less.


#3

@johnsondavies Cool, thanks! I’ll be able to rewrite my code the way I wanted it.

@VinceMulhollon While your explanation matched my understanding in theory, I did not get how it applied to the test David lists above, since it looked to me like the inner let should be evaluated before the lambda expression.

But I guess the page you provided a link to contains an explanation for that, too:

(let ((lex t))
  (lambda ()
    (if lex 'lex 'dyn)))

is actually another - arguably more readable way - to express this:

((lambda (lex)
   (if lex 'lex 'dyn))
 t)

#4

Here’s a simpler explanation of the scoping test I gave in my first post.

We define a function f which uses a free variable, lex. The value of lex is defined as t by the enclosing environment:

(let ((lex t)) (defvar f (lambda () (if lex "Lexical" "Dynamic"))))

(I’ve changed the return values to strings to avoid confusion.)

Calling (f) returns the value “Lexical”:

> (f)
"Lexical"

Now we call f in an environment where lex has a different value, nil:

(let ((lex nil)) (f))

The question is, which value of lex should f now use?

With Dynamic Scoping f uses the value lex has when the function is called.

With Lexical Scoping f uses the value lex had when the function was defined.

Dynamic Scoping is sometimes useful; for example, if lex was a flag that controlled debugging output you could set it to nil to turn off debugging after the function was defined.

However, the consensus is that Lexical Scoping is preferred because it avoids obscure bugs due to the inadvertent reuse of variable names. For example, suppose we defined a function test using a parameter lex, forgetting that f uses lex:

(defun test (lex) (print lex) (f))

Now, in a Lisp with Dynamic Scoping, calling (test nil) changes the behaviour of f:

> (test nil)
nil
"Dynamic"

Note that if you want to try these examples on a Lisp with separate namespaces for variables and functions, like Common Lisp, change the calls to (f) in the above examples to:

(funcall f)