Fork me on GitHub
#beginners
<
2018-01-30
>
Chris Bailey05:01:57

Trying to create a multimethod with implementations that have multiple arity, but can’t quite get it. Something like this:

(defmulti fixture identity)

(defmethod fixture :user
  ([_] (fixture :user {}))
  ([_ attrs] (create-user (merge {:name "Chris" :email ""} attrs))))

(fixture :user)
(fixture :user {:name "Fred"})

Chris Bailey05:01:47

The (fixture :user {:name "Fred"}) version works, but I get an ArityException on (fixture :user)

seancorfield05:01:29

@chris.bailey The despatch function needs to accept the same arities as you want the defmethod to support. identity only accepts one argument.

Chris Bailey05:01:21

ok, I also tried it with fn [kind & args] kind, but same issue (sorry, newb on this, so I think I’m missing something maybe obvious?)

Chris Bailey05:01:22

I tried making the dispatch a multi-arity method too, with no luck, but I may not be implementing the actual dispatch function correctly either?

seancorfield05:01:01

Like this:

(defmulti fixture (fn [kind & args] kind))

Chris Bailey05:01:09

hmm, ya, that’s what I had/have, and I still get the artityException when I do (fixture :user)

seancorfield05:01:48

You probably need to restart the REPL. Multimethods can't just be redefined I think...

seancorfield05:01:21

(I haven't used them much in the REPL but ran into strange behavior just now trying to redefine an example to try it out)

Chris Bailey05:01:10

oh, snap! restarting the repl fixed!

Chris Bailey05:01:19

oy, thank you!

Chris Bailey05:01:42

REPL restarts (when needed) are def something I’m learning/finding out about 😕

seancorfield05:01:03

I'm sure there is a way to redefine multimethods without restarting the REPL... I've just never needed to figure it out 😐

greglook05:01:46

you can probably ns-unmap the previous definition

Chris Bailey05:01:56

nah, tis totally fine, small frequency, so I just need to beware of it in the future

greglook05:01:02

but you also need to re-eval all the defmethod forms too

seancorfield05:01:11

Ah, looking at the definition of defmulti, it only defines the function if it doesn't already exist...

seancorfield05:01:20

So you can do

(def fixture nil)
and then defmulti will work

seancorfield06:01:44

@chris.bailey That'll save some pain in future -- just def it to nil and then do defmulti (and defmethod) again!

Chris Bailey06:01:40

awesome, thanks!

noisesmith12:01:49

the easy way to rebind a defmulti is (def fixture nil) before reloading the defmulti

noisesmith12:01:16

defmulti refuses to overwrite a defmulti, but def doesn't care, and defmulti will overwrite a def

flowthing12:01:10

IIRC, (defn dispatch-fn [kind & args] kind) (defmulti fixture #'dispatch-fn) should also be reloadable.

noisesmith12:01:21

oh, yeah, that should work

noisesmith12:01:58

but it would be sad to see all the defmultis obfuscated like that

flowthing13:01:11

Yeah, it's not optimal.

Nick Stares14:01:46

why is ['(1 2 3)] different from (vec '(1 2 3))?

joelsanchez14:01:59

vec turns a collection into a vector, while in the first example you have a list inside a vector

joelsanchez14:01:57

the first example would be equivalent to (vector (list 1 2 3))

joelsanchez14:01:13

probably that's the source of your confusion: vector != vec

edwaraco14:01:15

I think the first is taken like a vector from 1 element (`(1 2 3)`), in the second it's vector of three elements because '(1 2 3) is taken as list

Nick Stares14:01:45

ah did not realize that vector != vec. thanks

Charles14:01:53

Howdy folks. I have been trying to get datomic pro to work locally. I started the peer-server and added the dependency to my project. When I start the repl and try to issue the require command, I get an error: "CompilerException java.lang.RuntimeException: Unable to resolve symbol: halt-when in this context, compiling:(datomic/client/api.clj:57:11)" Any thoughts?

joelsanchez15:01:57

halt-when was added with clojure 1.9....so maybe related to clj version

Charles15:01:08

I am using clojure 1.8

joelsanchez15:01:36

it seems odd that you seem to be compiling datomic's code instead of simply using the jar...but I never used peer server

joelsanchez15:01:21

maybe execute lein deps :tree and see what version of clj does the library require

joelsanchez15:01:33

you may need to use a version that supports clj 1.8, or upgrade to 1.9

nulligor18:01:59

Hey guys, any recommendations regarding choosing either a database or some sort of construct to persist 2 files (or collections) prone to application state mutation?

noisesmith18:01:27

IMHO treating a thing outside process (inside a storage engine) as being a "collection" is asking for trouble - treat it as an external source of information with all that entails because it has failure modes and gotchas that collections never have

nulligor18:01:15

even though said external source of information goes through a lot of mutation?

noisesmith18:01:29

I've had great luck with using kafka channels to stream events that represent status updates - then every consumer can replay the same stream and achieve the same state

nulligor18:01:41

:thinking_face:

noisesmith18:01:44

because clojure collections are immutable

noisesmith18:01:51

external storage can't be

nulligor18:01:20

at first, to keep it ultra simple I was thinking of swapping atoms

nulligor18:01:30

might definitely not be the best approach

noisesmith18:01:02

oh - I assumed you meant something outside the vm, shared across runtimes or servers

noisesmith18:01:30

atoms work great if you don't need a guarantee that the state persists between servers or restarts, I might have misunderstood your question

nulligor18:01:14

oh, cool! Yea I'm assuming only a single instance with a single source of truth (which would be these 2 collections and their state thats constantly shifting)

nulligor18:01:49

I could go for a database approach but that might be overkill

noisesmith18:01:54

right - but still I'd suggest adjusting the mental model - in clojure collections never change, the container i nthe future might hold a different collection though

noisesmith18:01:35

an atom synchronizes changes to a container, that change usually means putting a different collection inside - each of those collections is immutable

nulligor18:01:25

I see, thank you very much for the insights!

nulligor18:01:42

I'll dig deeper, might end up going this way though

justinlee22:01:09

I like to write functions using the “named parameters” trick like so:

(defn labeled-input
  [& {:keys [name class type on-cancel focus?]}] ...
Some of these are optional and some are not. I was hoping to use spec (or some other library) to check to ensure that I’ve always specified the mandatory parameters and to throw an error if something unexpected is passed. I can’t figure out how to get spec to do this since it seems focused on positional parameters. Are there any libraries out there that help with this?

andy.fingerhut22:01:23

@justinlee I haven't done it before, but I have seen keys* mentioned when such things are asked about.

noisesmith22:01:39

if you had an :as clause in the map couldn't you spec the thing that binds to it?

andy.fingerhut22:01:58

Search for keys* (with the asterisk after the "s") on this page: https://clojure.org/guides/spec

justinlee22:01:57

@andy.fingerhut nice! thanks. i read that whole page and didn’t register that.