Let's look at some of the language features in UTILISP compared to the original MTS LISP.
UTILISP brings support for strings, which can also be treated as a sequence of bytes that can be accessed independently.
> (setq s "hello world") "hello world" > (string-length s) 11 > (sref s 1) 133 > (sset s 1 134) 134 > s "hfllo world" > (string-search "world" s) 6 > (cutout s 0 2) 34950
In the last expression, we take the first two bytes of s and extract it as an integer. There's also
bset which can be used to access individual bits in a string.
UTILISP has both fixed and floating point numbers; arithmetic operators for floating point numbers have
$ at the end of the keyword to distinguish them.
> (setq f 0.0) +0.0000000?+00 > (setq i 0) 0 > (0= i) T > (0= f) @@@ ILLEGAL ARGUMENT TYPE +0.0000000?+00 -- C#/0= > (0=$ f) T
Vectors and references
Vectors are implemented as a primitive data type: they are created with
vector and addressed with
vset. Vectors can contain any LISP data types. Here we create a four element vector, initially filled with the number 42 in each slot.
> (setq v (vector 4 42)) V#-8057044 > v V#-8057044 > (vector-length v) 4 > (vref v 0) 42 > (vset v 0 43) 43 > (vset v 1 "hello") "hello"
You can set references, which are pointers to elements of the vector which can then be dereferenced or updated through the reference.
> (setq r (reference v 3)) R#-8057028 > (deref r) 42 > (setref r 99) 99 > (vref v 3) 99
mapv can be used to operate on each element of a vector; it passes a reference to each element.
> (mapv v (function (lambda (r) (print (deref r))))) 43 "hello" 42 99 V#-8057044
There is also a
hash function that could be used to build efficient lookup tables using vectors.
> (hash 32) 32 > (hash "hello") 373277
UTILISP has macro support similar to modern LISPs, with selective quoting and evaluation via backtick and comma. Here we define a macro that will change a variable to nil if it is currently t.
> (defmacro t-to-nil (v) `(cond ((eq ,v t) (setq ,v nil)))) T-TO-NIL > (setq a 1) 1 > (setq b t) T > (t-to-nil a) NIL > (t-to-nil b) NIL > a 1 > b NIL
Scope and closures
UTILISP has dynamic scope and no lexical-let. There also seems to be no funarg implementation although this was available in Maclisp. It is possible to mimic closures with macros:
> (defun make-adder (init) (let ((sym (gensym))) (set sym init) `(lambda ( val) (cond (val (setq ,sym (+ ,sym val))) (t ,sym))))) MAKE-ADDER > (setq aa (make-adder 40)) (LAMBDA (VAL) (COND (VAL ?) (T G0002))) > (funcall aa 1) 41 > (funcall aa 2) 43
One interesting macro package that comes with UTILISP is INFANT (Infix Antidote), which allows infix notation for arithmetic expressions via
> (! 2 + 3 * 4) 14
MTS Volume 22 describes the UTILISP implementation and how to run it on MTS.