Simplify complex hash-table manipulations in Common Lisp I'm trying to write a simple triplestore in Common Lisp that will store triples in the form subject-predicate-object. The code is inspired by the book "Programming the Semantic Web".

(defvar triplestore-graph (make-hash-table))

(defun triplestore-init ()
  (puthash :spo (make-hash-table) triplestore-graph)
  (puthash :pos (make-hash-table) triplestore-graph)
  (puthash :osp (make-hash-table) triplestore-graph))

(defun triplestore-add (s p o)
  (triplestore-add-to-index :spo s p o)
  (triplestore-add-to-index :pos p o s)
  (triplestore-add-to-index :osp o s p))

(defun triplestore-add-to-index (index a b c)
  (let ((index (gethash index triplestore-graph)))
    (cond ((not (gethash a index))
           (let ((tmp (make-hash-table)))
             (puthash a
                      (progn
                        (puthash b
                                 (list c)
                                 tmp)
                        tmp)
                      index)))
          ((not (gethash b (gethash a index)))
           (puthash b
                    (list c)
                    (gethash a index)))
          (t
           (nconc (list c)
                  (gethash b
                           (gethash a index)))))))


Here (puthash k v table) is defined as (setf (gethash k table) v). The code above is roughly equivalent to the Python code:

graph = {}

def init(graph):
    graph['_spo'] = {}
    graph['_pos'] = {}
    graph['_osp'] = {}

def add(graph, (s, p, o)):
    _addToIndex(graph['_spo'], s, p, o)
    _addToIndex(graph['_pos'], p, o, s)
    _addToIndex(graph['_osp'], o, s, p)

def _addToIndex(index, a, b, c):
    if a not in index:
        index[a] = {b: set([c])}
    elif b not in index[a]:
        index[a][b] = set([c])
    else:
        index[a][b].add(c)


How can i improve readability and elegantness of the Common Lisp code? I'm mainly concerned that operations with hash-tables are verbose comparing to Python's equivalents. Are there macros for hash-table handling, for example?
First of all, I suggest that you use a structure instead of a hash table when you know that the table will only have 3 elements; this improves both readability and performance:

(defstruct triplestore-graph 
  (spo (make-hash-table))
  (pos (make-hash-table))
  (osp (make-hash-table)))
(defvar *triplestore-graph* (make-triplestore-graph))


Next, there is no puthash in Common Lisp, use (setf gethash) instead.

Finally, I would modify the code like this:

(defun triplestore-add (s p o)
  (triplestore-add-to-index (triplestore-graph-spo *triplestore-graph*) s p o)
  (triplestore-add-to-index (triplestore-graph-pos *triplestore-graph*) p o s)
  (triplestore-add-to-index (triplestore-graph-osp *triplestore-graph*) o p s)) ; o s p?!

(defun triplestore-add-to-index (index a b c)
  (let ((at (gethash a index)))
    (if at
        (pushnew c (gethash b at))
        (setf (gethash a index)
              (let ((tmp (make-hash-table)))
                (setf (gethash b tmp) (list c))
                tmp)))))