Fork me on GitHub
#beginners
<
2018-09-20
>
dfcarpenter01:09:56

I'm struggling to learn how to model my problem domain in clojure and reading as much as I can. Anyways I was looking at this advice https://gist.github.com/reborg/dc8b0c96c397a56668905e2767fd697f#should-i-use-a-namespace-to-bundle-functions-to-model-a-domain-object-like-a-customer-or-product and trying to understand if the advice is dissuading me from creating a map per entity or just a namespace per entity with its associated "getters and setters". If I am reading it correctly i'm assuming I can still map my entities to hashmaps but do so in a way where I can reuse functions. Try to use as few data structures as possible and have lots of functions, basically don't segregate my entities strictly. Any examples on github of a clojure codebase with complex domain modeling?

seancorfield02:09:13

@dfcarpenter Having "getters and setters" as functions is not idiomatic Clojure. Just use keywords for the items in the hash map.

👍 4
mihir09:09:06

I am trying to write a function to add 2 positive integers without using the + operator.

mihir09:09:10

(defn inc-1 [x]
  (loop [y 1
         x x]
    (if (not (= 0 y))
      (let [c (bit-and x y)
            x (bit-xor x y)
            y (bit-shift-left c 1)]
        (recur y x))
      x)))

(defn add [x y]
  (let [x1 (atom 0)
        y1 (atom 0)
        return? (atom false)
        result (atom 0)]
    (while (not @return?)
      (cond (not (= x @x1)) (do (reset! result (inc-1 @result)) (reset! x1 (inc-1 @x1)))
            (not (= y @y1)) (do (reset! result (inc-1 @result)) (reset! y1 (inc-1 @y1)))
            (and (= y @y1) (= x @x1)) (reset! return? true)))
    @result))

mihir09:09:30

Is there a better way to do it?

mg09:09:40

can just do peano:

(defn plus
  [x y]
  (if (pos? y)
    (recur (inc x) (dec y))
    x))

mg09:09:46

if inc and dec are allowed anyway

Denis G09:09:48

@mihir why are you using atoms? are you doing it in parallel?

Denis G09:09:01

@michael.gaare no. inc/decs are not allowed. you can write your own inc, dec using bit operations, then it's legit but @michael.gaare shows very good abstraction. This code is easy to understand

orestis11:09:18

What’s the most idiomatic way to go from a seq of maps that share a common key to a map from a value of that key to the original map?

orestis11:09:46

I have

(def orig [{:k 1 :v "a"} {:k 2 :v "b"} {:k 3 :v "c"}])
user=> (into {} (for [m orig] [(:k m) m]))
{1 {:k 1, :v "a"}, 2 {:k 2, :v "b"}, 3 {:k 3, :v "c"}}

manutter5111:09:24

You could also do

(def ms [{:foo 1} {:foo 2} {:foo 3}])
=> #'user/ms
(into {} (map (juxt :foo identity) ms))
=> {1 {:foo 1}, 2 {:foo 2}, 3 {:foo 3}}

orestis11:09:17

Just juxt 🙂

😉 8
mihir11:09:56

@denisgrebennicov @mg How about this?

(defn add [x y]  (- x (- y)))

Denis G12:09:20

I assume - is also not allowed. Otherwise it doesn't make sense 😄

mihir12:09:31

No only + is not

Denis G12:09:39

Is it an interview question?

mihir12:09:45

Nope. Self imposed.

orestis12:09:16

Integer.sum 😄

Denis G12:09:17

a + b = a - (- b)

Denis G12:09:17

But I think the point was of doing it using bitwise operators, otherwise, dunno ... I've heard about this kind of interview questions

mihir12:09:26

(defn add [x y]
      (loop [x x
             y y]
            (if-not (zero? y)
                    (recur (bit-xor x y) (bit-shift-left (bit-and x y) 1))
                    x)))

Denis G12:09:20

(add 3 1) gives you 4?

mihir12:09:24

Maybe, not that great at math.

Denis G12:09:03

😄 looks good but I guess it doesn't work on negative numbers, does it? works O_o

Denis G12:09:34

:thumbsup:

enforser13:09:01

@orestis maybe group-by would work for you - it returns a slightly different structure (wraps the value in a vector, in case there are multiple similar keys)

