diff --git a/README.md b/README.md index 124d668..9e48456 100644 --- a/README.md +++ b/README.md @@ -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` @@ -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 diff --git a/src/better_cond/core.clj b/src/better_cond/core.clj index 7725006..850bb9b 100644 --- a/src/better_cond/core.clj +++ b/src/better_cond/core.clj @@ -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 diff --git a/test/better_cond/core_test.clj b/test/better_cond/core_test.clj index 9783e3c..d890466 100644 --- a/test/better_cond/core_test.clj +++ b/test/better_cond/core_test.clj @@ -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])))))))