clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
2021-01-14T00:03:40.252900Z

https://gist.github.com/hiredman/a68a2add9751eb8de3d2776363219e13 is an attempt to golf a swimm style cluster membership / failure detection core.async thing that uses wrapped timeouts

2021-01-14T00:06:25.253Z

cool, thanks! you always have the best core.async gists

ghadi 2021-01-14T00:10:20.253200Z

cooool

ghadi 2021-01-14T00:12:17.253400Z

the CML join function could be a protocol (that allowed you extend it to Process or CompletableFuture), and it would return you a channel.

ghadi 2021-01-14T00:13:34.253600Z

returns you a readport, technically

ghadi 2021-01-14T00:14:32.253800Z

CML is the Simpsons of concurrency, they did it all

πŸ’― 1
2021-01-14T00:14:47.254Z

I dunno if you've seen https://clojure.atlassian.net/browse/ASYNC-234 (which relates to ASYNC-225) but it turns out in some circumstances alts! can leak memory when used with something like a timeout channel, because there is a global reference to the timeout channel, and a callback that closes over data may be waiting on that channel even after an alts! selected a different channel

2021-01-14T00:15:07.254200Z

so you actually need nacks to not leak memory

ghadi 2021-01-14T00:15:26.254400Z

no, wow

2021-01-14T00:16:14.254600Z

restart REPL πŸ˜›

2021-01-14T00:17:05.254900Z

I do use refresh, but I also don't always trust it, so when I really want to be sure I eventually restart my REPL

2021-01-14T00:17:19.255100Z

Or I run an AOT compile

2021-01-14T00:20:08.255400Z

I mean, you just described (refresh)

ghadi 2021-01-14T00:20:22.255700Z

nice writeup

2021-01-14T00:20:52.255900Z

All it does is unmap namespaces and their vars, then reload them from source

2021-01-14T00:32:01.256100Z

it was a fun little puzzle. I was chatting with someone about ASYNC-225 which led me to the conclusion the leak must exist, and then a lot of digging trying to figure out if I didn't see a leak in a simple test case because I was wrong or because there was something else confounding things

seancorfield 2021-01-14T00:35:12.256400Z

It's a sledgehammer. And it tries to rebuild "everything". Which is why it can break stuff.

seancorfield 2021-01-14T00:35:52.256700Z

I occasionally (and surgically) remove and then load a single ns in the very rare situations where I need just that.

2021-01-14T00:37:01.256900Z

I don't know, its pretty clear in what it does. Remove all namespaces that were modified since the last time you run refresh and then load them again in dependency order. Or, remove all namespaces and reload them in dependency order.

seancorfield 2021-01-14T00:37:26.000100Z

I am very specifically advocating tight/narrow manual control over your REPL and eschewing "magic" that tries to do clever stuff / too much stuff.

seancorfield 2021-01-14T00:38:06.000300Z

And I usually don't have tools.namespace as a dependency πŸ™‚

2021-01-14T00:38:47.000500Z

I agree, but for this particular use case, I think refresh-all is the best tool. You've moved things around, you want to know, wait... could any of my code be using something that only exists in the REPL but not in my source files? Well, to know that, you need to remove all loaded namespaces, and reload your source files.

2021-01-14T00:39:46.000700Z

If I can agree on one thing, is I wish refresh was more stupid actually. The whole "trying to keep your state and clean up things" is where maybe it starts to mess up.

seancorfield 2021-01-14T00:40:02.000800Z

I disagree. Automated refresh is lazy and "magic" and breaks unexpectedly for a lot of people -- witness all the beginners here on Slack who trip over problems with it. We should teach better hygiene instead of just calling in a mob of cleanup crew folks.

2021-01-14T00:41:23.000900Z

I'd need to review that, I've never had it break unexpectedly on me, I suspect the only unexpected thing is that their new code is broken, but their old code worked

2021-01-14T00:43:48.001Z

Like as soon as you want to check that your REPL state doesn't contain things that's missing from your source files, you've entered a difficult place. Its pretty easy to not remember what you changed where, and lose track of what you should remove-ns and load again to be sure. That's when I'll use refresh, as a faster way than restarting the REPL.

