Fork me on GitHub
#clojure-europe
<
2022-04-26
>
pez05:04:48

Morning!

mccraigmccraig07:04:40

måning down t'common

💙 2
🔔 1
genRaiy08:04:42

Good morning

maleghast08:04:11

Madainn mhath

dharrigan14:04:54

What's the current thinking on keywords arguments or passing the entire map to a function, i.e., (foo :a 1 :b 2 :c 3) vs (foo {:a 1 :b 2 :c 3})? On the clojure destructuring webpage, it says that keyword arguments fall in and out of fashion over the the years. Do you have a preference?

borkdude14:04:26

My preference is to always use a map

👍 5
2
1
borkdude14:04:04

To me it doesn't make much sense to add a lot of fuzz to save two characters

dharrigan14:04:32

Given my limited time with Clojure, my perference is also to use a map, since as I was (still am) learning clojure, I didn't come across the [& ....] variadic form at that time. So I learnt just to pass maps back and forth. Feels more natural to me 🙂

1
jkxyz15:04:26

I'm curious if the 1.11 changes will make it more common to see the variable arity version, since it makes it easier to use and saves you from having to add a separate arity to call without any options. I tend to prefer the variable arity [x y & {:as opts}] or even better [x y & {:keys [foo bar]} so you can see the options in the arglists. But it only really makes sense if the options are truly optional - so for small utility functions mostly. If you expect a certain shaped map then better to just pass a map IMO

borkdude15:04:35

How I see it is the other way around: the change in 1.11 allows you to pass maps to named arg functions now. So you can use maps everywhere

💡 4
otfrom15:04:06

@U04V15CAJ thx. I hadn't properly understood that

dominicm15:04:52

I'd now mostly do & :keys, just because clojure 1.11. Especially for opts.

Ben Sless17:04:07

I want the compiler to figure it out and save on making and taking apart the map to begin with if I go for keyword args. So I'll just use a map

1
slipset06:04:59

Here’s perhaps a more nuanced answer. If I were to develop some fns to be used from the repl which took a couple of args, then perhaps it’d be nice to be able to call it like:

(foo :some-arg 'bar :some-other-arg 'baz)
That would relieve me of having to remember the argument order. If, however the fn took some kind of options-map, I’d most likely be more happy passing the options-map as, well, a map. Pomegranate has some examples where this all went terribly wrong, eg the lack of documentation from the args vector in add-dependencies https://github.com/clj-commons/pomegranate/blob/master/src/main/clojure/cemerick/pomegranate.clj#L57

slipset06:04:34

But of course, that fn wouldn’t have been much better if it were defined as

(defn add-dependencies [args] ...)

slipset06:04:55

For this reason, we tend to destructure the args to show which pieces of the map are interesting for the fn:

(defn add-dependencies [{:keys [classloader coordinates repositories] :as args}] ...)

👍 3
slipset06:04:03

The perhaps sad bit about this is that IIRC, destructuring is quite a bit slower than doing (get args :classloader) or some equivalent.

reefersleep06:04:21

Always maps for me, too. I was trying to conjure up an argument about maps being passed around in general Clojure code, bu t I don’t really know if it holds or if it’s just the convention I’m used to.

dominicm06:04:24

Only tangentially related, but there's a subtle point there. Destructing in your function arguments is documentation. If you want args to be generated opaquely and not allow arbitrary map creation then destructuring in a let inside the function allows you to document that the value is opaque.

reefersleep06:04:57

I’m with you both on destructuring as documentation btw 👍👌

💯 2
reefersleep06:04:35

I’ve occasionally used :as x to give x a (documenting) name in the destructuring function, even though I haven’t used x in the function body, only its keys.

dominicm06:04:35

When questions like this come up, I consider scraping the "core" codebases (eg clojure itself, tools deps, etc) to get answers.

slipset06:04:41

We tend to use :as x for documentation purposes, even if x is not used in the fn-body. But at other times, it’s nice to be able to pass x on to some other fn which might do other stuff with it.

slipset07:04:59

@U09LZR36F I would be careful with looking at core for inspiration. I believe @U072WS7PE has made some tweets around the fact that core is written with different tradeoffs in mind than what you’d probably choose for your app.

dominicm07:04:38

He has, I was thinking in terms of API rather than implementation. :arglists, comments, docs, etc. For example, you could override :arglist manually to include destructuring as documentation, but use get in your function for performance. But the key question to answer here is what the user sees when looking up documentation.

slipset07:04:59

But then you’d have to keep the :arglists up to date as the fn evolves, since I guess the :arglist has no functional implications (I haven’t used that at all)

slipset07:04:31

And if there’s one thing I hate more than code that’s hard to read, it’s documentation that’s out of date.

dominicm07:04:46

Core does it in a few places where the API is not possible to express, but people know what you mean.

dominicm07:04:00

defn might do it, even.

dominicm07:04:53

fdecl is manually parsed, because the arglists don't actually work the way they're documented.

borkdude07:04:40

Indeed, arglists sometimes uses an informal regex notation, which makes it actually unsuited to do anything serious with it