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 http://repl.it 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.
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 π
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 π
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.)]
;; <https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/LinkedBlockingQueue.html>
;; 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;
coll)
That should be (.drainTo q coll)
, I think. See also https://clojure.org/reference/java_interop
- I think there's a way to connect jupyter notebooks with Clojure - Reveal might make the experience more pleasant for them
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:
https://github.com/clojure/clojure/blob/7baa5b2f79f05c56a04e1d5c90129972cd962df3/src/clj/clojure/core.clj#L4465-L4466
user=> (let [defaults {:a 1}] (time (dotimes [i 100000000] (val (find defaults :a)))))
"Elapsed time: 379.921527 msecs"
nil
user=> (let [defaults {:a 1}] (time (dotimes [i 100000000] (contains? defaults :a) (defaults :a))))
"Elapsed time: 759.108181 msecs"
wow. crazy how different the speeds of (defaults :a)
(:a defaults)
and (get defaults :a)
are
feel free to file a jira
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"
nil
user=> (let [defaults '{a 1}] (time (dotimes [i 100000000] (second (find defaults 'a)))))
"Elapsed time: 6325.311779 msecs"
they are always symbols
yeah, and in the case of symbols the current implementation is way faster. hmm!
enforced by the spec
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"
nil
user=> (let [defaults '{a 1}] (time (dotimes [i 100000000] (contains? defaults 'a) (defaults 'a))))
"Elapsed time: 1593.39499 msecs"
nil
so yeah, I'll file a JIRA. yay!
I mean those numbers make sense to me based on what they're doing
the first is one lookup, the second is two
yes, that's why I wondered. so JIRA good?
yeah
Issue + patch: https://clojure.atlassian.net/browse/CLJ-2586
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.
mostly yes
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
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
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 @ghadi . 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
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
gotcha
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?
invokedynamic
I see an issue about that here: https://github.com/oracle/graal/issues/2762 Seems like they are working on it. Also the MethodHandle problem in Reflector.java 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.
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?
Thanks all for your suggestions. They've been helpful. I'll spend a bit more hammock time on this.
interesting, didn't realize https://github.com/oracle/graal/issues/2761 was in progress
Yeah, that would be cool. Currently I work around the problem in Reflector.java like this: https://github.com/borkdude/clj-reflector-graal-java11-fix
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β¦
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?
cryptographically
clojure.data/diff-associative
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
See also https://github.com/borkdude/speculative/issues/70#issuecomment-435185067
Thanks!
No. I don't.
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.
TIL:
user=> (defn foo:bar [])
#'user/foo:bar
Is this "supported" behavior? I found it here:
https://github.com/littleredcomputer/sicmutils/blob/6dc57d74e6f2ee22688b061012b14fa314636086/src/sicmutils/series.cljc#L58It wouldn't surprise me if this was an edge case in the reader
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. > https://clojure.org/reference/reader#_symbols
'foo::bar
doesn't work thoughone or more non repeating
Argh, thanks :) Reading is hard.
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.
best pun i've seen in a month
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.