Fork me on GitHub
Alex Miller (Clojure team)00:02:46

3% said they were using something older than 1.8, but they could also have been using something newer


just as a datapoint, CIDER now requires a minimum of 1.8 to work. There are still a few cljx based projects out there that are in wide use. cemerick's url package and its dependency pathetic for example


@noisesmith, so if I switch to refs instead of atoms, it would be ok to use watches? I'm little bit reluctant to just use a function, as even getting derived lookup-index from data that stored in first atom requires to process over million rows, that's why I was thinking about caching it.


sounds like what you want is a cache, and then an atom used to derive the cached values, and clear of (parts of) the cache if the atom changes


you might want to check out core.cache, and for that usage a cache used by a function over an atom would make sense, where alterations to the atom might clear some or all of the function's cache


using two atoms together isn't generally doable in a correct way, but a cache of a function over an atom is doable


Trying some examples from SICP I've found that Ratio is pretty slow - is that expected? E.g. using continued fraction to compute inverse of golden ratio:

(defn cont-frac-iter [n d k]
  (letfn [(frac-iter [i acc]
            (if (< i 1)
              ;; WARNING: if you don't use double coercion then the procedure will be slow for large k
              (recur (dec i) (double (/ (n i) (+ (d i) acc))))))]
    (frac-iter k 0)))

