Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make vars constant for use in case statements in Clojure

Tags:

clojure

In Clojure, is there a way to make a var constant such that it can be used in case statements?

e.g.

(def a 1)
(def b 2)

(let [x 1]
  (case x
    a :1
    b :2
    :none))
=> :none

I understand I can use something like cond or condp to get around this, but it would be nice if I could define something that does not require further evaluation so I could use case.

like image 914
leeor Avatar asked Oct 29 '25 15:10

leeor


1 Answers

Related and answer stolen from it:

As the docstring tells you: No you cannot do this. You can use Chas Emericks macro and do this however:

(defmacro case+
  "Same as case, but evaluates dispatch values, needed for referring to
   class and def'ed constants as well as java.util.Enum instances."
  [value & clauses]
  (let [clauses (partition 2 2 nil clauses)
        default (when (-> clauses last count (== 1))
                  (last clauses))
        clauses (if default (drop-last clauses) clauses)
        eval-dispatch (fn [d]
                        (if (list? d)
                          (map eval d)
                          (eval d)))]
    `(case ~value
       ~@(concat (->> clauses
                   (map #(-> % first eval-dispatch (list (second %))))
                   (mapcat identity))
           default))))

Thus:

(def ^:const a 1)
(def ^:const b 2)
(let [x 1]
  (case+ x
    a :1
    b :2
    :none))
=> :1

An alternative (which is nice since it's more powerful) is to use core.match's functionality. Though you can only match against local bindings:

(let [x 2
      a a
      b b]
  (match x
    a :1
    b :2
    :none))
 => :2
like image 194
ClojureMostly Avatar answered Nov 01 '25 13:11

ClojureMostly



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!