seancorfield 2021-01-14T01:01:55.001500Z

Like I said: good hygiene, evaluate every change, think carefully about your refactoring. If you're moving functions between namespaces, you're changing the "public API" of those namespaces, so that should already give you pause. I'm not saying "don't refactor" -- I'm saying be methodical, be careful, take the smallest steps possible, and keep your REPL in sync at every step (if possible). Needing to "refresh" a namespace should be rare and it is easy to do manually -- no library needed. Needing to "refresh everything" means you screwed up somewhere in your process and need a better process. Having bad work practices and just papering over them with a "nuclear" library is the wrong approach (IMO).

seancorfield 2021-01-14T01:04:06.001700Z

(and also like I have said several times: my REPL runs for days, or weeks without restarts so... πŸ™‚ )

2021-01-14T01:09:10.001900Z

I think I agree with you mostly, except that, once you've decided to rename something, or move a function from one namespace to another. Keeping that hygiene is pretty hard. Should you try to avoid renaming and moving things around for fun, sure. But when you decide to do it, you do have an issue. What if you forgot to update a reference? Ok, clj-kondo does help a lot, but that's new stuff πŸ˜› We didn't always have it before. Even then, clj-kondo won't work across namespaces. So it can't guarantee you didn't forget to update a reference.

seancorfield 2021-01-14T01:10:41.002100Z

clj-kondo does work across namespaces because it reparses and caches information about each ns as you make changes.

2021-01-14T01:12:10.002300Z

I kinda of wasn't sure, but I just tried it, and it did not

seancorfield 2021-01-14T01:12:30.002500Z

When I've updated a function arity at the definition and I switch to another ns, it highlights the now-incorrect arity in calls.

2021-01-14T01:12:54.002800Z

Arity maybe, let me try that. It didn't for rename

seancorfield 2021-01-14T01:13:50.003Z

Well, this has been fascinating in parts but I have cats to attend to so I'm off (and I am going to unsub from this thread as it seems to have run its course at this point).

2021-01-14T01:13:50.003200Z

Also in general, it seems if I do:

(ns foo
  (:require [bar :as b]))

(b/this-does-not-exist-in-bar)
It doesn't highlight it

2021-01-14T01:14:12.003400Z

Fair enough, have a nice evening

beders 2021-01-14T04:38:09.007100Z

Heroku is ideal for that. Even has some clojure examples

jumar 2021-01-14T04:54:51.008600Z

I'm confused about these results - shouldn't I get a primitive array? It looks like an array of objects