;; this is pretty fast
(time (double (cont-frac-iter (constantly 1)
                              (constantly 1)
;; "Elapsed time: 14.10361 msecs"
;; => 0.6180339887498948

;; BUT!
;; Original version without doulbe coercion and k=5000 took more than 3 seconds!!!
;; "Elapsed time: 3642.036658 msecs"
;; => 0.6180339887498948


Clojure Ratio values are exact, and I believe that every single arithmetic operation you do on one also does a greatest common divisor calculation to reduce the numerator and denominator integer values to lowest terms, changing the type to an integer if the result happens to be one.

👍 5

Because the results are exact, if the numerator and denominator get large, then all calculations will be doing exact arithmetic on large integers.


Great, thanks for enlightenment

Wes Hall12:02:30

Folks, this strikes me as a question where I am sure that the answer is going to embarrass me, but I am drawing a mental blank. Is there a simple way to toggle a single item within a set. I.e. if it's there remove it, if not add it? Used to do this with set xor in my JS days.


Besides (if (contains? s i) (disj s i) (conj s i))?

Wes Hall12:02:20

Yes, exactly the code I was trying to simplify.

Wes Hall12:02:50

Had a feeling there might be a (xor s #{i}) type thing.

Wes Hall12:02:57

No big deal but felt like it might be there.


put that in a fn called toggle-member?

Wes Hall12:02:11

Yeah, can definitely do that. I just had this, "I am sure there is an API function for this" feeling. If not it's good, means I am not going crazy 🙂.


not sure what you want to do, but set/intersection might help


(set/difference s (set/intersection s #{i}))?

Wes Hall12:02:07

I think the first example is probably the most readable barring the existence of a (toggle s i) function or similar. I occasionally get this weird thing that happens in my brain where I think, "Ok, I am sure that there is either a built in function for this, or an obvious pattern that I am missing". No worries, thanks for your help.


hmmm… doesn’t do the “if it’s no there, add it part”


You might want to use a map: (update {:foo true} :foo not)

Wes Hall12:02:09

Hah! That's a neat trick. I think this is the kind of thing I meant by some pattern that I might be missing. Will explore whether converting to a map makes sense. This is all "first world problems", the contains check is not that complex anyway really.

David Pham12:02:28

Is it possible to write docstring in defmethod when using multimethod?

Wes Hall12:02:20

@neo2551 To my knowledge, no. Docstring goes on the defmulti.


I have this weird problem, where I get a ClassCastException when calling (class x) but no error when calling (.getClass x). Here's the code:

(let [^Property src        (get-prop inst)
       [target-id target-prop] val
       target-inst (tree-search/find-nearest-by-id inst (str target-id))
       target-class (.getClass target-inst)
       get-target-prop (get-property-fn target-class target-prop)
       ^ObservableValue target     (get-target-prop target-inst)]

       (println "Now binding" src "with" target)

       (.bind src target))
This works, but when I replace (.getClass target-inst) with (class target-inst) I get: java.lang.Class cannot be cast to clojure.lang.IFn. What's going on here?

Wes Hall13:02:50

@roti Have you shadowed, "class" somewhere else in the context? (def class ...) etc


yes, I have. damn this has cost me something, why didn't I see it?


I should take a break 🙂

Wes Hall13:02:09

Not a problem. I only saw it because I have done similar things myself 🙂

David Pham13:02:49

@wesley.hall Thanks a lot for your answer 🙂

David Pham13:02:06

Guess I am doing something wrong if I need to have docstring in the method xD

Wes Hall13:02:31

@neo2551 No worries. I actually quite often drop in a regular comment at the top of my multimethods in place of an actual docstring. Seems that I often find it useful to give a little explanation about what this specific case does. There is probably nothing to attach an actual docstring to though by the time all this stuff gets munged down to .class or .js.


@neo2551 No you’re not doing it wrong. It’s a flaw IMO.


I needed this the other day. I’ll play around w/ it more real quick.

Noah Bogart14:02:34

So I have a test helper function that checks to see if a given string is in hashmap state, which I use like (is (last-log-contains? state "Barrier")). This works fine when it's correct, but when it's not correct, the output is: expected: (is (last-log-contains? state "Barrier")) \n actual: (not (last-log-contains? [FULL STATE CONTENTS HERE] "Barrier")). Because of how this app works, the state hashmap can occasionally be extremely massive, which is tough to handle in test output.

Noah Bogart14:02:53

(defn last-log-contains?
  [state content]
  (some? (re-find (re-pattern content)
                  (-> @state :log last :text))))

Noah Bogart14:02:23

Is there any better way to write the function to avoid this? Is there any better way to write the is assertions to avoid this?


perhaps you could check (select-keys state ["Barrier"]) for the key rather than the full map. When you are wrong you will be presented only with the value of the thing you are checking

Noah Bogart15:02:11

That's not a bad idea! Let me try that

Noah Bogart15:02:33

And thank you @dpsutton and @bronsa! Your help yesterday was fundamental to me getting my PR up


that's awesome. was there any performance gain? measured or just "felt"?


or just feel better that you are doing things more correctly. that's always a win too 🙂

Noah Bogart15:02:14

haha Mostly the last one. Compile times seem to take a bit longer, and the tests were already pretty quick, but this is a major win for DRY

Noah Bogart15:02:10

We had 3 macros that did similar but slightly different things, and this allows me to extract the main let bindings that were shared between the 3 into a single spot


so a couple things


you could use the meta reader macro anywhere you want


e.g. (defmethod ^{:doc "foo"} foo* :bar [args])


the problem with that^ is


You basically can’t get back to that docstring at runtime. It’s attached to the symbol foo*.


You could also do something more drastic, like re-implement defmethod:

(defmacro defmethod*
  [multifn dispatch-val mymeta & fn-tail]
  `(. ~(with-meta multifn {:tag 'clojure.lang.MultiFn})
      ~(with-meta `(fn ~@fn-tail)


Then you could get back to your docstring via: (meta (get-method foo* :baz))


However, it’s odd, because the docstring is attached to the function. Whereas the docstring of the multimethod is attached to the var.

Alex Miller (Clojure team)15:02:20

there's a jira about this btw


i.e. you get the multimethod docstring like so: (meta #'foo)


ah good to know


So the unfortunate truth is, as @wesley.hall deduced, there’s no consistent place to hang a method’s metadata.

Alex Miller (Clojure team)15:02:48

well, I thought there was, not seeing it now. I know I've had some conversation about it in the last 6 months

Wes Hall15:02:46

It doesn't seem like there is much point in supporting it because defmethods have no real identity. All of the tools that give power and usefulness to docstrings rely on being able to talk about a named thing, but defmethods are not named things. Perhaps if things like the (doc) function were changed to optionally support a dispatch value in the case of multimethods or something, but otherwise... why bother? 🙂

Alex Miller (Clojure team)15:02:55

but yes, that's the tricky bit

Wes Hall15:02:39

I would say, in my case it would only be supporting docstrings in the usual "docstring location", even if they were ignored entirely at runtime, but all that is really for is consistency of documentation in the code itself.

Wes Hall15:02:16

It's not really a problem to drop in a standard comment, they just look different in cursive and I don't get the pretty green 🙂

David Pham15:02:29

Thanks a lot for all your answer!


(def valid-guid?
    #(int? %)
    (constantly 1)))
=> #'test-ns/valid-guid?
(s/form valid-guid?)
=> :clojure.spec.alpha/unknown


is this expected behavior?


$ clj -A:test
Clojure 1.10.0
user=> (require '[clojure.spec.gen.alpha :as gen])
user=> (require '[clojure.spec.alpha :as s])
user=> (s/def ::guid (s/with-gen int? #(gen/return 1)))
user=> (s/form ::guid)
user=> (gen/generate (s/gen ::guid))


(def valid-guid?
    (constantly 1)))
=> #'test-nss/valid-guid?
(s/form valid-guid?)
=> clojure.core/int?


right, anonymous functions in with-gen breaks s/form


In spec-alpha2 that won’t work at all anymore, so you better get used to it 😉


form won’t work at all with with-gen specs?


or you won’t be able to pass anonymous functions to with-gen?


you cannot do (s/form int?) in spec 2. you must write (s/form (s/spec int?))


would (s/form (s/spec #(int? %))) work?


This is what I get:

user=> (s/form (s/spec #(int? %)))
(fn [%] (clojure.core/int? %))


minor, but why are you writing #(int? %) instead of int?


just a simple example


Is there an existing pretty printing socket server repl I can utilize? Or do I need to write my own using clojure.pprint/pprint, is clojure.pprint/pprint even suited to pretty printing in a repl?


doesn't CIDER offer that but default to fipp?


meaning it should be fine?


yeah and fipp as a specialized fn that's repl friendly if I recall


I'm happy to use fipp for dev, but I'm not so sure about production with fipp. I don't want to conflict with someone else's version of fipp.


Cider offers a clojure.pprint integration too though


Is it possible to check whether a given symbol corresponds to a protocol method? e.g. (protocol-method? 'my-method) => true, (protocol-method? 'inc) => false


resolve it, and inspect the var, if it resolves


right, metadata of such vars has a :protocol key. thanks!


@dominicm Fipp is very stable. It should be safe to just forcibly use the latest version.


I promise stability.


@bbloom your word is sufficient, fipp it is!


if you ever have a problem w/ that, feel free to complain to me directly and i’ll get a fixed build out


Is there ever a legitimate use of a map from keywords to functions instead of using a multimethod?


I suppose if the function to be called can change at runtime based on context. Meaning you have one keyword, but the fn associated with that can change.


If your multifn is just dispatching off of keywords, seems like mostly the same thing to me. You're just able to define your dispatch map across multiple namespaces and downstream projects.


If all dispatch logic fits in one ns, a simple map would be just as good imo


@john yeah… basically, when dispatching on some function of an argument, a multimethod seems natural (or when there is a need for extensibility). But in my project I found myself passing a keyword as the first argument to my multimethod (a dispatch key) which is then totally ignored by the body of the function. It seems odd / a map would be cleaner


Yeah... IMO it would be cleaner, in some cases. The multifn approach is just so extensible - let's you build out that dispatch mechanism across namespaces.


and let's downstream authors extend your dispatch map


fair; makes sense. Ty 🙂


np. Feel free to check out dispacio if you're looking for any more inspiration around dispatch strategies


The finest-grain control might be to plug in interceptors, for manually constructing dispatch strategies


Ah; I’ll definitely check this out! I had a similar question some time ago about using pattern matching v.s. multimethods; I think the answer is the same; it depends on how much you care about extensibility


Why do a lot of clojure functions on sequences return lists? e.g. (rest [1 2 3]) returns (2 3), forcing me to write (into [] (rest [1 2 3])) to get a vector back. Am I doing something wrong?


Well, more specifically they return sequences


FWIU, the list is the underlying data type of the seq abstraction, which all functions that operate over sequences recognize.


If you are using rest for your computations things will be more efficient with linked lists (probably, by guestimate)


Either way there is always subvec

Alex Miller (Clojure team)22:02:38

@hmaurer @john protocols are implemented as a map of names to functions

Alex Miller (Clojure team)22:02:08

but special in having call-site optimizations


Well, there ya' go 🙂

Alex Miller (Clojure team)22:02:05

there's plenty of places where a map of "name" to function is a better simpler choice than a multimethod or protocol


Ah. Am I doing something wrong by assuming in my code that I have a vector? Because, say, if at some point in my code I assume that I have a vector and use conj to append an element to the end, I now have to add into [] or similar to all sequence operations I do on that vector, to ensure it remains a vector


Thanks @alexmiller for the extra explanation on multi-methods


You have to know, yeah. Otherwise conjing may do the different thing.

Alex Miller (Clojure team)22:02:19

why do you need to ensure it remains a vector?


Well otherwise conj might behave differently on it

Alex Miller (Clojure team)22:02:54

in practice, I'd say this is not a problem I ever have


hmm, maybe I am thinking of it as a problem but it wouldn’t be in practice. I’ll try to ignore it and see


If you want to not worry about it you can always whip up your own fn


I'm a bad programmer so I would


But I would feel bad afterwords


@emccue yeah, but I found myself bumping into it over and over and I thought that if the standard library behaves in that way then I might be doing something wrong

Alex Miller (Clojure team)22:02:24

as you will then just build your own bespoke collection handling that is different than what is done everywhere else


since I doubt all clojure devs re-write those functions, otherwise they would be in the std lib

Alex Miller (Clojure team)22:02:46

if you're having this issue, back up and explain how you got there

Alex Miller (Clojure team)22:02:32

generally once I have sequences, I am never "conj"ing to them, I am manipulating them at the sequence level with sequence functions


it is usually a problem if you are trying write what is an imperative algorithm using arrays in clojure


which, if you are doing that, don't use vectors, clojure has arrays

Alex Miller (Clojure team)22:02:43

I think you're maybe off down a side path from where hmaurer started

Alex Miller (Clojure team)22:02:06

I rarely use conj directly

Alex Miller (Clojure team)22:02:52 is probably something visible I work on regularly. it has tons of data structures and data structure manipulation. I use conj twice in the whole code base.

Alex Miller (Clojure team)22:02:08

or I should say, in two functions


Rarely does a clojure programmer not know obviously what the type being passed to conj is, I suppose. I rarely find myself getting bit by this either.


for example, if you where directly translating some javascript code, js allows you to call push or whatever (I guess an arraylist would be a better analogue to that in clojure) to push a value on to the front of an array


and basically all the clojure collection code is not built to support that style of usage


Well, I started like this. I have a map which I pass down some function, a “step”. This map contains a special key, e.g :remaining-steps [:a :b :c] with the steps left to execute. What I do is I take that map, and run it through all those steps until that array is empty (so first :a, look up the corresponding function, run it on the map, and keep going). Those functions can also push elements to the beginning of the vector under that key, adding more steps.


so if you were trying to use it like that, you would end up doing something silly like (into [] ...) everywhere


I guess? I wasn’t thinking of it that way. It’s more like a bag of information that I run through a sequence of stages


like a bunch of middlewares


sequence of steps *


basically like a middleware chain except that instead of directly calling the next one I add a keyword under a special key and some functions “above” is in charge of calling them in order until there are no more to call

Alex Miller (Clojure team)22:02:21

it's unclear from what you said whether you want a stack or a queue. if you want a stack, the fastest option is to use a list (and cons/peek/pop). You can also use a vector as a stack with insertion point at the end, but that's slower than a list.


(update amap :remaining-steps into :extra-step) or conj. But you already know it's a vector, so you know which direction conj works in in that case.


Oh you want to push it to the front?


just don't start with a vector and your stack will be great


alright, fair enough


or #queue


But some seq ops will lose your queue type

Alex Miller (Clojure team)22:02:45

note that that syntax is cljs-only

Alex Miller (Clojure team)22:02:23

clj has a queue, but not the literal for it, you just use clojure.lang.PersistentQueue/EMPTY to get an empty one


Folllow up question: is there a cleaner way to prepend a list of elements IN ORDER to a list than doing: (into some-list (reverse elements-to-prepend)) ?


e.g. pretend '(4 5) to '(1 2 3) to get '(4 5 1 2 3)


concat (with caveats)


said caveats being?


but what you are talking about is concat, the caveat with concat is becuase it builds lazy seqs if you are using it without care you will blow the stack when you go to realize it


you may want to reformulate how you keep your queue


instead of a list of items, keep a tree


tbh performance matters 0 in my case


I would just use concat


one way to think of something like your remaining steps is as a program, and what you have is the simplest kind of program, a linear list of instructions


so you could support more complicated trees of instructions, so something like clojure's do


sorry, what do you mean by this? (as a program)


it is a sequence of instructions for some abstract machine


you have `[do-x do-y do-z] and some program (the abstract machine) takes do-x off the control stack and does it, possibly pushing other things on to the control stack


yep that’s pretty much what I have


except that my do-x do-y do-z are keywords


so you might have something like [[:do-2-things do-x do-y] do-z] and have your abstract machine support do-2-things by pushing the 2 things back on to the control stack


hmm, I see


I don’t need that flexibility (for now) though, I think


Basically at first I was just piping something through functions with ->, but I needed to add some machinery between every step, so I wrote this simple abstract machine because

Dustin Getz23:02:31

when reloading a namespace with a defprotocol, Is there a way to avoid the dreaded IllegalArgumentException No implementation of method: :foo of protocol: #' found for class: clojure.core/-cache-protocol-fn (core_deftype.clj:527) by unloading the protocol somehow at the repl?

Dustin Getz23:02:16

the readme for toolsnnamespace says “To avoid this problem, always create new instances of records after a refresh.” which doesn’t make sense to me, defprotocol is the thing that is throwing on reload

Lennart Buit23:02:37

what they probably mean is that you need to refresh your instances of your FooRecord, because if they keep surviving post refresh they refer to the old protocol

👍 5
Lennart Buit23:02:01

one way this can happen is if you def them in a namespace, which you :refer :all.

Lennart Buit23:02:18

That said, I have very similar issues often, so there is definitely more going on


I asked about this problem in this channel a couple times very recently. no luck... my conclusion (as suggested by someone else) is to resort to metadata-based protocol extension. I find the feature nifty anyway. You can vary-meta either defrecords ('thin' defrecords: implementing no protocols), or plain maps


if you support some kind of do operation in your machine, and add continuations, you can process trees of instructions without having to do annoying concats of instruction sequences


@hiredman what do you mean by continuations in this context?


the continuation of your abstract machine


as in? picking up evaluation again at some state?


yeah I do support that


if you have that, then instead of having to implement do by pushing things back on to the control stack, you push a new continuation that continues with the old control stack after the new control stack executes