Fork me on GitHub
#clojure-europe
<
2020-11-09
>
dharrigan06:11:35

Good Morning!

pez06:11:33

Good morning! That flow-storm-debugger looks super nice!

ordnungswidrig08:11:02

“Wilt u uw eigen enquêtes maken?” :rolling_on_the_floor_laughing:

ordnungswidrig08:11:36

(on “http://de.surveymonkey.com”, I guess the browser got cofuzzled by too much borkdude)

slipset08:11:42

God morgen

genRaiy09:11:14

Good dawning

javahippie09:11:44

Guten Morgen!

slipset10:11:27

So, this mornings topic from work: Given a map (or record) like so:

(def user {:first-name "Erik" :last-name "Assum"})
Should we be happy with
(:first-name user)
or should we go with
(defn first-name [user] (:first-name user))
I feel really torn here. I really like the conciseness of the first alternative, but it leaves little or no wiggle-room if you later want to change how we store the first-name of the user. The second alternative gives all kinds of wiggle-room, but it also forces us to create accessors/mutators (getters and setters) for stuff that is easily handled by the clojure std library.

slipset10:11:41

Expert answer is as always "it depends", but I'd be happy if you would share what you think it depends on.

borkdude10:11:52

@slipset we have a couple of those functions around in our app. this is especially handy if you end up renaming keys later on

borkdude10:11:17

but you can also just refactor from keywords to functions later of course

slipset10:11:27

exactly 🙂

borkdude10:11:15

another one: :user/first-name or :first-name or :first_name (no translating keys when dealing with database)

slipset10:11:27

I'm somewhere between denial, acceptance, and wishing the world was a better place.

❤️ 6
ordnungswidrig11:11:03

@slipset it’s a more genera question of how strongly you want to couple two things. I this case data representation and the code location(s) using it. Same goes for protocol if you only have a singe implementation (yet) or using sql vs build a data abstraction layer. Question you might want to ask when it comes to coupling: • How often will it change? • How likely is a change? • What is the cost of the change? • What is the code of the abstraction the allow to decouble from the change? • Will the abstract catch all the change? • What is the impact on testing? • …

ordnungswidrig11:11:21

Also known as “the cost of abstraction” (see https://250bpm.com/blog:86/)

slipset11:11:17

Thank you!

simongray12:11:26

@slipset My take is this: as long as (first-name user) and (:first-name user) evaluate to the same thing, defn'ing first-name seems like a premature (and pointless) abstraction. If :first-name at some point changes to a different key you can still easily search-replace all instances of :first-name (…or if you use namespaced keywords in intellij simply automatically refactor the keyword). And if the way you access the first name in user changes drastically, you can still defn a user-name function and then just replace all occurrences of (:first-name user) with (first-name user). The only thing defn'ing first-name at the onset really gives you is fewer search-replaced lines down the line if you do decide to change it. At the same time, it actually hides valuable information from the reader (the fact that user is associative and has a :first-name key). Faced with this trade-off, I much prefer using the keyword directly, preferably a namespaced one.

pez13:11:00

I am with @simongray here. Transparency is nice.

otfrom13:11:20

I generally use namespaced keywords directly, but then I'm usually not going in to/out of a database or other system to force camel/snake/kebab like problems

otfrom13:11:58

if I do have csk things, I generally go with the most restrictive system and then leave it rather than converting everywhere.

borkdude13:11:49

At first :foo_bar seemed heresy to me, but after trying it out and seeing how much headaches this saves me, not making any mistakes translating maps back and forth, I think it's worth it, when dealing with the most restrictive systems (like postgres)

otfrom13:11:55

tho in my data scrubbing code lately I have taken to using namespaced keywords for each bit of the stage. I have nice kebab'ed ones for my domain that have a good domain keyspace, and the ones that read in from a client specific file have a client specific namespace for their keywords. This way I can trace how I've done the transformations from row in excel/csv -> map of those values -> proper domain map the rest of my data science model code wants

otfrom13:11:51

but the point of most of my systems is converting from a pile of files to something I can calculate

borkdude13:11:14

map-csv-reduce ;)

otfrom13:11:54

all day long, every day

otfrom13:11:12

tho most of it is excel nowadays as that way I don't have people messing up the csv export

otfrom13:11:08

I had a read of: https://clojurians.slack.com/archives/CBJ5CGE0G/p1604916751247500 and I like it for domain things that will be long lived. I'm trying to decide if it makes a difference in my transformation code

pithyless13:11:18

RE: keywords; there's actually a third option I've seen in the wild:

(def first-name :first-name)
See e.g. https://github.com/fulcrologic/fulcro-rad/blob/develop/src/main/com/fulcrologic/rad/report_options.cljc You use the var as you would use a keyword, but you're free to add docstrings and the keyword namespace does not have to match the actual namespace (while still allowing you to use an imported ns alias).

otfrom13:11:54

that I've not seen before

otfrom13:11:25

I'm generally not a fan of creating getters and setters. That was an OOP habit I was hoping to have left behind by getting to values

borkdude13:11:37

@pithyless Good point. I do that with re-frame as well.

(def tag-path [:foo/bar :baz/tags])

(reg-event-fx ... (update-in db tag-path ...))

❤️ 3
borkdude13:11:54

so I can later on change things more easily without refactoring a whole bunch of things

pithyless13:11:44

Yeah, it's interesting to use e.g. when you want to use 3rd party keywords (both for having a single source of truth and also having a good place to write some documentation for yourself). The only thing that bugs me about it is you lose the keyword destructuring sugar syntax.

pithyless13:11:28

> I'm generally not a fan of creating getters and setters. That was an OOP habit I was hoping to have left behind by getting to values I don't consider (def foo :foo) an OOP getter/setter pattern. Instead, I think of it as an additional hop that decouples your local app name from the data layer name. If it's true that "your data lives longer than your application", then your ::domain/first-name may change less frequently than your favorite ::app.ns/first-name ; I've been going back-and-forth whether this distinction is worth the trouble of foregoing certain syntax conveniences and making this distinction more explicit in my own code.

borkdude13:11:20

yes, it's just an extra level of indirection, the solution to almost anything in CS

3
otfrom14:11:16

except too many layers of indirection

💣 3
orestis17:11:16

I needed to make a POST request to an endpoint I’m developing to validate “external” behaviour and I started (yet again) googling for curl arguments to make a JSON post payload etc. Then I realized I had my REPL open and I could just do a clj-http request 😄

borkdude17:11:26

If you really need to go through curl:

$ bb -e '(-> (curl/post "" {:body "dude" :headers {"Content-Type" "text/plain"}}) :body (json/parse-string true) :data)'
"dude"

❤️ 3
slipset17:11:01

@orestis I/we have fns for that in our dev/user.clj which handles auth and stuff. Quite useful.

slipset17:11:26

Another thing that’s quite useful is to have a great story on integration-tests so it’s really simple to write a simple test for your new endpoint. C-c C-t r is your friend in that case.

borkdude17:11:56

C-c C-t is undefined in my emacs

slipset17:11:11

I took it from memory :( my fingers know the key binding, my brain does not

slipset17:11:28

Basically it’s rerun failing tests

genRaiy18:11:24

👋:skin-tone-3: @djblue ... bonjour Chris

genRaiy18:11:46

oh and did I tell you I hate threads 🙂

djblue18:11:02

Do you? 😏

dominicm18:11:19

Threads are rather terrible. All the extra clicking.

djblue18:11:37

True, but they scope conversations 🤷

djblue18:11:45

Like a namespace

djblue18:11:58

And don't we all love namespaces?

genRaiy18:11:07

ok, let's just say that this is a flat namespace

genRaiy18:11:26

channels scope conversations too, like a namespace

👍 3
genRaiy18:11:42

anyway welcome ... 🙂

genRaiy18:11:56

you know I 😍 you

dharrigan18:11:21

I’m not a fan of threads either. All that extra jumping around.

orestis18:11:57

You know @borkdude I still haven’t installed babashka on my machine 🙈

dominicm19:11:56

I like threads as a concept, just not how slack does it.

djblue19:11:53

I think they use to be way worse before you could resize the window split

dominicm20:11:08

They still don't update on the threads page though

borkdude20:11:37

I want true multi-threading

😆 3
😂 3
dominicm20:11:46

I'd be happy with communicating sequential people