These three combinators build new predicates out of existing ones. negate flips a predicate's result; overEvery combines several with logical AND; overSome combines them with OR. Together they let you assemble complex conditions from small named checks — overEvery(isActive, isAdmin) reads better than a hand-written &&.
Implement negate(predicate), overEvery(...predicates), and overSome(...predicates). Each returns a new predicate that forwards all its arguments to the inner predicate(s) and short-circuits like && / ||.
function negate(predicate) {} // (...args) => !predicate(...args)
function overEvery(...predicates) {} // true if ALL pass (AND)
function overSome(...predicates) {} // true if ANY pass (OR)
const isEven = (n) => n % 2 === 0;
const isPositive = (n) => n > 0;
negate(isEven)(3); // true — 3 is not even
overEvery(isEven, isPositive)(4); // true — even AND positive
overSome(isEven, isPositive)(3); // true — positive (OR)
overEvery(isEven, isPositive)(-4); // false — positive fails
overSome(isEven, isPositive)(-3); // false — neither passes
negate — returns the boolean opposite of the predicate's result.overEvery is AND — true only when every predicate passes; short-circuits at the first failure.overSome is OR — true when any predicate passes; short-circuits at the first success.overEvery() with no predicates is true; overSome() is false (matching &&/|| identities).