This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-10-06
Channels
- # aleph (70)
- # announcements (9)
- # babashka (43)
- # babashka-sci-dev (6)
- # beginners (97)
- # cider (2)
- # clj-commons (3)
- # clj-kondo (41)
- # clojure (88)
- # clojure-europe (44)
- # clojure-nl (2)
- # clojure-spec (22)
- # clojurescript (65)
- # community-development (6)
- # conjure (10)
- # cursive (6)
- # datahike (13)
- # datomic (4)
- # eastwood (11)
- # events (1)
- # fulcro (45)
- # graalvm (1)
- # graphql (3)
- # hyperfiddle (3)
- # integrant (7)
- # jobs (1)
- # lambdaisland (1)
- # lsp (58)
- # nbb (4)
- # nrepl (3)
- # pathom (15)
- # shadow-cljs (27)
- # tools-deps (1)
Mornin'
Good morning!
morning!
The confessions of a multimethoder. In my Java days, I got infatuated with this if-less programming - rather than having a bunch of ifs littered across your code base, youâd instantiate the correct set of classes, assemble them, and youâd have a straight execution path. I carried this over to Clojure and (ab)used multimethods to achieve the same thing. Then I read a tweet by @potetm which prescribed only using protocols and multimethods for dispatches that needed to be open for extension for users who do not have access to our code base. And most of the stuff we do is not. Yesterday I finalized (I hope) removing a set of multimethods and replace them with some sql:
(hh/where (pq/and [:not [:= :status "paused"]]
(pq/or [:< :start now] [:= :runNow true])
(pq/or [:= :lastHeartbeatTs nil]
[:< :lastHeartbeatTs (t/ago job-execution-timeout)])
(pq/or [:= :type [:inline "one-off"]]
[:= :type [:inline "recurring"]]
(pq/and [:= :type [:inline "debouncing"]]
[:not [:exists (-> (hh/select 1)
(hh/from [job-history-table :h])
(hh/where (pq/and [:= :j.name :h.name]
[:< [:extract [:epoch-from [:- now :completed]]] :j.period])))]]))))
which albeit a bit complicated is still easier on the mind than looking through the three multimethods that implemented this previouslyIâve mostly balked at eager multimethoding that was not grounded in a need for extensibility. It makes the code much, much harder to navigate. You could achieve a similar dispatching effect with a dispatch function that refers to a map literal or a case
call, lexically gathering everything in one place. A multimethod implementation may have the same intent by the author - just to be multiple dispatch, but still gathered in one place (an ns, or a small group of nses whose files are neighbours) - but to the human reader, it leaves a huge door open for extensions that are not immediately visible.
I think you did right in reducing multimethod usage đ
ya, intention matters - even if 2 things are semantically equivalent, they could be considered different to a human.
And yes, the first thing that comes to my mind are case
(or cond
) too. (If youâre static typing, then itâs the rather cool âpattern matchingâ feature)
incidentally in another group I heard ppl mention the âearly return patternâ / âreturn first patternâ
What's that?
I donât know đ their original link is this: https://youtube.com/shorts/Zmx0Ou5TNJs?feature=share
Ah, I know about the general thing. I thought it was a Clojure specific discussion
Guard clauses are great. Donât remember the lib, but Someone(TM) has a early returning let library. Another thing around this is that in imperative langs, you use ifs only to choose an execution path, since ifs arenât expressions. In Clojure you can use conditionals not only choose the execution path, but also to conditionally return values, consider
(if foo
(println âItâs a foo!â)
(println âItâs not a foo!â))
vs
(println (if foo
âItâs a foo!â
âItâs not a foo!â))
Itâs little stuff like this that I miss when writing eg javascript.
Only 2 months late to the party, but that's excellent to hear @U04V5VAUN!
Actually, maybe now's a good time to ask whether now, 2 months later, you still think removing the multimethods was the right call?
And, btw, the previous implementation was to pull everything out of the db and do the filtering in Clojure land, included in that were at least three trips to the database. Now itâs all done in the database with only one query
Iâve been there! It feels awesome to move into one snappy transaction đ
đ We went with an overly ambitious tick list. Climbing in the forest can be pretty humbling ;)
Yeah. Tequila sunrise a 6c at franchard isatis and la John Gill at apremont that was a project left over from 5 years ago ... Pretty pleased with that one ;)
nice work! > only using protocols and multimethods for dispatches that needed to be open for extension for users who do not have access to our code base given that our codebase is closed and will never be used as a library by anyone, we've chosen not to care about this and use multimethods extensively
i love mms, they're a really nice tool for writing extensible application code
Donât worry, we still have our share of multimethods, some of them well designed, others, perhaps just a case-statement in disguise. The answer to âWhen and where should I use a multimethod?â seems to be, perhaps not surprisingly âIt dependsâ
Yeah. I've also used case
instead of multimethods in the name of performance but perhaps it didn't matter that much, while I now have a 500 line (didn't count, just an estimate) case expression in clj-kondo ;P
funny reactions lol - itâs both scary and exciting:sweat_smile:. Humans are funnyâŚ
love me some map lookups đ