clojurehigher-order-functionspointfree

Higher-order if-then-else in Clojure?


I often have to run my data through a function if the data fulfill certain criteria. Typically, both the function f and the criteria checker pred are parameterized to the data. For this reason, I find myself wishing for a higher-order if-then-else which knows neither f nor pred.

For example, assume I want to add 10 to all even integers in (range 5). Instead of

(map #(if (even? %) (+ % 10) %) (range 5))

I would prefer to have a helper –let's call it fork– and do this:

(map (fork even? #(+ % 10)) (range 5))

I could go ahead and implement fork as function. It would look like this:

(defn fork
  ([pred thenf elsef]
   #(if (pred %) (thenf %) (elsef %)))
  ([pred thenf]
   (fork pred thenf identity)))

Can this be done by elegantly combining core functions? Some nice chain of juxt / apply / some maybe?

Alternatively, do you know any Clojure library which implements the above (or similar)?


Solution

  • As Alan Thompson mentions, cond-> is a fairly standard way of implicitly getting the "else" part to be "return the value unchanged" these days. It doesn't really address your hope of being higher-order, though. I have another reason to dislike cond->: I think (and argued when cond-> was being invented) that it's a mistake for it to thread through each matching test, instead of just the first. It makes it impossible to use cond-> as an analogue to cond.

    If you agree with me, you might try flatland.useful.fn/fix, or one of the other tools in that family, which we wrote years before cond->1.

    to-fix is exactly your fork, except that it can handle multiple clauses and accepts constants as well as functions (for example, maybe you want to add 10 to other even numbers but replace 0 with 20):

    (map (to-fix zero? 20, even? #(+ % 10)) xs)
    

    It's easy to replicate the behavior of cond-> using fix, but not the other way around, which is why I argue that fix was the better design choice.


    1 Apparently we're just a couple weeks away from the 10-year anniversary of the final version of fix. How time flies.