Sebastian Oberhoff
Sebastian Oberhoff

Reputation: 1311

How can I use a result in both the test and the result of a cond?

I've got the following code:

(cond
  (case-1? (compute-1 x)) (compute-1 x)
  (case-2? (compute-2 x)) (compute-2 x)
  (case-3? (compute-3 x)) (compute-3 x))

I want to avoid repeated computations of compute-1, compute-2, and compute-3. One option is:

(let [result-1 (compute-1 x)
      result-2 (compute-2 x)
      result-3 (compute-3 x)]
  (cond 
    (case-1? result-1) result-1
    (case-2? result-2) result-2
    (case-3? result-3) result-3))

Now I'm not repeating computations, but instead if now (case-1? result-1) evaluates to true, result-2 and result-3 were computed for no reason.
Behaviorally I want something like this:

(let [result-1 (compute-1 x)]
  (if (case-1? result-1)
    result-1
    (let [result-2 (compute-2 x)]
      (if (case-2? result-2)
        result-2
        (let [result-3 (compute-3 x)]
          (if (case-3? result-3)
            result-3))))))

However this code is obviously quickly becoming unmanageable. Is there a better solution to this problem?

Upvotes: 2

Views: 75

Answers (1)

madstap
madstap

Reputation: 1552

There's a library called better cond that solves exactly that problem with a macro.

It's used like this:

(ns foo.core
  (:require
   [better-cond.core :as b]))

(b/cond
  :let [result-1 (compute-1 x)]
  (case-1? result-1) result-1

  :let [result-2 (compute-2 x)]
  (case-2? result-2) result-2

  :let [result-3 (compute-3 x)]
  (case-3? result-3) result-3)

That snippet will macroexpand to code that's similar to your last example.

Upvotes: 3

Related Questions