This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-08-13
Channels
- # announcements (27)
- # beginners (184)
- # boot (4)
- # cider (9)
- # cljdoc (1)
- # cljsrn (2)
- # clojure (208)
- # clojure-austin (1)
- # clojure-conj (4)
- # clojure-dev (20)
- # clojure-europe (15)
- # clojure-italy (66)
- # clojure-losangeles (2)
- # clojure-nl (32)
- # clojure-spec (64)
- # clojure-uk (80)
- # clojurescript (50)
- # cursive (2)
- # data-science (3)
- # datomic (17)
- # emacs (1)
- # events (6)
- # fulcro (3)
- # jobs (15)
- # juxt (5)
- # klipse (2)
- # leiningen (31)
- # nyc (3)
- # off-topic (34)
- # re-frame (2)
- # reagent (9)
- # schema (1)
- # shadow-cljs (52)
- # specter (5)
- # sql (3)
Thanks @seancorfield for next jdbc! ๐
@dharrigan I'm very gratified to hear that!
And... morning!
Morning
morning
mรฅning
what's the thoughts on destructuring a map for keyword parameters in a function, or just passing the map as-is into the function and extracting out the keys inside it? Is there a "rule-of-thumb" guideline?
can you give an example function?
I typically try and destructure the map then use :as some-map
if i need to use it as well.
oh something trivial, like (defn foo [{:keys [bar baz]}] .....)
as opposed to (defn foo [m] (let [{:keys [bar baz]} m] ....))
The use of keyword arguments has fallen in and out of fashion in the Clojure community over the years. They are now mostly used when presenting interfaces that people are expected to type at the REPL or the outermost layers of an API. In general, inner layers of the code find it easier to pass options as an explicit map.
nested keyword arguments are the spawn of the devil
for me it boils down to how difficult the code is to read
it's nice to not-do-too-much on one line
but also not have important lines of code concealed by lines of cruft
dissenting opinion: i'm quite happy with destructuring in fn
args... i don't like :keys
destructuring though, i always use long-form map destructuring
yeah, :keys
is alright when things are simple, but it's horrible when you want to destructure from some nested structure, and it doesn't support changing the name of the binding, and worst of all, since you are going to have to use long-form some of the time to work around the previous 2 issues it forces you to have two different types of destructuring around which makes visual parsing harder
quite often i'll bind a long key to a short name with long-form destructuring, which means i have to use the long name once, rather than repeatedly throughout the fn
i think a lot of it comes down to what you are used to - my initial tendency was to use :keys
for compactness, but after realising that i was going to have to use long-form destructuring anyway i settled in favour of only using a single type of destructuring as preferable to compactness
but i found that once i'd been using long-form for a while, it visually parsed more easily than :keys
Yeah i remember using long-form, but not shortening the name, so it would always be really long in fn
also things like (fn [{a-id :id :as a} {b-id :id :as b}] ...)
I'm flip-flopping between the two at the moment, but I'm sorta settling on the top-level let
atm.
but I do like destructuring on the function, if it's only a couple of things I'm interested in.
nested is painful. is there an opening here tho for a macro, a defn/let variant that could destruct nested maps like get-in?
umm... i don't find destructuring from nested maps painful - if it was massively deep then sure, but one level deep is straightforward and (i find) easily visually parsed with long-form map destructuring
you can destruct nested too btw but only the more painful way without the :keys and similar keywords
@benedek aha! i don't think that's the more painful way - i prefer it
I like select-keys
for filtering out just the bits of a map I need, assuming I know what I need ๐
{{foo-id :id :as foo} :foo
{bar-id :id :as bar} :bar}
i find that very readable
it did take me a while to get used to it, but now i prefer it
Worth noting the destructured keys (in the fn args) will show up in the doc string too which can be very handy.
(defn foo [{:keys [x y]}]
(+ x y))
; local-api/doc | -------------------------
; local-api/doc | social.rewards.processor/foo
; local-api/doc | ([{:keys [x y]}])
I'll usually name it too like {:keys [foo bar] :as _opts}
so you know which arg it was, not just the keys inside it.
@olical or bad ๐ you have to think about what you're communicating when you do that. Are you taking a randomly constructed map, or is it a named concept like a ratio?
I just quite like seeing what keys the function mainly needs from a map at a glance / as I'm typing.
well I would use :as
to name the entire construct
swings and roundabouts really
like
(defn foo
[{:keys [x y] :as ratio}]
(+ x y))
Yup, I rarely destructure without an :as
. If you prefix (the name) with an _
it will silence linters moaning about unused references too.
You're somewhat communicating there that only x/y is needed, and therefore I can discard the rest of ratio (and here is where the example falls over)
er, is that a bad thing?
It's describing what it needs and if spec is anything to go by that's kinda the right way.
no, don't break other people's stuff unless you have a good reason
Let's use user as an example,
;; v1
(defn foo
[{:keys [username] :as user}]
username)
So I go along and code (foo {:username "foo"})
.
Then you update foo's implementation, but you still take a user:
;; v2
(defn foo
[{:keys [username age name] :as user}]
(if (> age 20)
name
username))
v2 is gonna blow up, but your contract is the same (takes a user)