Fork me on GitHub
#clojure-uk
<
2019-08-13
>
dharrigan06:08:56

Using next jdbc with honeysql is sooo refreshingly simple ๐Ÿ™‚

dharrigan06:08:03

Thanks @seancorfield for next jdbc! ๐Ÿ™‚

seancorfield06:08:59

@dharrigan I'm very gratified to hear that!

seancorfield06:08:19

And... morning!

dharrigan06:08:21

yes, and morning all! ๐Ÿ™‚

dharrigan10:08:21

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?

guy10:08:10

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.

dharrigan10:08:51

oh something trivial, like (defn foo [{:keys [bar baz]}] .....) as opposed to (defn foo [m] (let [{:keys [bar baz]} m] ....))

dharrigan10:08:10

do people favour one approach over the other?

dharrigan10:08:42

Reading this

dharrigan10:08:51

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.

dharrigan10:08:06

do people here agree/disagree or as always "depends" ๐Ÿ™‚

guy10:08:50

I prefer the first one over the second for sure

Ben Hammond10:08:20

nested keyword arguments are the spawn of the devil

Ben Hammond10:08:24

for me it boils down to how difficult the code is to read

Ben Hammond10:08:36

it's nice to not-do-too-much on one line

Ben Hammond10:08:06

but also not have important lines of code concealed by lines of cruft

dominicm11:08:08

Nested is horrible

dominicm11:08:29

As an aside, generally don't do it in functions either, do a top level let

maleghast11:08:53

I'm with @dominicm - top-level let makes for very readable code...

mccraigmccraig11:08:28

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

guy11:08:35

wow really?

guy11:08:50

I find long-form to be rubbish when the names are long haha

mccraigmccraig12:08:16

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

guy12:08:42

yeah i agree ๐Ÿ™‡

mccraigmccraig12:08:44

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

guy12:08:59

ooh thats a good take on it.

mccraigmccraig12:08:25

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

mccraigmccraig12:08:57

but i found that once i'd been using long-form for a while, it visually parsed more easily than :keys

guy12:08:13

Yeah i remember using long-form, but not shortening the name, so it would always be really long in fn

mccraigmccraig12:08:20

also things like (fn [{a-id :id :as a} {b-id :id :as b}] ...)

guy12:08:51

hmm iโ€™ve never really done that before

dharrigan11:08:27

I'm flip-flopping between the two at the moment, but I'm sorta settling on the top-level let atm.

dharrigan11:08:54

but I do like destructuring on the function, if it's only a couple of things I'm interested in.

dharrigan11:08:00

<shrug> still making up my mind.

dominicm12:08:25

There's a recommendation in the wiki somewhere on it

dharrigan12:08:14

there's a wiki? ๐Ÿ™‚

benedek12:08:33

nested is painful. is there an opening here tho for a macro, a defn/let variant that could destruct nested maps like get-in?

benedek12:08:43

would be pretty awesome. am i missing something?

benedek12:08:02

could be under :nested-keys vector of vectors

mccraigmccraig12:08:25

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

benedek12:08:03

you can destruct nested too btw but only the more painful way without the :keys and similar keywords

mccraigmccraig12:08:07

@benedek aha! i don't think that's the more painful way - i prefer it

benedek12:08:17

yeah gets really messy when deeper

benedek12:08:41

:) fair enough. question of style as well i suppose

practicalli-john12:08:38

I like select-keys for filtering out just the bits of a map I need, assuming I know what I need ๐Ÿ™‚

mccraigmccraig12:08:59

{{foo-id :id :as foo} :foo 
 {bar-id :id :as bar} :bar}

benedek12:08:02

what to bound to is maybe tricky

mccraigmccraig12:08:15

i find that very readable

mccraigmccraig12:08:51

it did take me a while to get used to it, but now i prefer it

Olical14:08:05

Worth noting the destructured keys (in the fn args) will show up in the doc string too which can be very handy.

Olical14:08:44

(defn foo [{:keys [x y]}]
  (+ x y))
; local-api/doc | -------------------------
; local-api/doc | social.rewards.processor/foo
; local-api/doc | ([{:keys [x y]}])

Olical14:08:54

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.

dharrigan14:08:24

TIL @jr0cket select-keys awesome!

dominicm16:08:39

@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?

dominicm16:08:03

hence the advice to do

(defn foo [ratio]
  (let [{:keys [x y]} ratio]
    (+ x y)))

Olical16:08:51

I just quite like seeing what keys the function mainly needs from a map at a glance / as I'm typing.

dominicm16:08:42

goes into the abstract of concepts and naming things.

Ben Hammond16:08:52

well I would use :as to name the entire construct

Ben Hammond16:08:58

swings and roundabouts really

Ben Hammond16:08:23

like

(defn foo 
  [{:keys [x y] :as ratio}] 
  (+ x y))

Olical16:08:42

Yup, I rarely destructure without an :as. If you prefix (the name) with an _ it will silence linters moaning about unused references too.

dominicm16:08:47

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)

Ben Hammond16:08:08

er, is that a bad thing?

Olical16:08:30

It's describing what it needs and if spec is anything to go by that's kinda the right way.

Olical16:08:44

But you wouldn't remove other keys unless you had to anyway, imo

Olical16:08:00

"it depends" x ๐Ÿ’ฏ

Ben Hammond16:08:01

no, don't break other people's stuff unless you have a good reason

dominicm16:08:26

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)