Say I have a function:
(defun distribution-to-list (distribution)
(check-type distribution hash-table)
(loop for key being each hash-key of distribution
using (hash-value value) nconc (loop repeat value collect key)))
I want to ensure that at least all the values of the hash-table that are passed in are integers, as I'm using them to repeat values into a big list. Is there any way to do so with check-type
before the inner loop? Or would it be good enough practice to let the inner loop macro throw a type error when it tries to repeat
a string? (or whatever non integer type)
If you can write a function that can check whether a value is acceptable, then you can use satisfies to construct a type specifier, such as (satisfies is-acceptable)
. E.g.,
(defun all-integer-keys-p (ht)
(loop for k being each hash-key in ht
always (integerp k)))
(let ((h (make-hash-table)))
;; when the table contains only integer
;; keys, we're fine
(setf (gethash 1 h) 'foo
(gethash 2 h) 'bar)
(check-type h (satisfies all-integer-keys-p))
;; but a non-integer key will lead to an
;; error from CHECK-TYPE
(setf (gethash 'three h) 'baz)
(check-type h (satisfies all-integer-keys-p)))
With deftype, you can define a type as shorthand for (satisfies all-integer-keys-p)
, which you may find more readable:
(deftype all-integer-key-hash-table ()
`(satisfies all-integer-keys-p))
(let ((h (make-hash-table)))
(setf (gethash 1 h) 'foo
(gethash 2 h) 'bar)
(check-type h all-integer-key-hash-table)
(setf (gethash 'three h) 'baz)
(check-type h all-integer-key-hash-table))