Friday, August 15, 2008

Struquine: A Useful Lisp Trick

Records or structures - bundles of named objects - are ubiquitous in programming languages. A simple example in C might be a struct representing a 2d point.

typedef struct point_tag {
   double x, y;
} point;

point p;
p.x = 10;
p.y = 11;

Lisp is a list-based language, and although most modern lisps provide some higher level abstraction for structures or records, it is not uncommon to use simple lists and functions to represent them. There is one very nice feature of this approach which I have identified as a repeated pattern in a lot of my lisp code which uses the fact that lists are the main data type in Lisp to very good effect. Consider the Lisp approach to the point struct, assuming we don't want to use a built-in structure type and don't have access to something as excellent as CLOS. (This is emacs lisp with (require 'cl) - but probably also Common Lisp).

(defun point! (x y)
    (list 'point! x y))

(defun point-x (p)
    (cadr p)) ;; return the second element of the list p
(defun point-y (p)
    (caddr p)) ;; return the third element of the list p

(defun set-x (p v)
    (setf (cadr p) v))
(defun set-y (p v)
    (setf (caddr p) v))

(defun point-+ (p1 p2)
    (point! (+ (point-x p1) (point-x p2))
         (+ (point-y p1) (point-y p2))))

(let ((p1 (point! 3 4))
   (p2 (point! 3 3)))
   (point-+ p1 p2)) ;; evaluates to (point! 6 7)
  

What is useful about this is that the default printed representation of our data structure is the code which, when executed, makes a structurally identical data structure. If you are programming functionally, then structural equality is what you care about 85% of the time anyway. I find this trick useful for working with Emacs because it is easy to insert the result of a calculation into a buffer, where you then may want to put it into other forms to be evaluated later. I found myself creating this sort of data structure a lot in Emacs Lisp, Common Lisp and Scheme, so I wrote the following Emacs lisp macro to do some of the finger work for me.

(defun bang (sym)
  (intern (format "%s!" sym)))
(defun s-cat (sym1 sym2)
  (intern (format "%s-%s" sym1 sym2)))
(defun ques (sym)
  (intern (format "%s?" sym)))

(defmacro defstruquine (name &rest slots)
  (let* ((n-fields (length slots))
   (i 1)
   (out `(progn
     (defun ,(bang name) ,slots
       (list ',(bang name) ,@slots)) 
     (defun ,(ques name) (item)
       (eq (car item) ',(bang name))))))
 (loop for slot in slots do
    (setf out 
    (append out
      (list `(defun ,(s-cat name slot) (item) (elt item ,i)))))
    (setf i (+ i 1)))
 (append out (list nil))))

Which can be used thusly:

(defstruquine person first-name last-name age)

(let ((p (person! "Edward" "Olmos" 61)))
  (person-first-name p) ;; is "Edward"
  (person-age p) ;; is 62
  p) ;; returns (person! "Edward" "Olmos" 61)


Which I think is a small example of why Lisp works so well - you really couldn't do this in any other language without a lot of conceptual overhead. PS - This is called a struquine because a quine is a program which, when evaluated, prints itself. Check it out on the wikipedia.

4 comments:

blandest said...

I like this idea of building simple stuff on your own instead of relying on a complex library.

One small note: it won't work in Common Lisp because of the `format' function that uses C-like format parameters.

Anonymous said...

DEFSTRUCT in Common Lisp can also be used with lists and vectors. There is an option to specify a type.

For example:

(defstruct (foo-list (:type list)) a b)

If you do (make-foo-list) , it will create a list and not a structure object.

Art and Poetry said...

I hope you don't mind me coming to your blog. I see you are interested in Quantum mechanics I am only an artist but I have a theory on the two slit experiment. It would be nice to know what you think of it?
I have linked Newtons laws of motion with gravity and time.

All the best Nick

J.V. Toups said...

Nick, I have read your theory of the two slit experiment, and while the description is evocative, it doesn't constitute a real theory as such.

Quantum Mechanics makes actual, numerical predictions about the results of the two-slit experiment, and is able to reproduce the exact shape of interference patterns from a purely theoretical basis. The ability to make clear, numerical predictions about the universe is a necessary quality of a successful physical theory.

The good news is that, despite what people say, Quantum Mechanics is a very simple theory, and I am certain you could pick up the basics with a bit of time and effort - for instance, the prediction of the shape of the two-slit interference pattern is accessible to anyone with college level calculus and a semester of quantum mechanics. Have you considered taking a class?