This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-01-13
Channels
- # announcements (13)
- # beginners (52)
- # bitcoin (2)
- # calva (2)
- # cider (7)
- # clara (1)
- # clj-commons (11)
- # clj-kondo (6)
- # cljdoc (14)
- # clojure (68)
- # clojure-belgium (1)
- # clojure-denmark (6)
- # clojure-europe (57)
- # clojure-nl (2)
- # clojure-norway (10)
- # clojure-uk (3)
- # clojurescript (7)
- # code-reviews (17)
- # conjure (1)
- # cursive (5)
- # dev-tooling (11)
- # emacs (9)
- # fulcro (12)
- # hugsql (20)
- # introduce-yourself (6)
- # joyride (2)
- # leiningen (1)
- # lsp (61)
- # malli (30)
- # missionary (11)
- # nbb (6)
- # off-topic (26)
- # portal (5)
- # practicalli (5)
- # re-frame (8)
- # releases (8)
- # sci (21)
- # shadow-cljs (3)
- # sql (17)
- # squint (1)
- # xtdb (3)
Here is my dumb little function for paring down a given data shape:
(defn failures [{:keys [results]}]
(if-not (= 1 (count results))
(throw "Expectations failed: There was more than one result")
(let [suites (-> (first results) (get-in [:suites]))]
(if-not (= 1 (count suites))
(throw "Expectations failed: There was more than one suite")
(->> (-> suites first (get :tests))
(filter :fail))))))
By inspection, I’ve observed that it looks like the results
and suites
are always a sequence containing one map, rather than a map. I dunno why, it’s just what gets spit out. But what if I’m wrong? I’ll want to know, and update my assumptions. I feel like this if-not… throw
pattern is probably better handled by spec, right? When you’re trying to be quick and dirty and use an observation, but you know it might be wrong in the future and you want to do future self a favor, how would you handle this?If prefer when
for throwing exceptions. You could use function pre-conditions for these guys if you didn't want to go full spec
It's a personal thing, but I never use the keys
destructuring form. I think it looks incongruous. Even if I'm mapping keywords to vars in the same way, I'll just spell it out
Containers which I only expect to have one member happens often enough that I keep a special utility function.
(defn unique
"Returns the single member of `coll`, or nil if `coll` is empty. Calls `on-ambiguity` if there is more than one member (default is to throw an Exception).
Where
- `coll` is a collection
- `on-ambiguity` := (fn [coll] ...) -> `value`, default raises an error.
"
([coll on-ambiguity]
(if (seq coll)
(if (> (count coll) 1)
(on-ambiguity coll)
(first coll))))
([coll]
(unique coll (fn [coll]
(throw (ex-info "Unique called on non-unique collection"
{:type ::NonUnique
:coll coll
}))))))
Using that, you could re-write your code as something like:
(defn failures [m]
(let [suite (-> m
(:results)
(unique)
(:suites)
(unique)
]
(filter :fail (:tests suite)))
Having looked at this code for the first time in a while, I think the preferred order for those two versions of unique
should be in the opposite order with the shorter arg list first. Also, I should probably` (assert (coll seq))` and not make it a conditional.
I find this funny:
(defn failures
[{[{[{:keys [tests]}
:as suites] :suites}
:as results] :results}]
{:pre [(= 1 (count results))
(= 1 (count suites))]}
(filter :fail tests))
(defn failures {[{[{tests :tests} & suites] :suites} & results] :results}]
{:pre [(not suites) (not results)]}
(filter :fail tests))
yeah, just single line looks much better.
Still. That's quite a bit going-on in the arglist.
Also, the two &
should be :as
AFAICT.
oh yeah, didn't read the changed pre-conditions. Nice!