Search code examples
clojurelispconditional-statementscommon-lisp

Consolidated cond arguments in Clojure (CL style)


In Clojure I do this

(println (cond false "don't care" "otherwise" "otherwise"))

In Common LISP this would be

(print (cond (nil "don't care") ("otherwise") ))

Is there a way to get this kind of simplified cond in Clojure?


Solution

  • Version which includes a fix for that Alex Taggart noticed below. Passes all the test cases shown in the test. It allows for arbitrary clauses passed to my-cond to be length 1 instead of 2 which results in the length 1 clause being both the test for truthiness and the result if it is true. Based off my limited experience with CL I actually think this behavior is different from what cond does but seems to be in line with how I've interpreted what you're asking for. Kotarak's answer seems to line up with the CL one as using the last statement in the CL cond seems to match up with using the :else clause in the Clojure version.

    Regardless here is a solution where it should allow any clause to be a length of one and use that for both the truth test and result.

    (defmacro my-cond
      [& others]
      (if others
        (let [more# (next others)
              extra-clauses# (if more# `(my-cond ~@more#))
              clause# (first others)]
          (if (= 2 (count clause#))
            `(if ~(first clause#) ~(second clause#) ~extra-clauses#)
            `(if ~(first clause#) ~(first clause#)  ~extra-clauses#)))))
    
    
    (deftest my-cond-works
      (is (= 3 (my-cond (false 1) (false 2) (3))))
      (is (= "otherwise" (my-cond (false "don't care") ("otherwise"))))
      (is (= nil (my-cond (:one nil) ("otherwise"))))
      (is (= "care" (my-cond (1 "care") ("otherwise"))))
      (is (= "otherwise" (my-cond (false "care") ("otherwise") (true "true"))))
      (is (= "silly" (my-cond (nil "no") (nil) ("yes" "silly")))))
    

    I'd really advise translating the CL over to the Clojure form of cond. I would place the mental overhead of allowing the CL syntax along with the Clojure syntax in the same project as not worth saving time in translating it now. Looking at the code in the future after becoming used to how Clojure's cond and trying to remember why the other syntax is there seems not worth the time saved by not translating.

    Below version fails as Alex Taggart says below. Keeping it here so his comment makes sense. The below version does:

    (defmacro my-cond [[if1 then1] & others]
      (when (or if1 then1 others)
        (let [extra-clauses# (if others `(my-cond ~@others))]
          (if then1
            `(if ~if1 ~then1 ~extra-clauses#)
            `(if ~if1 ~if1  ~extra-clauses#)))))
    
    user> (my-cond (false "first") (nil nil) ("otherwise"))
    "otherwise"