UTILISP - Language features
Let’s look at some of the language features in UTILISP compared to the original MTS LISP.
Strings
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 bref
and bset
which can be used to access individual bits in a string.
Numbers
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 vref
and 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
Macros
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
INFANT
One interesting macro package that comes with UTILISP is INFANT (Infix Antidote), which allows infix notation for arithmetic expressions via !
:
> (! 2 + 3 * 4)
14
Further information
MTS Volume 22 describes the UTILISP implementation and how to run it on MTS.