Simon Willison’s Weblog

EuLisp

EuLisp

EuLisp gives programmers the ability to define their own error handlers. The default error handler used by the interactive interpreter displays a description of the error and starts the debug loop.

There are several types of function in EuLisp.—and + are both procedures, cons is a subroutine and there are generic functions as well. The differences between these types are minimal. The type of a function can be found by entering it at the interactive prompt:

> +
#<Procedure +>
> cons
#<Subr cons>
>

Modules are a way of structuring programs as independant chunks. These stand alone parts can then be used to construct much larger programs, which is a key principle of software engineering. The default module used within the interactive interpreter is “user”—hence the following error message:

> blah
Continuable error---calling default handler:
Condition class is #<class unbound-error>
message:        "variable unbound in module 'user'"
value:          blah

Debug loop.  Type help: for help
Broken at #<Code #1273d8>

Lists and symbols need to be quoted in order to be processed as data. Number, strings, floating point numbers etc all evaluate as themselves. Note that '3 evaluates as the number 3 and not as a symbol—som Lisp versions do allow symbols like this but most do not.

You can force symbols with pipes: |3|, |a symbol with a space| but doing so is generally considered bad practise and should be aboided.

Characters in EuLisp look like this: #\h (for the character ’h’). #\newline is a newline. The string-ref function returns a single character from a string:

> (string-ref "hello" 1)
#\e
>

Vectors

A vector is essentially a fixed-length list. While normal lists in Lisp are dynamic and can be spread about throughout system memory, vectors stay in a fixed section of memory and are stored together. This makes vectors far more efficient when it comes to indexing operations, which have to be performed recursively on lists. A vector looks like this: #(1 2 3). The vector-ref function can be used to retrieve an element from a vector:

> (vector-ref #(a b c) 2)
c
>

The vector-length function returns the length of a vector, while the string-length function does the same for a string:

> (vector-length #(1 a c f))
4
> (string-length "hi there")
8
>

Vectors can be created in several different ways. The make-vector function creates a vector of a specified length, and can optionally be provided with a value to pre-populate the vector fields with.

> (make-vector 4)
#(() () () ())
> (make-vector 3 'a)
#(a a a)
> (vector 1 2 3 'a 'b 'c)
#(1 2 3 a b c)
>

We can add items to a vector in a specific position using (setter vector-ref). This is our first example of a function that returns a function:

> ((setter vector-ref) #(1 2 3) 0 'a)
a
>

The statement returns a because it is the last value evaluated in the expression. The vector has been changed, but we can not see the effect as the vector itself is not returned.

EuLisp also has a data structure called table, which is a very useful hash table implementation. Any object can be used as a key for any other object stored in a table.

Expressions

In Lisp, every expression returns a value. For example, if is used in the following way:

> (if (< 1 2) 2 3)
2
>

The if expression evaluates the first argument as a boolean and returns the corresponding argument (arg2 if the result is true, arg3 if it is false). There is a slight exception to normal Lisp in that the statement will only evaluate the argument it is going to return. This is an example of a special form. If this were not the case the following code would display both “hello” and “bye”:

> (if (< 1 2) (print "hello") (print "bye"))
hello
"hello"
>

Hello is displayed twice. The first hello is the result of the print statement; the second “hello” is the value returned by the overall expression. This is an interesting demonstration of how the print function acts somewhat like the identity function—it returns its arguments unchanged.

> (if (if (= 1 2) #t #f) 4 5)
5
>

The above code is an example of a nested if statement, and also demonstrates two constants used in Lisp (although constants begin with a # they should not be confused with vectors). #t means true and #f means false. #f is actually a variable containing (), the empty list (also known as ’nil’). In Lisp the only thing that evaluates as false is the empty list—even 0 evaluates as true, which can be a gotcha to new programmers.

> (if 1 2 3)
2
> (if #t 2 3)
2
> (if 0 2 3)
2
> (= 1 2)
()
>

Useful Functions

The member function returns the rest of a list after the first occurrence of the specified value:

> (member 1 '(2 3 4 1 2))
(1 2)
> (member 2 '(2 3 4 1 2))
(2 3 4 1 2)
>

map takes a function and applies it to every element in a list:

> (map - '(1 2 3))
(-1 -2 -3)
> (map print '(1 2 3))
1
2
3
(1 2 3)
> (map list '(1 2 3) '(a b c))
((1 a) (2 b) (3 c))
> (map list #(1 2 3) '(a s d f))
#((1 a) (2 s) (3 d))
> 

Note that map returns a newly created list—it does not modify the original list.

This is EuLisp by Simon Willison, posted on 3rd October 2002.

Next: Lisp special forms

Previous: Googlebad

Previously hosted at http://simon.incutio.com/archive/2002/10/03/euLisp