frenchy64/fully-satisfies

`everyp`/`somef` operational equality bug

Closed this issue · 5 comments

This line:

(every? #(and (p1 %) (p2 %) (p3 %)) args)

should be

(and (every? p1 args) (every? p2 args) (every? p3 args))

Basically the same problem as https://clojure.atlassian.net/browse/CLJ-2649 but in the rest arity.

You think it should check all of p1 before checking all of p2, etc? My imperative intuition would loop over the arguments and check each predicate against them (as it works now).

Yes, I was surprised too. When I say "should", I'm referring to the operational equivalence with:

(defn everyp [& ps]
  (fn [& args] (every? #(every? % args) ps)))

I think the "imperative" version you're talking about of would be:

(defn everyp [& ps]
  (fn [& args] (every? (fn [x] (every? #(% x) ps) args)))

The problem is that the semantics are mixed. I think having both versions would be cool, but in different functions.

It's even worse than I thought. In these inner rest arities, ep3 checks x, y, z against p1, then p2 then p3, but then the every? starts all over again for the rest of the args, but in the opposite order.

       ([x y z & args] (boolean (and (ep3 x y z)
                                     (every? #(and (p1 %) (p2 %) (p3 %)) args))))))

ie., this is the checking order:

(p1 x) (p1 y) (p1 z)
(p2 x) (p2 y) (p2 z)
(p3 x) (p3 y) (p3 z)
(p1 args0)
(p2 args0)
(p3 args0)
(p1 args1)
(p2 args1)
(p3 args1)
...

I think it should be:

       ([x y z & args] (boolean (and (p1 x) (p1 y) (p1 z) (every? p1 args)
                                     (p2 x) (p2 y) (p2 z) (every? p2 args)
                                     (p3 x) (p3 y) (p3 z) (every? p3 args))))))

Smaller:

       ([x y z & args] 
        (let [test #(and (% x) (% y) (% z) (every? % args))]
          (boolean (and (test p1) (test p2) (test p3)))))

Same problem with somef. The arities that recur via sf2, sf3, sfn flip the order.