Comparing MTS LISP to modern Lisps
LISP is quite different from the languages we’ve looked at so far - but I will not give an introductory description, as there are plenty of resources on the Internet by better writers than me - instead I will look at how MTS LISP differs from more modern Lisps.
In the examples below, input is prefixed with a *
and output with >
.
Representation
MTS LISP assumes all keywords are in uppercase, but is otherwise free format. Do note that if you put the comment character ;
in the first column this will be treated as a carriage control instruction to advance to the start of the next page in listings, so best prefix this with a space.
Types
As in other LISPs, the primitive data structure is the atom, which have values and property lists; atoms can be combined in s-expressions to form binary trees or lists. Here we define three atoms, a, b, c:
* (SETQ A 42)
> 42
* (SETQ B 'A)
> A
* (SETQ C (CONS A B))
> (42 . A)
There are no strings, but you can double-quote atom names to get printable output:
* (SETQ GREETING '"Hello, world!")
> Hello, world!
* (PRINT GREETING)
> Hello, world!
Arrays are simulated using functions: for a 2x2 array:
* (DEFINE (A ARRAY (2 2)))
> A
* (SETA (A 1 2) 42)
> 42
* (PRINT (A 1 2))
> 42
Standard functions
Many familiar Lisp functions for list manipulation, arithmetic and control flow are available in this version; a brief sample:
* (SETQ L1 (LIST 1 2 (SUB 10 7)))
> (1 2 3)
* (SETQ L2 (LIST 1 2 (SHIFT 2 2)))
> (1 2 8)
* (CADR L2)
> 2
* (RPLACA L2 42)
> (42 2 8)
* (DELETE 8 L2)
> (42 2)
* L2
> (42 2)
* (MAPC 'PRINT L2)
> 42
* 2
> NIL
Functions and scope
As expected, you can use LAMBDA
to define an anonymous function and DEFUN
to attach a lambda function to an atom
* ((LAMBDA (A) (ADD A 2)) 40)
> 42
* (DEFUN FACTORIAL (N) (COND ((LESS N 2) 1)
* (T (TIMES N (FACTORIAL (SUB1 N))))))
> FACTORIAL
* (FACTORIAL 5)
> 120
*LISP
has dynamic scoping; variables defined in a function, or in a PROG
, replace the current definitions for the life of the expression, including calls to other functions. See below, where the definition of X
in the PROG
is available in the GETX
function.
* (SETQ X 42)
> 42
* (DEFUN GETX () X)
> GETX
* (GETX)
> 42
* (PROG (X) (SETQ X 3) (GETX))
> 3
* (GETX)
> 42
There is no let
or lexical binding in *LISP
, hence no lexical closures. It may be possible to do dynamic closures but there does not seem to be function
or funargs
available which is needed to implement this.
There are also no macros (like defmacro
) which can be expanded and evaluated at run time; there is a basic macro facility called READMACRO
and PRINTMACRO
where a function can be called whenever a designated atom is encountered while reading or printing.
GOTO in LISP
This rather blew my mind: a GOTO in LISP? The below will loop and read input until the user enters a number larger than 99. A
and Z
are labels:
(PROG (X)
A (SETQ X (READ))
(COND ((GREATER X 99) (GO Z))
(T (GO A)))
Z (PRINT 'DONE))
There is also (RETURN x)
, which will break out of the enclosing PROG
immediately.
Super parentheses
Lisp is famously parenthesis-heavy: today we have text editors that can show matching pairs and indent code automatically but contemporary users of LISP would have had to do this by hand. To help out, *LISP
defines the angle-brackets {}
as super-parentheses: whenever an opening brace is encountered, the depth is remembered and when the matching close is found, the depth returns to that level. For example:
* (SETQ A (CAR (CDR (CDR (LIST 'A 'B 'C 'D)))))
> C
* (SETQ A <CAR (CDR (CDR (LIST 'A 'B 'C 'D>)
> C
Worlds
*LISP
has a facility similar to database transactions where you can store the current state, change data and then either commit or rollback. (NEWWORLD)
will return a ticket to the current state; you can then change data with special versions of LISP primitives such as SETQ2
or RPLACA2
. (REALWORLD)
can then be used to commit or (GETWORLD x)
to roll back to the state at the time of ticket x.
* (SETQ2 A '42)
> NEWWORLD 04-15-82 RESTORED
> 42
* (SETQ W (NEWWORLD))
> (((0 *UNDEF* . 42)) (NIL))
* (SETQ2 A 33)
> 33
* A
> 33
* (GETWORLD W)
> NIL
* A
> 42
Further information
Chapter 3 of Successful Lisp is a good introduction to Lisp if you’ve not used it before; it’s written for Common Lisp users but most is applicable here.