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.

Click Here to Leave a Comment Below