(into-array Integer/TYPE [1 2 3])
;; => #object["[I" 0x39b658f6 "[I@39b658f6"]
(int-array [1 2 3])
;; => #object["[I" 0x3d5bb38f "[I@3d5bb38f"]
(in Joy of Clojure they show something like #<int[] ... as an output, at least for the first one)

2021-01-14T14:34:32.052800Z

You can write functions in Clojure with (I think) up to at most 4 arguments that take a mix of Object, and long and double primitives, and return Object, or a long or double primitive, using type hints, but I don't recall are any built-in Clojure functions that do this.

2021-01-14T14:34:53.053Z

And that technique works only for those primitive types, no others.

phronmophobic 2021-01-14T05:02:53.009300Z

I think the "[I" indicates that it is an array of ints

seancorfield 2021-01-14T05:03:04.009600Z

@jumar `Integer` is an object. Irrelevant: Integer/TYPE is int which actually surprised me (I've been doing Clojure too long!)

jumar 2021-01-14T05:03:05.009700Z

Ah, so this is interesting:

(type (to-array [1 2 3]))
;; => [Ljava.lang.Object;
(type (into-array [1 2 3]))
;; => [Ljava.lang.Long;
(type (into-array Integer/TYPE [1 2 3]))
;; => [I
(type (int-array [1 2 3]))
;; => [I
(type (aget (int-array [1 2 3])
            0))
;; => java.lang.Integer

jumar 2021-01-14T05:03:28.010300Z

Yeah, I think it makes sense - see more examples in the thread

πŸ‘ 1
jumar 2021-01-14T05:03:48.011Z

the last one with aget - is that always an object?

seancorfield 2021-01-14T05:03:54.011300Z

Just saw that, yes. And the printed representation of objects has changed since JoC.

πŸ‘ 1
phronmophobic 2021-01-14T05:05:35.011500Z

I don't think so:

> (type (int 1))
java.lang.Integer

phronmophobic 2021-01-14T05:06:25.011800Z

> (identical? (int 1) (Integer. 1))
false
> (identical? (int 1) (int 1))
true

phronmophobic 2021-01-14T05:07:01.012100Z

I'm not sure how test for boxed Integer vs int

seancorfield 2021-01-14T05:08:12.012300Z

user=> (let [is (into-array Integer [1 2 3])] (class is))
Execution error (IllegalArgumentException) at java.lang.reflect.Array/set (Array.java:-2).
array element type mismatch
user=> (let [is (into-array Integer [(int 1) (int 2) (int 3)])] (class is))
[Ljava.lang.Integer;
user=>
Just to confirm that [I is an array of int

seancorfield 2021-01-14T05:09:22.012500Z

As for identical? I believe Java caches small values of numbers so (int 1) is always the same object, but (Integer. 1) is going to construct a new instance (with the same value) but a unique identity?

πŸ‘ 2
phronmophobic 2021-01-14T05:10:57.012700Z

the docs say aget /`aset` are overloaded for arrays of primitives, https://clojure.org/reference/java_interop#primitives

phronmophobic 2021-01-14T05:12:12.013Z

regarding identical?, it looks like it just uses the java == operator, https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Util.java#L134

phronmophobic 2021-01-14T05:13:22.013300Z

which I think is value equality for primitives like int

seancorfield 2021-01-14T05:14:26.013500Z

user=> (take 1 (drop-while #(identical? (int %) (int %)) (range)))
(128)

seancorfield 2021-01-14T05:14:37.013700Z

So the first 128 (0..127) are cached.

seancorfield 2021-01-14T05:15:25.013900Z

This is true for byte and long as well.

2021-01-14T05:15:43.014100Z

I think working with primitives is very tricky, every time arguments are passed to a function, primitives are boxed again. So I wonder if the call to type will box the return of aget

seancorfield 2021-01-14T05:17:47.014300Z

See https://insideclojure.org/2014/12/15/warn-on-boxed/

phronmophobic 2021-01-14T05:20:09.014600Z

ohhh, so https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Util.java#L134 is using the == operator, but only after converting the ints to Integers?

phronmophobic 2021-01-14T05:20:35.014800Z

because the method signature is identical(Object k1, Object k2)

phronmophobic 2021-01-14T05:20:50.015Z

rather than also having identical(int k1, int k2)

2021-01-14T05:22:11.015200Z

Ya, identical also boxes, all function by default will box their arguments and return value, unless they are type hinted as long or double (no other primitive type hint will work to remove the boxing)

πŸ‘ 1
2021-01-14T05:22:31.015400Z

Unless they also have an overload in Java, like aget does

seancorfield 2021-01-14T05:23:37.015600Z

I don't think type can actually return a primitive type, right?

2021-01-14T05:24:29.015900Z

I guess it couldn't, since it probably can't actually be passed one as argument

jumar 2021-01-14T05:25:10.016100Z

(a great discussion, thanks!)

2021-01-14T05:26:06.016300Z

And like that article sean posted says: > You probably noticed we didn’t see the boxing occurring on the way in/out of the function. That’s not actually boxed math, just function invocation, so it’s a little harder to detect and warn on automatically Which is why its extra tricky, cause :warn-on-boxed won't show you if you're boxing by accident through function application

2021-01-14T05:27:22.016500Z

But also, in a primitive context, you can't use any of Clojure core, except for arithmetics and the array functions, everything else will box again.

phronmophobic 2021-01-14T05:28:34.016800Z

also, referencing @seancorfield’s comment about small Integers being cached. you can use that to make 1+1=3, https://pedrorijo.com/blog/java-integer-cache/

seancorfield 2021-01-14T05:29:08.017100Z

Hahaha... πŸ™‚

😈 2
2021-01-14T05:33:01.017900Z

Is it possible to walk a lazy-seq without creating intermediate data?

2021-01-14T05:36:04.018900Z

Say I know I'll consume it all, and just want to sum the numbers in it.

2021-01-14T05:38:47.020900Z

It depends on what you been by "lazy-seq" and "intermediate data"

2021-01-14T05:40:23.024Z

Walking a lazy seq will realize any unrealized bits, and if a lazy seq is lazily built on top of another lazy seq (like with map) then it needs to realize that lazy seq as well

2021-01-14T05:40:30.024200Z

Ya, I'm a little fuzzy on that myself. But basically, I know some of the overhead of walking a lazy-seq is that each next that is beyond the chunk size, we will create a new lazy-seq no?

2021-01-14T05:45:04.027600Z

Transducers and CollReduce are the way to pipeline operations without creating intermediate stuff between the pipeline steps

2021-01-14T05:45:52.028200Z

Those would assume I am starting with a collection though right? But say I have the result of (map inc [1 2 3])

2021-01-14T05:47:48.029400Z

Imagine I had like 100 elements in there. Now if I understand, traversing that will create intermediate representation every chunk right? And there's no way around it?

2021-01-14T05:55:04.030Z

They do not assume that

2021-01-14T05:57:21.033300Z

For example range has a very complicated implementation of ReduceInit (the java interface version of CollReduce) which turns reducing into a loop with a counter

2021-01-14T05:59:38.034800Z

It has a complicated implementation, the ReduceInit impl is pretty simple

2021-01-14T06:01:32.035500Z

Hum, so its possible that reducing over a lazy-seq will take a faster route? Like in the case of range?

2021-01-14T06:02:25.035800Z

Even when its wrapped like above in a call to map?

2021-01-14T06:02:50.036400Z

Range can return a complicated object

2021-01-14T06:03:42.037700Z

One that is a seq, but also has a separate code path for optimized reducing

2021-01-14T06:04:40.039200Z

So no, if you operate on it like a seq, you get it as a seq, if you use transducers you get the optimized reduce path

2021-01-14T06:06:22.040900Z

Clojure's reducing operations are defined over a number of different interfaces and types, it actually does a lot to try and avoid bottoming out at reducing over a sequence

2021-01-14T06:06:58.041900Z

You can implement ReduceInit or CollReduce and never bother with ISeq and be reducible

2021-01-14T06:07:29.042500Z

I think next.jdbc does that for things like result sets

seancorfield 2021-01-14T06:23:58.043900Z

Yes, next.jdbc avoids all of the overhead of creating hash maps etc if you can reduce rows to simple values -- and the reduction automatically manages the opening and closing of the connection.

seancorfield 2021-01-14T06:25:21.045300Z

If a "collection" (conceptually) knows how to reduce itself efficiently, via ReduceInit then reduce will go down that path, else it'll use CollReduce which will ultimately fall back to walking the sequence if no faster approach is defined.

2021-01-14T06:41:29.047900Z

https://github.com/clojure/clojure/blob/master/src/clj/clojure/core/protocols.clj#L75 is where CollReduce is extended to different types/interfaces to provide faster reducing then walking seqs, Iterable for instance

2021-01-14T06:58:33.048800Z

Ok, thanks, I'll have a look at this. If I follow though... (map inc coll) would not be returning something that has a faster path for reducing correct?

2021-01-14T06:59:21.049700Z

It would need to be (eduction (map inc) coll) or something that uses transducers instead, or a direct call to reduce with a collection type that can be fastly reduced?

2021-01-14T07:07:25.050900Z

Correct, reduce is the fast path, the way to build transformation pipelines with reduce is transducers

2021-01-14T07:08:56.051400Z

Sounds good, thanks

2021-01-14T14:38:26.054200Z

@didibus If you would like to see the JVM objects returned by various function calls, to see what lazy seqs look like in memory, there are a couple of examples in this article using my cljol library: https://github.com/jafingerhut/cljol/blob/master/doc/README-gallery.md

πŸ‘€ 1
Steven Katz 2021-01-14T14:50:41.056300Z

Question about PersistentHashMap. If I the map contain kvpair1 and I assoc in the same pair does the amount of memory the map consumes change or is assoc smart enough to know that no change is actually needed?

Steven Katz 2021-01-14T14:52:21.058Z

(def foo {:a 1 :k2})
(assoc foo :a 1)
is the underlying storage change in any way?

bronsa 2021-01-14T14:56:15.058500Z

the current impl returns the original map if both k and v are already present

bronsa 2021-01-14T14:56:20.058800Z

but it's an impl detail

bronsa 2021-01-14T14:56:29.059Z

not a guarantee

Steven Katz 2021-01-14T15:13:18.059600Z

@bronsa Thanks!!

kiranshila 2021-01-14T16:32:57.060900Z

What is everyone using nowadays for validating ECDSA sigs

GGfpc 2021-01-14T19:19:02.062400Z

Do operations over lazy sequences in clojure work the same as in kotlin where each element goes through the whole "pipeline" at once, instead of each function being applied to the whole data in sequence?

2021-01-14T19:29:46.062600Z

Yes, but, I don't have enough Kotlin experience to know for sure what they do, I suspect that Kotlin is not lazy, and does something more like Java streams. So lazy-seq would not be the same. Transducers are more likely to be similar to what Kotlin does I'd suspect

2021-01-14T19:30:49.062800Z

That said, lazy seq will not go through the whole data in sequence, each element is pulled and then goes through the whole pipeline of transformation, and then the next element is pulled, etc.

GGfpc 2021-01-14T19:31:46.063100Z

Awesome, that's exactly what I wanted to know, thanks!

2021-01-14T19:33:35.063300Z

Hum, a quick read of the Kotlin doc on sequences, so maybe they are much more like Clojure sequences.

2021-01-14T19:33:41.063500Z

And not like Java streams

2021-01-14T19:36:24.063700Z

one difference might be with chunking - clojure has chunked collections which are evaluatred N elements (often 32) at a time, rather than one by one

πŸ‘ 1
2021-01-14T19:36:43.063900Z

this won't matter if you don't use laziness for side effects (don't use laziness for side effects)

2021-01-14T19:39:31.064100Z

Anyways, I'm not sure on the Kotlin side, but I'm sure on the Clojure side πŸ˜› From Clojure, think of it as "pulling". When you define a pipeline:

(->> [1 2 3]
  (map inc)
  (filter odd?)
  (map #(* 2 %)))
You are defining the transforms you want applied when someone requests the next element. So I'd go and say, please give me the (first ...) element. At which point, each step buble up their request of getting the first from the previous step until you reach the collection, so the collection will return the first, then (map inc) will increment it and pass it to (filter odd?). Now (filter odd?) will see that the element is 2 and it is not odd?, so this is not the first odd? element. So filter will now ask the previous step for the second element, so (map inc) will ask the coll for the second, gets 2, increments it to 3, passes it to (filter odd?) which now see yup this is the first odd element, so it passes 3 to (map #(* 2 %)) which will double it and return it to you.

2021-01-14T19:43:33.064400Z

And like noisesmith said, there's a small caveat in that because Clojure support ChunkedSeq as well (not all lazy-seq are chunked but most are). Well when you have a ChunkedSeq you can't ask for the next element, you can only ask for the next chunk. So the default chunk size is 32 it means even if you say please give me (first ...) you're actually saying please give me the first result of the first chunk. Which means 32 elements will go through the pipeline, but not one at a time, it'll be 32 go through the first step fully, and then 32 go through the second step, etc.

2021-01-14T19:44:40.064600Z

This is a performance optimization, but it also means if you really want to be one at a time lazy, you have to be careful you're not using ChunkedSeq.

2021-01-14T19:45:12.064800Z

\end-wall-of-text

2021-01-14T19:47:51.065200Z

Ok, the Kotlin docs might put the Clojure docs to shame: https://kotlinlang.org/docs/reference/sequences.html

2021-01-14T19:47:58.065500Z

Damn 😞

2021-01-14T19:48:43.065700Z

> you can't ask for the next element, you can only ask for the next chunk you can ask for one item, but a chunk at a time is realized and cached on the implementation side

πŸ’― 1
2021-01-14T19:49:11.065900Z

> you're actually saying please give me the first result of the first chunk

2021-01-14T19:51:05.066300Z

I'm talking specifically of their infographics

2021-01-14T19:52:45.067100Z

Also, I might wager that Kotlin was inspired directly by Clojure for this feature, I mean all the names are the same, Sequence, filter, map, take, etc.

2021-01-14T19:58:56.067300Z

@ggfpc12495 Ya so the infographic at the end of the Kotlin doc https://kotlinlang.org/docs/reference/sequences.html this is exactly how the Clojure lazy sequence work when not chunked.

2021-01-14T20:00:45.068300Z

Would be nice if we made a similar infographic to explain lazy-seq, chunkedseq, transducers, etc... Hum, add it to my backlog of todos that I never get too πŸ˜›

alexmiller 2021-01-14T20:09:33.068700Z

well the model is way more complicated once you factor in chunking and transients

alexmiller 2021-01-14T20:11:06.070Z

but filing an issue on https://github.com/clojure/clojure-site/issues with concrete requests is a great step. we have resources to do this kind of work when time allows (more skillful people than me :)

quoll 2021-01-14T20:38:53.074500Z

I have a question about the path set by deps.edn. Our deps.edn file includes:

:test
  {:extra-paths ["test"]
   :extra-deps {com.cognitect/test-runner {:git/url "<https://github.com/cognitect-labs/test-runner.git>"
                                           :sha "209b64504cb3bd3b99ecfec7937b358a879f55c1"}}
   :main-opts ["-m" "cognitect.test-runner"]}
However, it’s not getting included when running: clj -M:dev:test I keep getting:
Execution error (FileNotFoundException) at clojure.main/main (main.java:40).
Could not locate cognitect/test_runner__init.class, cognitect/test_runner.clj or cognitect/test_runner.cljc on classpath. Please check that namespaces with dashes use underscores in the Clojure file name.
When I look at my path with clj -Spath -M:dev:test then I don’t see anything appropriate. My colleagues execute this and their paths include:
.gitlibs/libs/com.cognitect/test-runner/209b64504cb3bd3b99ecfec7937b358a879f55c1/src
But I don’t have this in my path, and I don’t even have a .gitlibs directory. Any suggestions for where I can track this down please?

seancorfield 2021-01-14T20:40:36.074900Z

Does clojure -Sforce -M:dev:test solve the problem?

quoll 2021-01-14T20:41:02.075100Z

Just tried… and no

seancorfield 2021-01-14T20:41:29.075600Z

clojure -Sdescribe -- what version of the CLI are you running?

seancorfield 2021-01-14T20:42:01.076300Z

I suspect you're using an older version where -M doesn't bring in dependencies (it used to just run :main-opts).

☝️ 1
quoll 2021-01-14T20:42:13.076400Z

{:version "1.10.1.492"
 :config-files ["/usr/local/Cellar/clojure/1.10.1.492/deps.edn" "/Users/pgearon/.clojure/deps.edn" "deps.edn" ]
 :config-user "/Users/pgearon/.clojure/deps.edn"
 :config-project "deps.edn"
 :install-dir "/usr/local/Cellar/clojure/1.10.1.492"
 :config-dir "/Users/pgearon/.clojure"
 :cache-dir ".cpcache"
 :force false
 :repro false
 :resolve-aliases ""
 :classpath-aliases ""
 :jvm-aliases ""
 :main-aliases ""
 :all-aliases ""}

seancorfield 2021-01-14T20:42:29.076600Z

Yup, that's too old for the new -M behavior.

alexmiller 2021-01-14T20:42:30.076800Z

that's really old

quoll 2021-01-14T20:42:50.077100Z

ah, thank you

seancorfield 2021-01-14T20:42:52.077300Z

1.10.1.763 is current. A lot of changes happened after 1.10.1.536

quoll 2021-01-14T20:43:21.077500Z

I’ve only been paying attention to the 1.10.1

quoll 2021-01-14T20:44:33.077700Z

Yup, pulling it in now. Thank you!

seancorfield 2021-01-14T20:44:48.077900Z

https://clojure.org/releases/tools lists all the changes.

quoll 2021-01-14T20:49:58.078200Z

While it’s trying to pull it in now, it’s failing to do so:

Cloning: <https://github.com/cognitect-labs/test-runner.git>
Error building classpath. Destination path "test-runner" already exists and is not an empty directory
I can’t find a directory by this name anywhere. Could it be a different directory that I should be trying to clean out?

alexmiller 2021-01-14T20:50:34.078400Z

I suspect that's in ~/.gitlibs

quoll 2021-01-14T20:50:52.078600Z

thank you!

alexmiller 2021-01-14T20:51:11.078800Z

either you had a failed thing there that's messing with it or you are encountering a known race condition with downloading gitlibs

alexmiller 2021-01-14T20:51:22.079Z

adding -Sthreads 1 avoids the latter for the moment

quoll 2021-01-14T20:54:07.079200Z

OK, I’ve hurt myself here. I blew away the ~/.gitlibs directory, but now it says:

Cloning: <https://github.com/cognitect-labs/test-runner.git>
Error building classpath. git@github.com:cognitect-labs/test-runner.git: USERAUTH fail

quoll 2021-01-14T20:55:38.079400Z

maybe it’s my 2FA on github

alexmiller 2021-01-14T20:57:35.079600Z

it's not finding your user auth. 2fa isn't specifically an issue

alexmiller 2021-01-14T20:58:10.079800Z

https://clojure.org/reference/deps_and_cli#_git has some further tips

quoll 2021-01-14T20:58:16.080Z

Thank you

alexmiller 2021-01-14T20:58:35.080200Z

first, check

ssh-add -l
to ensure it's finding your identity in the agent

alexmiller 2021-01-14T20:58:58.080400Z

then most thing wrong is in ~/.ssh/config

alexmiller 2021-01-14T20:59:27.080600Z

using IdentityFile ~/.ssh/id_rsa there is usually problematic - try commenting that out (with #)

alexmiller 2021-01-14T21:00:14.080800Z

hang on, I think it's probably something else

alexmiller 2021-01-14T21:00:24.081Z

you're using https (public) but it's trying git (private)

alexmiller 2021-01-14T21:00:48.081200Z

are you on CI or local?

quoll 2021-01-14T21:01:17.081400Z

sorry, I don’t follow that question

quoll 2021-01-14T21:01:29.081600Z

oh, continuous integration

quoll 2021-01-14T21:01:33.081800Z

local

quoll 2021-01-14T21:01:38.082Z

this is all my local command line

alexmiller 2021-01-14T21:02:22.082200Z

do you have a ~/.gitconfig with a insteadOf directive?

alexmiller 2021-01-14T21:02:47.082400Z

enabled with something like git config --global url."git@github.com:".insteadOf <https://github.com/>

alexmiller 2021-01-14T21:03:34.082700Z

(don't run that, but that's how it would be enabled)

alexmiller 2021-01-14T21:03:50.082900Z

grep for insteadOf in ~/.gitconfig

alexmiller 2021-01-14T21:04:07.083100Z

could also be set on a per-project basis

quoll 2021-01-14T21:04:15.083300Z

ah, yes, I do. Last set up some years ago, so I forgot it

alexmiller 2021-01-14T21:04:34.083500Z

yeah, that's forcing it to try to use your ssh key

quoll 2021-01-14T21:05:13.083700Z

Finally got it. Thank you!

quoll 2021-01-14T21:05:19.083900Z

(actually running the tests now)

alexmiller 2021-01-14T21:05:49.084100Z

cool

quoll 2021-01-14T21:06:25.084300Z

appreciate it all. That was a lot πŸ™‚

vemv 2021-01-14T23:04:06.087600Z

Anyone know how to create a middleware-like pattern using Jetty Servlets? I gave a go to HandlerWrapper and HandlerCollection, but they are a bit different from what I expected: when the original (i.e. wrapped) handler completes, the HTTP connection is succesfully terminated with its result. Whereas I'm seeking something more similar to Ring middleware, where a given member can alter the previous member's body/status/headers prior to HTTP termination.

2021-01-14T23:21:24.088200Z

that isn't actually how ring middle works

2021-01-14T23:21:57.088700Z

it looks like it because of the commonly used idioms for wrapping handlers in middleware

2021-01-14T23:23:19.089700Z

but a ring middleware is (fn [f] (fn [req] (f req)))

2021-01-14T23:24:04.090400Z

given a handler, return a new handler that applies the given handler to a request

2021-01-14T23:28:17.093500Z

it has been a long time since I've used servlets, I think the equivalent for them is something like a servlet that takes another "inner" servlet when constructed, and then whenever its doGet or whatever method is called, fiddles with the parameters it gets, maybe replacing some streams with bytearrayinputstreams or whatever, and then passes those parameters to the "inner" servlet, possibly waiting around for it to complete, then examining the baos and doing whatever

vemv 2021-01-14T23:37:04.097Z

I see. Sounds very fiddly indeed :) if this kind of transparent composition isn't encapsulated under a handy technique or class like HandlerWrapper/HandlerCollection I'm not sure I'd want to give it a shot. Closest thing I found (just now) is HttpOutput.Interceptor. In principle it only alters bodies, although maybe status/headers will remain mutable using usual methods

wilkerlucio 2021-01-14T23:40:53.099700Z

hello, one question on performance, I'm trying to make my custom version of reduce-kv, before adding my things to the process I'm trying to figure what is the fastest way to iterate on a map, other than reduce-kv, I ran a few experiments, the fastest option I found out was using the map iterator directly, but that still 1.2x slower than reduce-kv, is there a simple way to loop on a map that's closer in performance to reduce-kv?

wilkerlucio 2021-01-14T23:41:10.099800Z

this my iterator based implementation:

wilkerlucio 2021-01-14T23:41:11.100Z

(defn kv-loop-iterator [f init coll]
  (let [it (clojure.lang.RT/iter coll)]
    (loop [out init]
      (if (.hasNext it)
        (let [entry (.next it)
              k     (key entry)
              v     (val entry)]
          (recur (f out k v)))
        out))))

wilkerlucio 2021-01-14T23:41:25.100200Z

bench results (also add some other things I tried):

wilkerlucio 2021-01-14T23:41:27.100400Z

| title                   | mean          | bar                  | variance-str |
|-------------------------+---------------+----------------------+--------------|
| reduce-kv-native        | 136.966296 ns | β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–               | 0.000x       |
| reduce-kv-loop-iterator | 301.646678 ns | β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–         | 1.202x       |
| reduce-kv-simple-reduce | 412.819192 ns | β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‹     | 2.014x       |
| reduce-kv-loop-seq      | 530.523741 ns | β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ | 2.873x       |

Oliver George 2021-01-14T23:45:00.101100Z

On the clojure survey, what does "namespaces" relate to in Q22. Rate the priority of making improvements to Clojure in these areas.

seancorfield 2021-01-14T23:48:20.102200Z

I took it as meaning "all things related to namespaces" -- so if there's anything around how we write/use namespaces, mark how important that is for you, and then add a specific comment in the free text box on the last page.

wilkerlucio 2021-01-14T23:49:11.102700Z

I took the same as @seancorfield, one example that comes to my mind is this issue: https://clojure.atlassian.net/browse/CLJ-2123

Oliver George 2021-01-14T23:53:01.102900Z

Thanks