clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
solf 2020-11-04T11:04:43.089500Z

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.

tugh 2020-11-04T11:22:13.089900Z

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.

tugh 2020-11-04T11:23:57.090100Z

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

solf 2020-11-04T11:40:53.090400Z

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.

πŸ’ͺ 1
solf 2020-11-04T11:42:18.090600Z

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 πŸ˜…

πŸ™ƒ 1
dominem 2020-11-04T12:09:17.090800Z

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 πŸ™‚

1
2020-11-04T12:50:41.091900Z

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&lt;? super E&gt; 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)

flowthing 2020-11-04T13:15:43.092300Z

That should be (.drainTo q coll), I think. See also https://clojure.org/reference/java_interop

Ben Sless 2020-11-04T13:43:15.092800Z

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

2020-11-04T13:56:58.093200Z

gah of course! thanks

2020-11-04T13:59:13.093400Z

works perfectly πŸ‘Œ:skin-tone-4:

borkdude 2020-11-04T14:32:35.094400Z

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=&gt; (let [defaults {:a 1}] (time (dotimes [i 100000000] (val (find defaults :a)))))
"Elapsed time: 379.921527 msecs"
nil
user=&gt; (let [defaults {:a 1}] (time (dotimes [i 100000000] (contains? defaults :a) (defaults :a))))
"Elapsed time: 759.108181 msecs"

dpsutton 2020-11-04T14:36:11.095300Z

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

alexmiller 2020-11-04T14:39:27.095400Z

feel free to file a jira

borkdude 2020-11-04T14:39:36.095800Z

Actually the or defaults are usually symbols:

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

alexmiller 2020-11-04T14:40:06.096300Z

they are always symbols

borkdude 2020-11-04T14:40:33.096800Z

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

alexmiller 2020-11-04T14:40:42.097Z

enforced by the spec

borkdude 2020-11-04T14:41:35.097300Z

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

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

borkdude 2020-11-04T14:41:51.097500Z

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

alexmiller 2020-11-04T14:42:04.097800Z

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

alexmiller 2020-11-04T14:42:18.098Z

the first is one lookup, the second is two

borkdude 2020-11-04T14:42:34.098400Z

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

alexmiller 2020-11-04T14:42:38.098600Z

yeah

borkdude 2020-11-04T14:52:26.099Z

Issue + patch: https://clojure.atlassian.net/browse/CLJ-2586

alexmiller 2020-11-04T14:54:20.099300Z

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

ghadi 2020-11-04T14:55:33.099800Z

is this causing an issue in real world code?

borkdude 2020-11-04T14:56:11.100500Z

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

ghadi 2020-11-04T14:56:34.100900Z

because it's not costless to accept changes

ghadi 2020-11-04T14:56:52.101600Z

this is only called at macroexpansion time, right?

borkdude 2020-11-04T14:56:54.101700Z

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

borkdude 2020-11-04T14:57:15.102Z

mostly yes

borkdude 2020-11-04T14:57:34.102400Z

in sci I do call it at runtime

borkdude 2020-11-04T14:58:06.103Z

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

ghadi 2020-11-04T14:58:26.103500Z

please don't be sarcastic

ghadi 2020-11-04T14:59:28.104Z

core team works hard to manage issues

borkdude 2020-11-04T15:00:13.105400Z

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?

ghadi 2020-11-04T15:00:20.105800Z

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

borkdude 2020-11-04T15:00:21.106100Z

I would also have accepted: no

ghadi 2020-11-04T15:00:24.106200Z

(like last)

borkdude 2020-11-04T15:01:35.107Z

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

alexmiller 2020-11-04T15:04:20.107900Z

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

ghadi 2020-11-04T15:05:11.108300Z

you are misreading the ticket @alexmiller

ghadi 2020-11-04T15:05:41.109Z

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

ghadi 2020-11-04T15:06:02.109300Z

it's the destructuring macro helper itself

alexmiller 2020-11-04T15:07:17.109900Z

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

borkdude 2020-11-04T15:10:23.111400Z

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.

borkdude 2020-11-04T15:10:37.111600Z

I'll add this to the ticket.

borkdude 2020-11-04T15:18:38.112700Z

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

borkdude 2020-11-04T15:25:53.113200Z

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

ghadi 2020-11-04T15:26:35.114100Z

no worries.

ghadi 2020-11-04T15:26:46.114400Z

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

πŸ’œ 1
borkdude 2020-11-04T15:27:00.114800Z

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

ghadi 2020-11-04T15:27:43.115600Z

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

ghadi 2020-11-04T15:28:05.116Z

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

borkdude 2020-11-04T15:31:07.118200Z

gotcha

ghadi 2020-11-04T15:31:19.118600Z

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

ghadi 2020-11-04T15:31:46.119500Z

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

borkdude 2020-11-04T15:31:48.119700Z

what's indy?

ghadi 2020-11-04T15:31:52.119900Z

invokedynamic

borkdude 2020-11-04T15:33:51.120900Z

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.

borkdude 2020-11-04T15:34:25.121500Z

> 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.

2020-11-04T15:35:11.121900Z

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?

2020-11-05T22:18:15.158800Z

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

ghadi 2020-11-04T15:36:38.122Z

interesting, didn't realize https://github.com/oracle/graal/issues/2761 was in progress

borkdude 2020-11-04T15:37:55.122300Z

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

pez 2020-11-04T15:51:07.123700Z

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…

roklenarcic 2020-11-04T15:56:30.124200Z

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

roklenarcic 2020-11-04T15:57:17.124400Z

nested maps do require more memory than plain java objects

roklenarcic 2020-11-04T15:58:28.124600Z

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

2020-11-04T16:10:30.125Z

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.

ghadi 2020-11-04T16:16:16.125200Z

do you need to sign the xml?

ghadi 2020-11-04T16:16:30.125400Z

cryptographically

p-himik 2020-11-04T16:26:52.127100Z

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}))
=&gt; (1 1)

p-himik 2020-11-04T16:27:32.127200Z

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

2020-11-04T16:29:14.127400Z

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

borkdude 2020-11-04T16:33:24.128Z

and https://clojure.atlassian.net/browse/CLJ-1087

p-himik 2020-11-04T16:34:08.128200Z

Thanks!

2020-11-04T17:38:39.128400Z

No. I don't.

2020-11-04T17:43:46.128600Z

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.

borkdude 2020-11-04T18:46:08.129300Z

TIL:

user=&gt; (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#L58

πŸ˜† 1
borkdude 2020-11-04T18:46:57.129800Z

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

Darin Douglass 2020-11-04T18:50:22.130500Z

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

borkdude 2020-11-04T18:56:36.130800Z

'foo::bar
doesn't work though

bronsa 2020-11-04T18:56:54.131Z

one or more non repeating

borkdude 2020-11-04T18:57:19.131200Z

Argh, thanks :) Reading is hard.

3
solf 2020-11-04T19:37:15.131600Z

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.

solf 2020-11-04T19:45:04.131800Z

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.

dpsutton 2020-11-04T19:49:38.132100Z

best pun i've seen in a month

☝️ 1
pez 2020-11-04T20:28:51.132400Z

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?

solf 2020-11-04T22:08:59.133Z

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).

solf 2020-11-04T22:10:07.133200Z

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