Lisp toys

Some small things of no real use

Subject-verb-object syntax

Lisp is a `verb-noun' language: apart from special forms and some macros, its syntax is uniformly (fn argument ...). Where fn is the `verb' and the arguments are the things - `nouns' on which the verb operates.

Most traditional programming languages are also verb-noun. Syntaxes like fn(arg, ...) abound. However some recent so-called `object oriented' languages are `noun-verb', at least in their OO parts. Thus you see syntaxes like object.operator(...) where object is the `noun', or subject, operator is the `verb', and the arguments are `objects'. It has been argued - apparently seriously - that this syntax is an important component of object orientation.

Clearly, this syntax is very resrictive, since it effectively assumes a single-dispatch system where there is always exactly one privileged `subject'. For most `OO' languages (SmallTalk is a notable exception) things also break down horribly, since there is a conventional `verb-object' syntax as well as the `subject-verb' syntax. This is particularly horrible because it means that you always know whether you are doing `OO' or `ordinary' programming. In particular an interface defined one way can never transparently migrate to the other form. In Lisp you never need know that something has changed.

Be that as it may, if you want this subject-verb-object syntax for Lisp, you can have it: all you need do is twiddle the first two things that read returns. This code does it using [subject verb object ...] instead of (verb subject object ...).

See here for some notes on read-delimited-list and related things in CL.

Downloading the file, contact information

Copyright slip.lisp is copyright 2000 by me, Tim Bradshaw, and may be used for any purpose whatsoever by anyone. It has no warranty whatsoever. I would appreciate acknowledgement if you use it in anger, and I would also very much appreciate any feedback or bug fixes.
Here is the current version.
Please mail me with any bugs or comments.

Global lexical variables

In CL, there is no real notion of a `global lexical environment'. In particular there is no way of having a variable defined at top level which behaves the same way as lexical variables at other levels. Saying (defvar x ...) causes x to be globally special, while simply doing (setf x ...) is undefined, and therefore can be correctly intepreted by different implementations in different ways, including making daemons fly out of your nose.

The lack of global lexicals is not often a problem, although it causes endless confusion to people who either don't understand or don't want to understand what is going on. Fortunately, it's relatively easy to provide a simulation of global lexicals with symbol macros: all you do is define a secred `global binding environment' - a hash table, say, and then define global lexicals as symbol macros which get their value from this environment. A couple of refinements support unbound global lexicals, and help things work at compile time. Unfortunately, because there is no portable way to teach the compiler only about something, getting the correct compile time behaviour really involves compilation causing the lexical variable to become defined, which is not ideal, but should not hurt too much.

The user macro in this code is defglex (`DEFine Global LEXical'). An example of using it is as follows.

(defglex /x/ 1)

(defun foo (y)
  ;; this function can see the global lexical value of /X/
  (+ y /x/))

(let ((/x/ 2))
  (defun bar (y)
    ;; This function closes over the lexical value of /X/, 2.
    (+ y /x/)))

Downloading the file, contact information

Copyright glex.lisp is copyright 2003 by me, Tim Bradshaw, and may be used for any purpose whatsoever by anyone. It has no warranty whatsoever. I would appreciate acknowledgement if you use it in anger, and I would also very much appreciate any feedback or bug fixes.
Here is the current version.
Please mail me with any bugs or comments.

NCASE: case using a jump-table

This is an example of how you can use macros to do some hairy optimisation of code which would not be possible without either modifying the compiler or writing a macro system in languages without proper macros.

The macro ncase is semantically the same as case. However, in certain cases - when all the keys are integers, and when they pass a (possibly user-defined) test, it will rewrite the code using a jump-table - an array is created containing the bodies of the cases, and an element of that array is simply funcalled. This means that a possibly large number of tests are replaced by a single indirection and function call.

In real life, a compiler is likely to be able to make a better decision about whether or not to compile a jump-table than ncase can, and it will have lower-level access to the machine as well. However, this is still a good example of the sort of things that macros can do, and things `like' ncase are often useful in practice, and not realistically possible without macros.

Downloading the file, contact information

Copyright ncase.lisp is copyright 2003 by me, Tim Bradshaw, and may be used for any purpose whatsoever by anyone. It has no warranty whatsoever. I would appreciate acknowledgement if you use it in anger, and I would also very much appreciate any feedback or bug fixes.
Here is the current version.
Please mail me with any bugs or comments.

FOR: iteration in the style of Python

Python does iteration by having methods on classes which construct `iterators' - objects which iterate over the contents of the original object. This is similar to how some other languages do it, and it's probably a design pattern of some kind. The nice feature of something like this is that you can iterate over any object which supports the iteration protocol. The bad feature is that it's probably fairly hard to make iteration this way really efficient, although it's probably possible.

This implementation is fairly rudimentary - the protocol is probably inadequate, the iteration macro itself is fairly rudimentary, and iterators are only defined for a few types. The interesting things are:

iter
Make an iterator for an object. Keyword arguments potentially allow different types of iterators - for instance an iterator over a stream might be by characters or lines. Many simple iterators can just be be closures.
next
Step an iterator. Returns a second value to say if the iterator is exhausted or not. If the iterator is a function, just calls it with no arguments.
for
The macro that uses these things. Has a syntax a bit like let. Has a cleverness that if the object being iterated over looks like a call to iter it doesn't generate one itself, allowing you to use non-default args to iter.

There is also some support for `range' objects - these are things which will iterate over a range of numbers.

Downloading the file, contact information

Copyright for.lisp is copyright 2004 by me, Tim Bradshaw, and may be used for any purpose whatsoever by anyone. It has no warranty whatsoever. I would appreciate acknowledgement if you use it in anger, and I would also very much appreciate any feedback or bug fixes.
Here is the current version.
Please mail me with any bugs or comments.

List comprehensions, after Python

Based on the above code, it turns out to be extremely easy to write a macro, gather which works like a generalised version of Python's list comprehensions.

(gather (* x x)
  for (x '(1 2 t 3))
  when (numberp x)
  when (oddp x))

is equivalent to

(collecting
 (for (x '(1 2 t 3))
   (when (numberp x)
     (when (oddp x)
     (collect (* x x))))))

using a collecting macro which collects lists forwards.

A more elaborate example:

(gather x
  for (c (range :from 0 :below 100))
  for (l (range :from 1 :to c))
  let ((x (random l)))
  when (evenp x))

Is the same as

(collecting
 (for (c (range :from 0 :below 100))
   (for (l (range :from 1 :to c))
   (let ((x (random l)))
     (when (evenp x)
       (collect x))))))

Note that forms nest, as they do with Python's list comprehensions.

gather used to restric the `clause keywords' but in fact it doesn't need to - it simply rewrites things so that they nest appropriately, and so it can be used with any `standard pattern with-x macro, such as with-open-file, dolist or macros of your own invention.

gather is contained in for.lisp.

[TFEB]