Fork me on GitHub
Adrian Smith00:05:48

seems to work no problem did a refactor to use clj-new and the app survived 😄


Do Clojure future or promise provide an API for listening for completion/failure asynchronously?


@nicholas.jaunsen No, but if you're using Clojure on the JVM you can use Java's CompletableFuture for that I suspect.


(and in ClojureScript, I think you can interact with JS promises? I don't do cljs tho')


At work we have some macros that make it easier to use -- see the examples at the end of the README

Sanghyun Lee20:05:53

Hello, I’m trying to use ClojureScript with quil, rein, and figwheel. I’m running repl and try to run some function but it throws an error that I don’t understand. Can someone explain why this is happening?

Sanghyun Lee21:05:34

Got an answer It turned out that quil APIs are not very repl friendly.

Timur Latypoff20:05:39

Hi guys, really need your insight, my brain got stuck on this basic thing.

(->> collection (map :optional-param) (filter some?) (apply min))
I'd love to find the minimum of :optional-params in the collection, or nil if no such non-nil values exist. The code above does not work when collection does not have such elements. Is there an idiomatic and beautiful way of doing it without breaking the beautiful thread and without defining an "extended" min with 0 arity that returns nil?

Timur Latypoff21:05:10

Ok, found that I can simplify the form above to a very cool

(->> collection (keep :optional-param) (apply min))
Didn't yet find elegant short-circuiting of empty seqs.


... seq (#(or % '(nil))) (apply min) will do the trick, but you ain't gonna win any beauty contests with it


or, you know, you could just (some->> collection (keep :optional-param) seq (apply min))


Yup, some-> and some->> are great for short-circuiting threading pipelines.


Nice trick with seq there too!


I don't love that it does unneeded nil checks, though


(seq (keep :whatever collection)) would avoid that, but then it just looks less "pretty" 🙂


(some->> (seq (keep :whatever collection)) (apply min))


I think that's reasonable.


On the other other hand, a sufficiently smart compier ™️ would know that keep can't return nil , so that check could be optimized away 🙂


clojure's compiler has no intention of ever being smart


or, to be less glib, the designers of the clojure compiler explcitly aren't trying to be clever, and they will rearrange things as they are found to be bottlenecks but prefer better internal design to cleverness in the compiler implementation. if extra nil checks inside a lazy-seq pipeline is your bottleneck there's a lot you can do to improve speed before the compiler needs to get complicated


it seems like a relatively low hanging fruit in compile time optimizations though, just some type inference and dead branch elimination


anybody in any thread can redefine keep at any time


so as a static check, it doesn't fit clojure's compilation model, and as a runtime check the extra logic would just make it harder for hotspot to do its job


(and would be more expensive than the original nil check, which is very low overhead)


inlining a happy paths + a guard for redefinition / slow path seems like a pretty usual JIT approach, I'm not convinced it can't be beneficial here


we have a JIT, it's called hotspot, adding your own JIT logic makes hotspot less efficient


I mean, you're welcome to try this sort of thing, of course, the project is open source, but RH has been explicitly averse to these sorts of changes


I'm absolutely not saying you should add your own, but isn't hotspot smart enough to do things like that sometimes?


I mean, it knows the return type and it knows it's a nil check


it can, and that sort of thing can be done for you if you keep your bytecode simple enough


it knows the return type of the invoke method of a specific object, we call that object keep, to the bytecode / vm it's a specific class in clojure.core with an invoke method. And yes, hotspot can and will optimize that method if it has bytecode it knows how to improve.


@UTQEPUEH4 I think I misunderstood your point, apologies


no problems, I absolutely did miss that thing about redefs


I assumed redefs don't affect "already compiled" stuff, and had to do a quick experiment in repl to see that it's indeed wrong


there's a trick where you can use a binding context (function arg, let block), the right hand side will see redefs (it's attached to the var), but the lhs, the new binding, is captured and isn't resolved again


it's more often you see the "counter-trick" to that trick, of using the #'var quoting of the var to ensure the new binding sees your redefs :D


yeah, the #'var thing is why I assumed that


so, thanks for the explanation 🙂