# Common Lisp accessors

The Common Lisp setf function is deceptively humble looking. At the start I didn’t think much of it – except that it looked eccentric compared to the assignment syntax from other languages – but recently I’ve found out how powerful it is, though not quite as convenient as I would hope.

In most languages to perform a variable assignment one does something like

```a = 22;
```

which places the value “22” into a place in memory which has been given the label “a” in the program.

In Common Lisp the corresponding syntax is

```(setf a 22)
```

and the only thing weird about it is the everything-is-a-list pattern of Lisp.

I was blown away, though, when I saw code that looked like this

```(setf x `(1 2 3 4 5))  ; x -> (1 2 3 4 5)
(setf (rest x) `(6 7 8))  ; x -> (1 6 7 8)
```

You can’t do this as transparently in C++ or Python. You can implement a function to do the equivalent thing, but you can’t assign a value to the return value of a function. This Lisp syntax makes writing code very compact and logical.

However, I stumbled when I tried to use this in a function I wrote. My function started at the root of a complete binary tree and then snaked recursively through it to find and return a particular node.

```;; Use the binary representation trick to thread through the
;; tree to a given node. No bounds checking is done here
; root - the root of the tree
; index - the index number of the node (1 = root)
(defun find-node (root index)
(multiple-value-bind
(n-idx rem) (floor index 2)
(if (= n-idx 1)
(find-node-pick-child root rem)
(find-node-pick-child (find-node root n-idx) rem))))

(defun find-node-pick-child (nd rem)
(if (= rem 0) (node-lchild nd) (node-rchild nd)))
```

When I tried to execute the statement (which I expected to attach new-node to the place returned by find-node)

```(setf (find-node root 8) new-node)
```

I ran into compiler errors.

This lead me on a very interesting chase all round the internet with the word “generalized variables” turning up a lot. It turns out that (setf …) is a lisp macro that can figure out how to get hold of the “place” its first argument refers to. If the first argument is just a variable, (setf …) acts just like the assignment statement in any other language. If the first argument is a function, Lisp will look for a setter form for that function and setf on the place returned by that function (which is called an accessor function).

Following advice from answers on comp.lang.lisp, I came up with the following code:

```;; Use the binary representation trick to thread through the
;; tree to return an tree node and some auxiliary information
;; No bounds checking is done here
; root - the root of the tree
; index - the index number of the node (1 = root)
; Return value is a pair
; node - a node of the tree
; info - :me - this is the node we're looking for
;        :lchild - we want the left child
;        :rchild - we want the right child
(defun find-node-with-index (root index)
(multiple-value-bind
(new-idx rem) (floor index 2)
(cond
((= new-idx 0) (values root :me))
((= new-idx 1) (values root (if (= rem 0) :lchild :rchild)))
(t (find-node-with-index
(if (= rem 0) (node-lchild root) (node-rchild root))
new-idx)))))

(defun node-with-index (bh index)
(multiple-value-bind
(nd info) (find-node-with-index (bheap-root bh) index)
(case info
(:me (bheap-root bh))  ; Special case - root of heap
(:lchild (node-lchild nd))
(:rchild (node-rchild nd)))))

;; The accessor or setter function. Note the "setf"
(defun (setf node-with-index) (new-node bh index)
(multiple-value-bind
(nd info) (find-node-with-index (bheap-root bh) index)
(case info
(:me (setf (bheap-root bh) new-node))  ; Special case - root of heap
(:lchild (setf (node-lchild nd) new-node))
(:rchild (setf (node-rchild nd) new-node)))))
```

I like the DRY principle primarily because it reduces bugs and the total amount of code. I tried to abstract as much of the core functionality into a single function as I could, but the getter and setter functions look so duplicate, but I don’t think I can reduce them further.

Thanks go to Kaz Kylheku and Barry Margolin on comp.lang.lisp for answering my questions on this topic and to Rainer Joswig for corrections to this post.