Fork me on GitHub

I'm going to give a (remote) introduction of clojure for some coworkers. I'll check the popular plugins for their editors (mostly vscode I think), but some of them are non-programmers. For them, I'm looking into the easiest to setup clojure ide. Maybe even something on cloud. I looked into but it doesn't allow to eval a single expression, only the whole file, and that seems so limiting when it comes to lisps.


Why not just use default repl for non-programmers? Which features of IDE you want to use? IMHO, just using the plain repl will make things easier in terms of tooling and setup issues.


BTW I've tried cursive and I'm currently using vscode(with calva and clj-kondo). vscode feels easier for me.


Using the repl directly is something that I didn't even think of doing, but actually makes sense, specially at the beginning. It's a good way to make them understand that the ide isn't doing much more than the line you're evaluating to the repl.

💪 3

But it's quite limiting, and not really a fun way to program, IMO. My main goal is to have them continue coming to the clojure lessons 😅

🙃 3

IMHO, it'd be better to do a quick intro to vscode + calva, at least to show how to set it up and then proceed to clojure. Hmm.. before vscode you can also do a quick intro to the plain repl itself, then say that there are much better tools for clojure dev and then show them vscode. You'll figure it out 🙂

calva 3
Ben Sless13:11:15

- I think there's a way to connect jupyter notebooks with Clojure - Reveal might make the experience more pleasant for them


Once leinigen or clj is installed, Calva is for sure a pretty easy step. But for people who aren’t devs, getting Java and lein installed can be quite daunting…


That is true... I've also never installed clojure outside of Linux, and it's remote so hard to help. I'm actually considering using babashka instead.


Thanks for the suggestions. I'm an emacs user, so I'll install vscode + calva by myself to see how easy it is and if I can guide people through it.


I was thinking if babashka might be the way to go. I've been wanting to bundle it with Calva. That would remove so much friction towards just trying the language out. Anyway, if you use babashka, what you need to do is to start it with its nrepl option and then connect Calva. When is this intro to happen?


It's still not decided, could be in a week or in a month. I actually just got news two hours ago that I'm allowed to fly back to Singapore, where my work is located. This would mean I could do it in person, in small groups of 5 people max (covid is under pretty good control there).


Bundling babashka with calva would be great! Maybe as an optional download.


Java interrop question.. How can I call .drainTo in Clojure on a BlockingQueue? Attempt below:

