CL-CUSTOM-HASH-TABLE: Custom hash tables for Common Lisp


CL-CUSTOM-HASH-TABLE allows creation and use of "custom hash tables". Custom hash tables can use arbitrary TEST/HASH functions, in addition to the TEST functions allowed by the standard (EQ, EQL, EQUAL and EQUALP).

This library is primarily a compatibility layer, unifying how to create these hash table in different Lisp implementations. Furthermore this library provides a simple yet fully functional fall-back solution for implementations that don't support this functionality natively (yet).


CL-CUSTOM-HASH-TABLE is released under a BSD-like license, see the ASD file.


This library does not shadow symbols in the COMMON-LISP package. It does require that all access to (potential) custom hash tables is lexical wrapped in a WITH-CUSTOM-HASH-TABLE form (see example below).

The standard hash table related functions are supported:

Hash table iteration using LOOP (using HASH-KEY or HASH-VALUE) is not supported in Lisp implementations where the fall-back solution is used.

Supported implementations


Custom TEST and HASH functions:

 (defun foo-equal-p (x y) (= x y))
 (defun foo-hash (x) (mod x 10))

Define the hash table type:

 (use-package :cl-custom-hash-table)

 (define-custom-hash-table-constructor make-foo-ht
    :test foo-equal-p :hash-function foo-hash)

Now MAKE-FOO-HT is a function that will create the custom hash table:

 (defparameter *foo-ht* (make-foo-ht)
   "Hash table using FOO-HASH and FOO-EQUAL-P")

You can trace your test/hash functions to check they are really getting called later:

 (trace foo-equal-p foo-hash)

Use WITH-CUSTOM-HASH-TABLE around access to the hash table. This ensures functions GETHASH, REMHASH and MAPHASH do the right thing. If you forget this, your code will not work in implementations that don't support custom TEST/HASH functions natively!

    (setf (gethash 1 *foo-ht*) 1
          (gethash 10 *foo-ht*) 10
          (gethash 2 *foo-ht*) 2)
    (maphash (lambda (k v) 
                (format t "~A: ~A~%" k v)
                (remhash k *foo-ht*))

Implementation details

Several Lisp implementations already support custom TEST and HASH arguments for MAKE-HASH-TABLE. This library is a small wrapper around the vendor-specific extensions. (Allegro CL, CCL, CMUCL, LispWorks, SBCL)

In other Lisp implementations (ECL) a fall-back solution is used:

How does this compare to genhash?