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.