(import '[java.util.concurrent LinkedBlockingQueue BlockingQueue]
          '[java.util ArrayList])

  (let [^BlockingQueue q (doto (LinkedBlockingQueue.)
                           (.put [:command 1])
                           (.put [:command 2])
                           (.put [:command 3]))
        coll (ArrayList.)]

    ;; drainTo(Collection<? super E> c)
    ;; Removes all available elements from this queue and adds them to the given collection.

    (.drainTo coll)
    ;; FAIL
    ;; Unhandled java.lang.IllegalArgumentException
    ;; No matching field found: drainTo for class java.util.ArrayList

    ;; FAIL
    ;; (.drainTo (make-array (type []) 3))
    ;; Unhandled java.lang.IllegalArgumentException
    ;; No matching field found: drainTo for class [Lclojure.lang.PersistentVector;



That should be (.drainTo q coll), I think. See also


gah of course! thanks


works perfectly 👌:skin-tone-4:


This might be an unimportant performance thing, but why isn't destructure using find + val here:

user=> (let [defaults {:a 1}] (time (dotimes [i 100000000] (val (find defaults :a)))))
"Elapsed time: 379.921527 msecs"
user=> (let [defaults {:a 1}] (time (dotimes [i 100000000] (contains? defaults :a) (defaults :a))))
"Elapsed time: 759.108181 msecs"

Alex Miller (Clojure team)14:11:27

feel free to file a jira


wow. crazy how different the speeds of (defaults :a) (:a defaults) and (get defaults :a) are


Actually the or defaults are usually symbols:

user=> (let [defaults '{a 1}] (time (dotimes [i 100000000] (contains? defaults 'a) (defaults 'a))))
"Elapsed time: 1583.053364 msecs"
user=> (let [defaults '{a 1}] (time (dotimes [i 100000000] (second (find defaults 'a)))))
"Elapsed time: 6325.311779 msecs"

Alex Miller (Clojure team)14:11:06

they are always symbols


yeah, and in the case of symbols the current implementation is way faster. hmm!


nope, sorry, I was using second which causes the slowness:

user=> (let [defaults '{a 1}] (time (dotimes [i 100000000] (val (find defaults 'a)))))
"Elapsed time: 697.04199 msecs"
user=> (let [defaults '{a 1}] (time (dotimes [i 100000000] (contains? defaults 'a) (defaults 'a))))
"Elapsed time: 1593.39499 msecs"


so yeah, I'll file a JIRA. yay!

Alex Miller (Clojure team)14:11:04

I mean those numbers make sense to me based on what they're doing

Alex Miller (Clojure team)14:11:18

the first is one lookup, the second is two


yes, that's why I wondered. so JIRA good?

Alex Miller (Clojure team)14:11:20

could you try to make a perf test that actually uses destructure ?


is this causing an issue in real world code?


it's not, but why not take the perf if it doesn't make the code any less readable


because it's not costless to accept changes


this is only called at macroexpansion time, right?


> This might be an unimportant performance thing Alex responded: feel free to file a jira. He also could have responded with: not important, bye.


in sci I do call it at runtime


but I'll make a perf test with destructure itself and I'll let it sit there until it's closed in 2028 ;)


please don't be sarcastic


core team works hard to manage issues


again, I asked the core team (Alex) if this was unimportant and the reaction was: feel free to file a JIRA. That's what I did, ok?


there is a non-issue, imho -- lots of macrotime things use the not most performant code


I would also have accepted: no


(like last)


macroexpansion has impact on startup time which is not unimportant I would say

Alex Miller (Clojure team)15:11:20

I think it's worth doing - we have chased micro gains in this area in the past given how common destructuring is


you are misreading the ticket @alexmiller


it's not the code emitted by destructuring that is being optimized (which would be worthwhile)


it's the destructuring macro helper itself

Alex Miller (Clojure team)15:11:17

I'm ok with bringing it to rich, he can decide


I'm accepting any outcome, I just found this while fixing a bug in sci. I appreciate the hard work the core team is doing. In the grand scheme of things this may not matter very much and that's ok. I don't find a noticable difference with:

(time (dotimes [i 10000000] (destructure '[{:keys [a] :or {a 1}} {:b 1}])))
(time (dotimes [i 10000000] (destructure2 '[{:keys [a] :or {a 1}} {:b 1}])))
where destructure2 is the patched one.


I'll add this to the ticket.


I do see potential speedups in the generated code, e.g. replacing get with a keyword lookup when :keys is used, etc, but I haven't considered this longer than core probably has. So I'll leave it at this for now


I didn't mean this sarcastically btw @U050ECB92 . There's so many trade-offs to make and probably this has been considered ages ago.


no worries.


i'm salty about the election and running on far less sleep than usual

💜 3

I can imagine... hoping for a good outcome...!


(:foo m) emits a lot of code, but is faster


emit a lot of code -> might make the surrounding method uninlinable


indy would make that go away, but interferes with graal native image


i'm hoping project leyden will have a hook for rewriting indy


what's indy?




I see an issue about that here: Seems like they are working on it. Also the MethodHandle problem in JDK11 would be solved with that.


> Note that the performance of such method handles will be slower compared to the Java HotSpot VM because no dynamic compilation of method handle chains is possible.


interesting, didn't realize was in progress


Yeah, that would be cool. Currently I work around the problem in like this:


I am wanting to replace an old Java service (slow and somewhat buggy) with Clojure. The interface to this system inputs/writes out several XML files marshallled and unmarshalled by JAXB-annotated code. I have the .xsd files defining the 250 or so classes that can be in-/output by this service (and of course, the generated Java classes). Does Clojure have any tools to make this replacement simpler or am I stuck updating the old Java code? So far, I've run across libraries like clj-xsd, which parses .xsd files, but gives no help as to reading/writing the XML data files that might be marshalled/unmarshalled by JAXB. And, although there are one-or-two libraries that mention JAXB, they seem to be moribund/aborted. Has anyone even attempted something like this in the past, or is this a data format that Clojure doesn't have a very good answer for?


I think most clojure solutions deserialize XML to nested maps, obviating the need for classes generated from xsd


nested maps do require more memory than plain java objects


alternatively you can keep JAXB and annotated classes and you can deserialize to those objects, but then use Clojure to process them


That's what I've sort of figured. The sheer number of classes potentially contained by the XML is giving me pause, though. I was thinking if there was some sort of way to generate Clojure code from the .xsd files for marshalling/unmarshalling the XML to records - which can be used like maps, my internal processing would potentially be a bit simpler and I could avoid manually generating the XML output.


do you need to sign the xml?




No. I don't.


Hum.... Not sure a Record uses any less memory then a map. I'd ignore the "fear of maps using more memory" and just parse the XML into maps


Thanks all for your suggestions. They've been helpful. I'll spend a bit more hammock time on this.

p-himik16:11:52 is implemented as:

(defn- diff-associative
  "Diff associative things a and b, comparing only keys in ks (if supplied)."
  ([a b]
     (diff-associative a b (set/union (keys a) (keys b))))
  ([a b ks]
But (set/union (keys a) (keys b)) when a and b are both regular maps can result in duplicate values because keys does not return a set-like object:
(clojure.set/union (keys {1 1}) (keys {1 1}))
=> (1 1)


So it seems like diff might do some useless work because it violates clojure.set/union contract.


yeah, calling set/union on non-set inputs is a bug


Yes, this is the one place I know of in Clojure's core code that it would violate an imagined spec on clojure.set/union that it only takes sets as inputs.



user=> (defn foo:bar [])
Is this "supported" behavior? I found it here:

😆 3

It wouldn't surprise me if this was an edge case in the reader

Darin Douglass18:11:22

per the reader docs, it seems like it's supported: > Symbols beginning or ending with ':' are reserved by Clojure. A symbol can contain one or more non-repeating ':'s. >


doesn't work though


one or more non repeating


Argh, thanks :) Reading is hard.


best pun i've seen in a month

☝️ 3