Fork me on GitHub
#clojure-europe
<
2022-10-06
>
teodorlu05:10:19

Hi 😎

reefersleep06:10:56

Good morning!

slipset07:10:39

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 previously

reefersleep07:10:39

I’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.

☝️ 1
reefersleep07:10:22

I think you did right in reducing multimethod usage 🙂

lemontea17:10:09

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)

lemontea17:10:37

incidentally in another group I heard ppl mention the “early return pattern” / “return first pattern”

lemontea17:10:05

I don’t know 😆 their original link is this: https://youtube.com/shorts/Zmx0Ou5TNJs?feature=share

lemontea17:10:45

(from my friends at a “general purpose” local IT community)

reefersleep17:10:33

Ah, I know about the general thing. I thought it was a Clojure specific discussion

slipset17:10:49

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!”))

reefersleep08:10:03

It’s little stuff like this that I miss when writing eg javascript.

potetm13:12:57

Only 2 months late to the party, but that's excellent to hear @U04V5VAUN!

potetm13:12:33

Actually, maybe now's a good time to ask whether now, 2 months later, you still think removing the multimethods was the right call?

slipset14:12:20

Still a very good call 🙂

slipset14:12:47

It’s actually going to be part of our coding-convetions 🙂

awyeah 1
slipset07:10:21

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

reefersleep07:10:19

I’ve been there! It feels awesome to move into one snappy transaction 🙌

slipset07:10:31

And, good morning from Fontainebleau

Ed12:10:25

I was there a few weeks ago. Are you bouldering?

slipset17:10:09

A bit. Mainly spotting/guiding the kids :)

Ed18:10:28

👍 We went with an overly ambitious tick list. Climbing in the forest can be pretty humbling ;)

slipset18:10:43

Did you manage anything from the list?

Ed18:10:32

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 ;)

Ed18:10:39

We did the 25 bosses too... That nearly killed me ;)

Ed18:10:15

Have fun. The best thing about font is the cheese and the wine ;)

robert-stuttaford07:10:52

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

robert-stuttaford07:10:32

i love mms, they're a really nice tool for writing extensible application code

borkdude07:10:59

Using multimethods even when your code is not open is fine

borkdude07:10:36

I love honeysql

slipset07:10:25

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”

borkdude09:10:05

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

😱 3
😍 2
lemontea17:10:49

funny reactions lol - it’s both scary and exciting:sweat_smile:. Humans are funny…

lemontea17:10:19

(cue some “SQL of despair” - extremely long SQL query)

otfrom09:10:31

a lot of times my multimethods are case statements they are map lookups

robert-stuttaford10:10:49

love me some map lookups 😆

genRaiy10:10:18

Good sunny morning

❤️ 3
lemontea17:10:46

hellooooo…… zzZZ……