Common Lisp compatibility


#1

I’ve been thinking about this, and the description of uLisp as (mostly) a subset of Common Lisp that can be found in various places. To what extent is this a significant consideration?

Specifically, I know that eval in uLisp does not behave the same way it does in Common Lisp. In Common Lisp, eval does not have access to the current lexical environment, as it is a function. In uLisp, it does. Assuming x is not defined as a global variable, this means that (let ((x 1)) (eval 'x)) returns 1 in uLisp, but raises a condition (specifically, UNBOUND-VARIABLE) in Common Lisp. (If x is special, eval can read it, so CL won’t error out.)

Is this to be considered a bug in uLisp, or is it simply a difference in behaviour that the programmer needs to account for? I’m not certain, because there is one aspect of uLisp that is glaringly incompatible with Common Lisp, although most of the time it can be papered over. uLisp is a Lisp-1, while Common Lisp is a Lisp-2. This means that you can do things in uLisp that Common Lisp can’t quite keep up with. For a demonstration:

(let ((x (lambda () 1)))
  (x))

This code will run fine in uLisp. It will cause an error in Common Lisp, or worse, call a different function. One way of keeping to the concept that uLisp code should run fine in Common Lisp here is to simply say that uLisp doesn’t support lexically scoped functions, and having the evaluator only search the global environment for functions - but at that point, it really is a sort of Lisp-2, it just happens that the function value is always the same as the normal value.

To loop back to the initial question, I’m not saying that uLisp code must be able to run in Common Lisp. I’m wondering to what extent the ability to do so should decide what uLisp does - because to the extent uLisp allows things Common Lisp doesn’t, it isn’t a subset, and in that case it would be good to know which differences are intentional and which are accidental.


#2

Yes, the major incompatibility with Common Lisp is:

There is one namespace for functions and variables.

Most target users of uLisp won’t know about Lisp-1s and Lisp-2s, but they do need to know that they can’t have a variable with the same name as a function they’ve defined.

The main consideration is that the syntax of uLisp should be a subset of Common Lisp. For example, it would have been easier to define make-array as:

(make-array dimensions [initialvalue])

but to make it compatible with Common Lisp the definition is:

(make-array dimensions [:initial-value initialvalue])

Perhaps the initial statement should be a bit weaker:

The aim is to make it possible to write uLisp programs that run under Common Lisp with identical behaviour


#3

That’s a very good way to put it. The edge cases are certainly in places where people for the most part need to go looking for them. I do wonder if it would make sense to have flet and labels as aliases, in the same way #' really exists only for compatibility purposes.

As for eval, I believe the restriction on the lexical environment is a sanity check. Using eval on the same list from two different lexical environments can suddenly give you different values. Worse, using eval can now change the values of lexical variables under your feet. It probably won’t, given the twistedness of the code needed to make it happen, but that wouldn’t be a fun bug to track down.