(group-by :k [{:k 1 :v "a"} {:k 2 :v "b"} {:k 3 :v "c"}])
=> {1 [{:k 1, :v "a"}], 2 [{:k 2, :v "b"}], 3 [{:k 3, :v "c"}]}

nifflor13:09:37

is there a way to use spec to enforce an order of keys in a map? {:a 1 :b 2 :c 3} passes but {:a 1 :c 3 :b 2} fails or is automatically re-ordered correctly? I was hoping i could use such a spec to reorder incoming json with potentially scrambled key order into csv with always the same ordering.

enforser13:09:49

common maps don't generally have a sort order - when you assoc a new value in a map there is no guarantee that it goes to the "end"

enforser13:09:24

what you can do to coerce it to sort by key is (into (sorted-map) {k1 v1 ... kn nv})

enforser13:09:00

user=> (into (sorted-map) {:a 1 :b 3 :c 2})
{:a 1 :b 3 :c 2}
user=> (into (sorted-map) {:a 1 :c 2 :b 3})
{:a 1 :b 3 :c 2}

nifflor13:09:31

thx, i kind of did that in a more opaque way by doing this:

(let [keys-sorted (sort (keys req))
        values-sorted ((apply juxt keys-sorted) req)]
(`req` being a request map) and then writing the sorted values. But then i realized i want an order that differs from alphabetical. And that that very order is already in my spec. So i wouldn't like to have to re-write it down here again to enforce it but was hoping i could get spec to do that for me

enforser13:09:34

maybe sorted-map-by would be useful? I've never used spec myself, so not sure if I'll be much help determining what it can do for you.

enforser13:09:01

user=> (into (sorted-map-by >)  {1 :a  2 :b  3 :c} )
{3 :c, 2 :b, 1 :a}

Alex Miller (Clojure team)13:09:33

well I’d first say you should think really hard about why you want to do this in the first place. generally I’d say you should try to avoid relying on sorted data structures if at all possible - it’s way too easy to serialize to edn and lose it on a print/read roundtrip

Alex Miller (Clojure team)13:09:46

but you can do it with spec:

Alex Miller (Clojure team)13:09:19

(s/every (s/tuple int? keyword?) :into (sorted-map))

Alex Miller (Clojure team)13:09:58

user=> (s/def ::c (s/every (s/tuple int? keyword?) :into (sorted-map)))
:user/c
user=> (s/conform ::c {1 :a  2 :b  3 :c})
{1 :a, 2 :b, 3 :c}

Alex Miller (Clojure team)13:09:33

actually, trying with some other examples, that’s not working. :into is really designed for default colls and I think is colliding here

Alex Miller (Clojure team)13:09:23

so would probably need a post step to dump into a sorted map

Alex Miller (Clojure team)13:09:37

but again, I’d think real hard about whether you really need it in the first place

nifflor13:09:26

thank you. i need to write to a log to COPY to redshift from s3. I was convinced that this requires ordered csv files, but it seems it doesn't. One can do json as well. In light of this, i will probably just stick with the json until COPY supports edn format 😉

Alex Miller (Clojure team)13:09:43

I don’t see any reason you need ordered maps to make csv export work - you only need to enforce the order when write a line

Alex Miller (Clojure team)13:09:30

juxt is useful to make a function that plucks values from a map in pred order

Alex Miller (Clojure team)13:09:45

user=> (def data [{:a 1 :b "x"} {:a 5 :b "y"}])
#'user/data
(user=> (map (juxt :a :b) data)
([1 "x"] [5 "y"])

Alex Miller (Clojure team)13:09:07

that assumes keyword keys, and you drop the column ordering into juxt - that’s a perfectly good input for writing csvs with a lib like data.csv

nifflor14:09:00

yeah, that was my starting point. Maybe my question wasn't clear enough. I am somewhat reluctant to have a spec file which specifies something like:

(s/def my-spec (s/keys [:b :c :a]))
and then have my csv-writer which specifies that order again
(map (juxt :b :c :a) data)
It seems redundant and error prone if the spec ever changes. Also, i have quite a few events with different keys and i would have to enlist each of them twice as above, once in the spec and once in the csv-writer, which further escalates the problem. I was just hoping that the spec (which i have written anyways) can take care of the second job (even with something like (map (juxt (keys-from-spec s/my-spec)))

nifflor14:09:31

so my hope was that someone could point me to a keys-from-spec function really...

nifflor14:09:38

but the principled answer 'avoid relying on order' is probably better anyways. Thanks again @U064X3EF3 @UCQL6E7PY

Alex Miller (Clojure team)14:09:04

it is possible to remove the duplication if you’re willing to use eval while defining the spec

Alex Miller (Clojure team)14:09:51

but this is actually an area I’m expecting will have better answers in spec in coming months

nifflor14:09:34

ok, looking forward to it then

stvnmllr215:09:47

Anyone have a good up-to-date reference for getting my editor connected to a figwheel clojurescript repl, so I can eval code like I do in clojure? (Vim user at the moment, but will try others if it's easier to set up)

g15:09:19

personally, i’ve found emacs to be easier on average for repl support.. fireplace.vim exists but i didn’t find it as useful

bhauman15:09:56

vim fireplace has limited functionality compared to cider or cursive

bhauman15:09:22

unfortunately things are a little in flux right now

bhauman15:09:45

VIM folks seem to enjoy spacemacs

borkdude15:09:09

inf-clojure in emacs may be an option if all else fails

stvnmllr215:09:50

Thanks Bruce, I just followed your Using-the-Figwheel-REPL-within-NRepl guide to get a cljs repl. Thought that might work. I'll try it from another editor, maybe that's the problem.

borkdude15:09:58

I noticed CIDER itself is also in flux, so I can’t really say what to use

bhauman15:09:52

I’d heartily recommend CIDER and emacs the docs are pretty good right now

bhauman15:09:30

it has instructions for figwheel and figwheel-main

bhauman15:09:42

and or try spacemacs

wizard 4
bhauman15:09:59

but keep in mind you will probably need to ask for help if its your first time

stvnmllr215:09:27

Thanks. Yeah, i've installed spacemacs a few times, but never had time to actually use it. Been happy with figwheel alone, but thought it'd be cool to demo eval in editor for a React talk

bhauman15:09:27

yeah editor eval is the jam, we are sooooo close to having it be easy to set up

bhauman15:09:08

CIDER is %97 percent of the way there

stvnmllr215:09:13

Nice, thanks for all the hard work! I'll mention it's coming 🙂

bhauman15:09:28

it works now quite well

bhauman15:09:43

its just that you need some experience if something goes wrong

bhauman15:09:02

and it also works handily in vsCode

g15:09:03

yeah i’ve used vim exclusively for years, but evil-mode made switching for the REPL integration a lot more palpable

stvnmllr215:09:18

ok, well.. the easy set up. ok. I'll give it a go

bhauman15:09:40

its reccomend straight emacs with evil mode

bhauman15:09:01

over spacemacs for starters

👍 8
stvnmllr215:09:04

Hmm.. the VSCode might go over much better with React folks

stvnmllr216:09:19

thanks, i think i tried it once. So it supports cljs eval?

sundarj16:09:33

not sure - i've not used it myself

dpsutton15:09:08

also learning a bit about debugging and navigating in emacs will pay dividends. its quite easy to just step through CIDER and then you are very close to being able to submit a patch 🙂

orestis15:09:48

@bhauman a demo with figwheel and vscode doing in-editor eval would be fantastic...

bhauman15:09:11

@orestis yeah plus a guide on how to set it up

bhauman15:09:39

but … the support isn’t anywhere near as good as CIDER or Cursive

bhauman15:09:53

and a tad buggy

orestis15:09:54

Yeah, I know. But in-editor eval is something CLJS lags behind compared to Clojure, right? Therefore we rely on watching the file system.

bhauman16:09:09

protorepl is more mature, but that falls pretty far short of CIDER and Cursive as well

bhauman16:09:45

@orestis have you used it?

bhauman16:09:57

its not far behind at all anymore

orestis16:09:59

I’m using emacs myself, but I’m willing to give vs code a try for the sake of teammates :)

bhauman16:09:13

I meant in editor eval, have you used that in CIDER or emacs for CLJS?

orestis16:09:33

Right, this is what I think a demo is useful... I base my comments on my experience months ago.

bhauman16:09:26

VSCode’s support does lag way behind if that is what you mean

g16:09:36

anyone ever used aleph or manifold? having some trouble with streams

schmee16:09:02

yes, fire away

g16:09:46

so.. i guess i’m showing my inexperience with JVM, but websockets in aleph are built with manifold streams which seem to accept strings or java.nio.ByteBuffers

g16:09:00

not quite sure how to get from something like a clj map to a bytebuffer.

g16:09:35

or if that’s even the right question to be asking … ultimately all i’d like to do is pass a clojure map through an aleph websocket.

schmee16:09:11

what you need is a called a serializer

schmee16:09:22

same thing as map -> json but this is map -> some binary format

schmee16:09:45

that uses gloss, which is pretty nice: https://github.com/ztellman/gloss

g16:09:12

ah, excellent! looks like the piece i’m missing. i’ll dig into this and see how much further i can get. thanks so much for your help!

👍 4
orestis16:09:10

@bhauman No, I mean CLJS in editor eval.

orestis16:09:52

Btw I am grateful for figwheel main. It’s awesome!

bhauman16:09:17

@orestis so I was wondering if you use CLJS editor eval in emacs regularly?

bhauman16:09:39

or do you rely on the console more?

orestis16:09:28

Nope, because I tried to set it up months ago, failed, then the general advice was “just use figwheel and save your files”. Has that changed now? Can I re-eval a reagent component and see live updates? That would make for a killer demo.

bhauman16:09:03

you can eval

bhauman16:09:26

but that’s not going to cause your app to re-render

bhauman16:09:59

if you eval a (re-render) fn that will

bhauman16:09:45

In answer to your question, CLJS eval works very well in cider and has for quite some time

bhauman16:09:04

I can not live without it

orestis16:09:37

Then is it “just” a matter of setting up some post-eval hooks somewhere to call a function after every eval? Or just bind the re-render to some keystroke and do it manually?

orestis16:09:23

Right, I have to check the cider docs on how to get a CLJS repl again. I think this has changed now, so it kinda “just works”?

bhauman16:09:26

I hear what your are asking, I just think they are different things

orestis16:09:53

Yep, agreed.

bhauman16:09:13

and there is no hook for eval

bhauman16:09:24

just like Clojure

bhauman16:09:42

But yes check the CIDER docs

bhauman16:09:02

there are some tricky parts, if you are using clj aliaes

lilactown16:09:07

:thinking_face: I wonder if you could use mount/component/whatever like in CLJ to re-render your app

orestis16:09:25

It might not even make sense to work this incrementally when doing UI work, but for some reasons I was convinced that it was the expected way.

bhauman16:09:04

inline eval is good for the same things in ClojureScript that it is good for in Clojure

bhauman16:09:23

good for checking expressions

bhauman16:09:31

resetting state

stvnmllr216:09:48

yes!! i often have to switch to clj files to try out my code before copying back to cljs

stvnmllr216:09:36

I use the repl sometimes, but hard for bigger chucks of code.

bhauman16:09:10

nah that’s just hard (editing in the REPL itself)

bhauman16:09:30

do you know about the CIDER scratch buffer?

dpsutton16:09:37

much prefer enter to add a newline and control-enter submit for evaluation

bhauman16:09:54

good to know 🙂

stvnmllr216:09:58

I don't know about CIDER scratch buffer, I'm in Vim so wouldn't apply right?

bhauman16:09:13

oh yeah that wouldn’t apply

bhauman16:09:24

but I guess you get the idea

bhauman16:09:45

you create a scratch buffer that you can edit and eval in

bhauman16:09:08

and you can accumulate expressions in there

bhauman16:09:42

in VIM you could just create a script file or something to do the same I guess

henrik16:09:18

For me, everything but Protorepl has been a struggle.

henrik16:09:41

Many swear by Cursive, but I have no previous IntelliJ experience, and found the number of bells and whistles daunting. No previous Vim and Emacs experience, so I found it mentally challenging to put what I’m actually trying to accomplish aside for the unknown number of days (or weeks? Who knows) it will take me to be sufficiently fluent in them. Calva and VSC seem nice, but don’t seem to support deps.edn etc. just yet.

g16:09:06

anyone know if there is an emacs discussion slack?

henrik16:09:01

With regards to editors, I feel like there are some aimed at beginners, some aimed at experts, and virtually none aimed att creating experts out of beginners.

dpsutton16:09:15

i think emacs is quite helpful at becomming an "expert". everything is documented, navigation to source code is built in. evaluation, debugging, etc. It is quite easy to start poking and prodding with a 20 minute introduction to where files are and how to navigate

g16:09:18

@henrik what would something like that look like?

dpsutton16:09:50

@gtzogana i doubt you will find lots of emacs people on a closed platform like slack. This gives an official emacs channel or irc though: https://www.emacswiki.org/emacs/EmacsChannel

dpsutton16:09:14

unless you mean #emacs

g16:09:15

that’s what i thought. thanks

g16:09:43

didn’t know about that one though, cool

sveri16:09:05

I'd raise a thumbs up for cursive + intellij idea. Easy setup and integration, everything you do not know the shortcut for can be clicked or reached quickly by the action bar.

henrik16:09:34

Designing anything that’s sufficiently curated, and yet sufficiently flexible, allows quick entry, and yet continued growth, is really hard in any domain. Arguably, that’s one of the key ingredients in what’s made Apple successful in their domain.

👍 4
sveri16:09:02

Idea delivers exactly that 🙂

sveri16:09:54

The only disadvantage in my eyes is that cursive is not open source.

henrik16:09:35

I feel like Lighttable had that goal inherently, along with the idea of taking the feedback loop further, but the project didn’t quite reach maturity.

henrik16:09:22

I bet Emacs and Vim are kind of like Clojure itself, a revelation once understood. From the “entry” aspect, though, it’s a challenging thought to need a tutorial to open a file or switch to another file.

lilactown17:09:10

that's one of the reasons that I started with (and still use) spacemacs

lilactown17:09:42

it did still require an investment of time, but the interface was at least discoverable through the HUD menus

henrik17:09:57

Emacs and Vim have a strength in being completely “other” though. The first impression of opening IntelliJ is like being transported back in time to the age where we’d just about popularized GUI, but hadn’t quite mastered it yet.

dpsutton17:09:04

I like that CIDER is open source. I like that cursive is closed source. One person makes his livelihood from it and is quite responsive

sveri17:09:33

Being completely different is a disadvantage. It makes discovery harder for the beginners. Apart from that, you can customize intellij idea so far you can use key strokes for everything.

sveri17:09:58

It also makes discovery harder when you took a longer break from emacs / vim, cause you have to look up shortcuts again.

henrik17:09:09

I can really see both of them having a niche in being by and for experts though, there’s nothing wrong with that.

sveri17:09:47

Thats true, I just wanted to give a counterpoint for someone starting out with clojure so that he does not have to learn clojure + some editor.

lilactown17:09:15

agreed sveri. that's why I'm really excited for Calva + VS Code

henrik17:09:50

The ecosystem aspect is hard to overcome as well. Lighttable ran into this. We could plot a new editor, I suppose, but it takes enormous muscle just to get the basic aspects right. Even if that is done right, there’s the even greater challenge of the network effects (or lack thereof). All editors need community to provide for the “able to grow into an expert” aspect, even if “entry” and the rest of it is taken care of.

👍 4
sveri17:09:34

Thats also the reason why clojure / clojurescript is so popular, cause its backed by java / javascript and the jvm.

sveri17:09:39

despite being a LISP

henrik17:09:51

Absolutely. It’s always mildly amusing when the JVM is brought up as a disadvantage when it’s so obviously a major reason for Clojure’s success.

Alex Miller (Clojure team)17:09:12

none of us would be talking about Clojure today if it didn’t use the JVM

Alex Miller (Clojure team)17:09:35

because it would be dead

👍 4
💀 4
henrik17:09:15

Or never would have been born, perhaps.

Alex Miller (Clojure team)17:09:17

which is not to say that the JVM is all benefits. but it’s benefits vastly outweigh its downsides

henrik17:09:33

No, it’s just tremendous leverage.

Alex Miller (Clojure team)17:09:48

that’s what I’m saying

Alex Miller (Clojure team)17:09:15

people frequently ask for Clojure, but native, ignoring the amazing (and essential) things in the JVM like garbage collection

henrik17:09:45

Thinking of editors, there’s been progress in UX for programmers, but to me it seems like aspects of “simple” and “easy” are as poorly defined in UX as everywhere else. Efforts to enhance UX seem too often to lead down an “easy” path to a local maximum.

henrik17:09:10

I’ve studied design problems in what could be generalized to two domains: let’s call them “the smartphone domain” and the “nuclear reactor domain”.

henrik17:09:36

You cannot and should not design a nuclear reactor control room to the same affordances as a smartphone.

henrik17:09:36

Broadly: smartphones have to be designed for things to be easy to get right. Nuclear reactor control rooms have to be designed for things to be hard to get wrong.

henrik17:09:00

The tricky bit about programming and developing is that it’s a bit of both.

bhauman17:09:22

folks want both, and they want it for every possible programming situation, ML, data science, web, libraries, operating systems

henrik17:09:01

For what it’s worth @bhauman, your efforts are astoundingly effective in pursuing both paths.

bhauman17:09:39

oh thanks, wish I had a team of 10 programmers and support

henrik17:09:42

It’s really, really hard to strike the balance.

henrik17:09:02

We haven’t even talked about market forces yet, but yes, if there was an obvious big pot of gold at the other end of this, everyone would be at it.

henrik17:09:17

Google, Apple and others do have incentive to improve the “easy” part, because that yields payoffs in their respective ecosystems, but it’s also limited to getting people entrenched in the ecosystems. Not too much attention to “basic research” or however you want to put it, but more of applicability to their specific business challenges.

henrik17:09:18

Jeez, it just dawned on me that this is #beginners and not #off-topic. Sorry everyone!

quadron17:09:19

Does anyone here have experience with both core.async channels and aleph streams? Does anybody have strong opinions in this regard?

quadron17:09:58

I meant manifold streams*

hiredman17:09:42

I've used core.async a fair bit, but haven't used manifold directly, but have spent a little bit of time looking at the manifold source. My impression is that core.async is to some degree based more in the formalisms of CSP, whereas manifold doesn't seem to be based on some underlying formalism, so core.async has fewer(but plenty of) kind of weird edges and omissions

hiredman17:09:16

particularly when trying to do something like core.async's alt!, manifold I don't think manifold can do that, you can get a deferred from two different streams, and then choose between the deferreds, but you can't un-get the deferred from the stream you didn't choose

4
hiredman17:09:55

but there maybe some different pattern for that case you are supposed to use in manifold (I use alt! a lot)

✔️ 4
henrik18:09:10

IIRC, core.async is a fundamental enough model that you can build most other async semantics with it.

noisesmith18:09:39

it prevents some operations that are allowed in a simpler model, and that way makes certain classes of errors impossible

henrik18:09:30

I've yet to come across something I couldn't model in the end with core.async though. But yes, some things are not allowed, and it takes some effort to reason with channels when starting.

noisesmith18:09:03

simple example: you can't look at a value ready from a channel without consuming it

noisesmith18:09:30

this is a straightforward operation in most queue based systems

henrik18:09:55

Yeah, it does stuff like that. You can't check if a channel is closed except by consuming from it.

noisesmith18:09:59

(but it introduces a class of race conditions that CSP avoids by not having that operation)

hiredman18:09:25

concurrent ml would be nicer, and would allow you to do things like extend core.async's alt! to work on a mix of core.async channels and java.nio channels

henrik18:09:26

Well, you can wrap anything you want in a channel really.

henrik18:09:32

A go-loop and a channel will handle most things.

hiredman18:09:08

that will likely work, but the go loop ends up acting as extra buffer

henrik18:09:14

Or it stops consuming from the channel, pausing your producer (assuming it's used to consume)

henrik18:09:40

It doesn't have to buffer, but will then pause instead.

henrik18:09:52

This is a desirable trait. I've used it to create customized threadpool-like functions.

hiredman18:09:30

go loops that read from one source and write to another must introduce a buffer of 1

henrik18:09:55

I.e., constraining concurrency to an arbitrary number of simultaneous executions.

hiredman18:09:59

because you read from one source, and then write to another, it doesn't atomically happen as once

henrik18:09:32

Oh yeah, something is always in flight.

henrik18:09:53

In the same sense that functions are buffers

henrik18:09:28

No, this is true.

hiredman18:09:48

anyway, cml is really neat

quadron20:09:25

@henrik what do you mean functions are buffers?

Mario C.21:09:12

Is it possible to have variadic multimethod?

hiredman21:09:57

yes, but your dispatch function also must be variadic in that case

Mario C.21:09:22

ahh okay I got it

Mario C.21:09:14

How would the (defmulti example ...) look like for a variadic one?

Mario C.21:09:10

nvm found an example