Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ A variation on cond which sports let bindings, when-let bindings, when and impli
*New in version 2.0.1:*

- Cond supports `do` for a single-line side effect.
- Cond allows symbols as an alternative to keywords for let, when-let, when, and do.
- Cond allows symbols as an alternative to keywords for let, when-let, when, while, when-some and do.
- Two new macros: `defnc` and `defnc-` are like `defn` and `defn-` with an implicit cond wrapped around the body.

`better-cond 2.0.1` requires Clojure 1.9 alpha 16 or higher. If you are still on Clojure 1.8, use `better-cond 1.0.1`
Expand Down Expand Up @@ -78,7 +78,7 @@ In Clojurescript, it is best to use `:require-macros`:
(:require-macros [better-cond.core :refer [cond]]))
```

As of version 2.0.0, writing let, when-let, when, and do as keywords is optional. So you can also write it like this, if you prefer:
As of version 2.0.0, writing let, when-let, when, when-some, while and do as keywords is optional. So you can also write it like this, if you prefer:

```clojure
(cond
Expand Down
29 changes: 16 additions & 13 deletions src/better_cond/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,24 @@
:let [a (quot a 2)]
(odd? a) 2
3).
Also supports :when-let.
:let, :when-let and :do do not need to be written as keywords."
Also supports :let, :when, :when-let, :when-some, :do and :while, which do not need to be written as keywords."
[& clauses]
(when-let [[test expr & more-clauses] (seq clauses)]
(if (next clauses)
(if (or (= :do test) (= 'do test))
`(do ~expr (cond ~@more-clauses))
(if (or (= :let test) (= 'let test))
`(let ~expr (cond ~@more-clauses))
(if (or (= :when test) (= 'when test))
`(when ~expr (cond ~@more-clauses))
(if (or (= :when-let test) (= 'when-let test))
`(when-let ~expr (cond ~@more-clauses))
`(if ~test ~expr (cond ~@more-clauses))))))
test)))
(if (next clauses)
(if (#{:when-some 'when-some} test)
`(when-some ~expr (cond ~@more-clauses))
(if (#{:while 'while} test)
`(while ~expr (cond ~@more-clauses))
(if (#{:do 'do} test)
`(do ~expr (cond ~@more-clauses))
(if (#{:let 'let} test)
`(let ~expr (cond ~@more-clauses))
(if (#{:when 'when} test)
`(when ~expr (cond ~@more-clauses))
(if (#{:when-let 'when-let} test)
`(when-let ~expr (cond ~@more-clauses))
`(if ~test ~expr (cond ~@more-clauses))))))))
test)))

(defmacro defnc "defn with implicit cond" [& defn-args]
(cond
Expand Down
85 changes: 85 additions & 0 deletions test/better_cond/core_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,88 @@
(:require [clojure.test :refer :all]
[better-cond.core :refer :all]))

(deftest cond-test

(testing "Base cases"
(is (= (cond) nil)
(= (cond :any-value) :any-value)))

(testing "Can behave like the regular clojure.core/cond"
(testing "Falsey clauses are never invoked"
(let [proof (atom [])]
(is (= [:proven]
(do (cond false (swap! proof conj :never-invoked)
true (swap! proof conj :proven))
@proof)))))

(testing ":else clause works"
(is (= :proven (cond false nil :else :proven)))))

(testing "Supports :let, :when :when-let, :do and :while directives"
(testing "Basic :let and :when support"
(testing "Passing :when clause"
(is (= [4 4]
(cond :let [a 2]
:when (pos? a)
:let [b (* a 2)]
[b b]))))
(testing "Failing :when clause"
(is (nil?
(cond :let [a -1]
:when (pos? a)
:let [b (* a 2)]
[b b])))))

(testing ":when-let"
(testing "Passing :when-let clause"
(is (= [4 4]
(cond :let [a 2]
:when-let [b (some-> a (* 2))]
[b b]))))
(testing "Failing :when-let clause"
(is (nil?
(cond :let [a nil]
:when-let [b (some-> a (* 2))]
[b b])))))

(testing ":do"
(let [proof (atom nil)
result (cond :let [a 2]
:when (pos? a)
:do (reset! proof :proven)
:let [b (* a 2)]
[b b])]
(is (= result [4 4]))
(is (= :proven @proof))))

(testing ":while"
(testing "Top-level :while"
(let [counter (atom 0)
condition (atom true)
proof (atom [])]
(cond :while @condition
:let [n (inc @counter)]
:do (reset! counter n)
:do (reset! condition (< @counter 10))
(swap! proof conj n))
(is (= @proof [1 2 3 4 5 6 7 8 9 10]))))
(testing "Nested :while"
(let [proof (atom [])]
(cond :let [a (atom 1)]
:while (< @a 10)
:let [b (* @a @a)]
:do (swap! proof conj b)
(swap! a inc))
(is (= @proof [1 4 9 16 25 36 49 64 81])))))

(testing ":when-some"
(testing "Passing :when-some clause"
(is (= [false false]
(cond :let [a [false]]
:when-some [b (some-> a first)]
[b b]))))
(testing "Failing :when-some clause"
(is (nil?
(cond :let [a [nil]]
:when-some [b (some-> a first)]
[b b])))))))