Boolean Operators on Series of Predicate Values
Yesterday I wanted to apply a Boolean operator to the results of calling a predicate on some variables. That is, I wanted to do something like this:
(apply #'and (mapcar #'predicate var-list))
Except hopefully shorter, because the code I didn’t feel like writing was the case where the var-list had two elements… Simply a nuisance to type out both the predicate calls, after all.
The problem was: AND is a macro, so can’t be applied. (Thought: Is a macro (call it macro-apply) that works analogously to APPLY for macros possible? Would presumably involve on-the-fly compilation/interpretation.)
Solution: The macros ANDS, ORS, NANDS and NORS (s for serial as in serial application of the same predicate).
(eval-when (:compile-toplevel :load-toplevel :execute)
(defun %pred-series (pred vars)
(mapcar (lambda (var)
`(,pred ,var))
vars)))
(defmacro ands (pred &rest vars)
`(and ,@(%pred-series pred vars)))
(defmacro ors (pred &rest vars)
`(or ,@(%pred-series pred vars)))
(defmacro nands (pred &rest vars)
`(not (and ,@(%pred-series pred vars))))
(defmacro nors (pred &rest vars)
`(not (or ,@(%pred-series pred vars))))
These saved me the horror of writing e.g.:
(and (predicate v1) (predicate v2))
Since I could now simply type:
(ands predicate v1 v2)
instead. Clear, concise, lazy.
Turns out I’m not the first to want to apply some Boolean operator to a series of predicate values. There’s a page in the HyperSpec detailing the functions EVERY, SOME, NOTEVERY, NOTANY (which are, of course, and, or, nand, nor, respectively). So the above ANDS is equivalent to:
(every #'predicate (list v1 v2))
Which requies slightly more typing. But EVERY & Co. are already in the language, and already support predicates of arbitrary arguments. So this post is an eulogy to the shortlived ANDS & Co.