Good Morning!
Good morning! That flow-storm-debugger looks super nice!
Guten morgen!
morning
“Wilt u uw eigen enquêtes maken?” :rolling_on_the_floor_laughing:
(on “http://de.surveymonkey.com”, I guess the browser got cofuzzled by too much borkdude)
haha
God morgen
Good dawning
Guten Morgen!
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.Expert answer is as always "it depends", but I'd be happy if you would share what you think it depends on.
@slipset we have a couple of those functions around in our app. this is especially handy if you end up renaming keys later on
but you can also just refactor from keywords to functions later of course
exactly 🙂
another one: :user/first-name
or :first-name
or :first_name
(no translating keys when dealing with database)
interesting read: https://vvvvalvalval.github.io/posts/clojure-key-namespacing-convention-considered-harmful.html
I'm somewhere between denial, acceptance, and wishing the world was a better place.
Morning
@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? • …
Also known as “the cost of abstraction” (see https://250bpm.com/blog:86/)
Thank you!
@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.
I am with @simongray here. Transparency is nice.
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
if I do have csk things, I generally go with the most restrictive system and then leave it rather than converting everywhere.
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)
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
but the point of most of my systems is converting from a pile of files to something I can calculate
map-csv-reduce ;)
all day long, every day
tho most of it is excel nowadays as that way I don't have people messing up the csv export
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
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).that I've not seen before
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
@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 ...))
so I can later on change things more easily without refactoring a whole bunch of things
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.
> 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.
yes, it's just an extra level of indirection, the solution to almost anything in CS
except too many layers of indirection
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 😄
If you really need to go through curl:
$ bb -e '(-> (curl/post "<https://postman-echo.com/post>" {:body "dude" :headers {"Content-Type" "text/plain"}}) :body (json/parse-string true) :data)'
"dude"
@orestis I/we have fns for that in our dev/user.clj which handles auth and stuff. Quite useful.
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.
C-c C-t is undefined in my emacs
I took it from memory :( my fingers know the key binding, my brain does not
Basically it’s rerun failing tests
👋:skin-tone-3: @djblue ... bonjour Chris
Hello @raymcdermott!
oh and did I tell you I hate threads 🙂
No?
Do you? 😏
🤐
Threads are rather terrible. All the extra clicking.
True, but they scope conversations 🤷
Like a namespace
And don't we all love namespaces?
ok, let's just say that this is a flat namespace
channels scope conversations too, like a namespace
anyway welcome ... 🙂
you know I 😍 you
Likewise
I’m not a fan of threads either. All that extra jumping around.
IRC FTW!
You know @borkdude I still haven’t installed babashka on my machine 🙈
I like threads as a concept, just not how slack does it.
I think they use to be way worse before you could resize the window split
They still don't update on the threads page though
I want tabs.
I want true multi-threading
I'd be happy with communicating sequential people