Every-pred for functions with multiple arguments

every-pred is great and it works nicely for functions that take a single argument. e.g.:

((every-pred string? #(< 5 (count %))) "lengthy") => true

But I wanted to make it work with functions that take more than one argument and something like this, doesn’t seem to work:

((every-pred (fn [a b]
              (and (number? a)
                   (number? b)))
              (fn [a b]
                 (< a b)))
1 2)

That throws an ArityException. I checked, the inner expression definitely retuns a function that takes two arguments. So why it doesn’t work then?

For archival, going to repeat my answer from the slack here as well:

every-pred only works with predicate functions of one argument, but to make it work with predicate functions of two or more args, you could simply wrap your predicate functions in a function of one argument.

For example, you could convert your preds of two args, into preds of one arg which take a vector of two elements:

((every-pred (fn [[a b]]
              (and (number? a)
                   (number? b)))
              (fn [[a b]]
                 (< a b)))
[1 2])

You can generalize this using apply as well:

;; Assume we have pred-x and pred-y, both take two args, and we
;; don't have control on them, so we can't change their definition, we
;; can use apply and anonymous functions instead:

(defn pred-x [a b]
  (= a b))

(defn pred-y [a b]
  (and (> 10 a)
       (> 10 b)))

((every-pred
   #(apply pred-x %)
   #(apply pred-y %))
 [5 5])
true

;; We can now make an even more generic function for this pattern:

(defn every-preds [& preds]
   (fn [& args]
     ((apply every-pred
             (mapv 
              (fn [p]
                (fn [args]
                  (apply p args)))
              preds))
      args)))

((every-preds pred-x pred-y) 5 5)
true
3 Likes

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.