This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-01-27
Channels
- # announcements (4)
- # asami (6)
- # aws-lambda (1)
- # babashka (38)
- # babashka-sci-dev (20)
- # beginners (87)
- # calva (67)
- # cider (19)
- # clerk (13)
- # clojure (102)
- # clojure-europe (52)
- # clojure-filipino (1)
- # clojure-hungary (4)
- # clojure-nl (1)
- # clojure-norway (6)
- # clojure-sweden (3)
- # clojure-uk (1)
- # cursive (13)
- # data-science (7)
- # datomic (8)
- # deps-new (1)
- # emacs (3)
- # fulcro (16)
- # graphql (3)
- # humbleui (3)
- # kaocha (3)
- # leiningen (3)
- # malli (3)
- # off-topic (14)
- # pathom (34)
- # polylith (4)
- # rdf (12)
- # reitit (3)
- # releases (1)
- # remote-jobs (7)
- # rum (2)
- # sci (22)
- # shadow-cljs (115)
- # tools-deps (26)
- # tree-sitter (29)
Why is the former faster than the latter?
(require '[criterium.core :as c])
(def data (vec (range 100)))
(c/report-result
(c/benchmark
(into [] (comp (filter even?) (map inc)) data) {}))
;; Evaluation count : 54365280 in 60 samples of 906088 calls.
;; Execution time mean : 1.112239 µs
;; Execution time std-deviation : 10.197468 ns
;; Execution time lower quantile : 1.099641 µs ( 2.5%)
;; Execution time upper quantile : 1.144325 µs (97.5%)
;; Overhead used : 2.033354 ns
;; Found 5 outliers in 60 samples (8.3333 %)
;; low-severe 1 (1.6667 %)
;; low-mild 1 (1.6667 %)
;; high-mild 3 (5.0000 %)
;; Variance from outliers : 1.6389 % Variance is slightly inflated by outliers
(c/report-result
(c/benchmark
(into [] (comp (map inc) (filter even?)) data) {}))
;; Evaluation count : 39379800 in 60 samples of 656330 calls.
;; Execution time mean : 1.537396 µs
;; Execution time std-deviation : 12.310657 ns
;; Execution time lower quantile : 1.515765 µs ( 2.5%)
;; Execution time upper quantile : 1.568869 µs (97.5%)
;; Overhead used : 2.033354 ns ;;
;; Found 3 outliers in 60 samples (5.0000 %)
;; low-severe 3 (5.0000 %)
;; Variance from outliers : 1.6389 % Variance is slightly inflated by outliers
In the first one, you only increment the even ones. In the second one you increment everything and then filter out the even ones. The two shouldn't even have the same result by the way, so it's not equivalent logic. If you increment first, you make the odds even and then remove the evens, leaving you with odds. On the other you remove the evens and increment the odds, leaving you with only evens.
(into []
(comp
(map inc)
(filter even?))
[1 2 3 4])
> [2 4]
(into []
(comp
(filter even?)
(map inc))
[1 2 3 4])
> [3 5]
@U0K064KQV @U2FRKM4TW I think I’m misunderstanding something. The doc for comp says it applies to the right most function when passes the result to the left. If that’s true, the first example will inc first then filter, the second will filter then inc. if that’s true then the second example will inc half the number of the first, so it should be faster, no?
In this case, you're comp
ing not inc
and even?
but rather (map inc)
and (filter even?)
, which are transducers.
It might not be obvious at the first glance why it's so, but it becomes rather clear when you learn more about transducers.
Here's a documentation section on comp
with transducers specifically: https://clojure.org/reference/transducers#_defining_transformations_with_transducers
Ya, when using comp for transducers, the order becomes left to right. Comp does compose right to left, but with transducer it ends up applying them left or right.
can i rely on this for "normal" clojure maps regardless of size?
(= (zipmap (keys m) (vals m)) m)
Consistent ordering is promised by the docs of keys and vals: https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/keys
Why is it that if you create an ex-info
with a nil
ex-data field, an IllegalArgumentException
is thrown? I can use nil
(nearly) everywhere else, but an ex-data-map is not valid with nil
?
Additionally, the doc-string tells us nothing about this behaviour, which I find quite puzzling.
> that carries a map of additional data. nil is not a map
Ok, but (ex-data (Exception. "foo"))
is nil
. Subsequently, you would need to introduce an instance check for ExceptionInfo
or an (or (ex-data e) {})
"catch". I think that is bad reasoning, tbh. If you provide nil
data, the intention is clear: No additional data can be provided. An IllegalArgumentException
makes no sense to me.
Not a lengthy explanation but an explanation, in the original ticket: https://clojure.atlassian.net/browse/CLJ-733?focusedCommentId=18813
I understand that there was an uncertainty to whether nil should be a valid argument, yet this wasn't picked up and discussed, because of the initial doc-string, which still does not explicitly tell you that it throws an IllegalArgumentException in that rather possible "intuitively correct" case. I do understand the reasoning to why ex-data
should return nil, but it isn't really coherent with the behaviour of non-existent exception data in java exceptions and an illegal argument of ex-info
There is a newer ticket about this (can’t easily search on my phone to find it) and I am hoping to fix it in 1.12. I agree with you that this is not good (and it’s entirely too easy to write error handling code that fails at runtime when there’s an error).
Seems like this is the one: https://clojure.atlassian.net/browse/CLJ-2640 > ex-info should be tolerant of nil data param
What about adding a single-arity variant of ex-info
that would simply use empty map (or nil) as the second argument?
That would still make things like
(let [data (something-that-can-return-nil)]
(throw (ex-info "I might never be thrown" data)))
possible.Oh yeah; I was just wondering some many times why ex-info doesn't accept a single, msg, argument 🙂
there's more work on this elsewhere that isn't visible here
I'm looking for something that acts as a promise, but lets me call a function on deref
/`realized?` (to see if done yet). Does this exist or should I just go ahead and write it myself?
(realized? a-promise)
works right? if i understand your requirement correctly.
I think this could work with a one-element lazy-seq
. Once you consume it, the task is spawned
@U02PS2BNULE hmm no, I want to perform the check each time it's checked/deref:ed until "done"
@U7ERLH6JX hmm no, I want something like
(def p (promise (fn [] (println "checking...") (are-we-done-yet?)))
(realized? p) ;; prints "checking..."
basically, I have an interface that takes a promise, but in this particular case I don't have a thread I can use to deliver the promise, so it'd be convenient to 'deliver' the promize lazily instead
what's the use case youre trying to model? maybe that helps in understanding more?
sounds like a delay
but it won't call the body when checked for realized?
There's a library function (defn do-something [a-promise])
that expects a promise. I have a function are-we-done-yet?
, that I can call to check if the promise should be delivered. I want to connect these two without having to create a thread that repeatedly calls are-we-done-yet?
just so it can deliver the promise.
yeah delay
doesn't quite do the trick
so essentially like a callback when the promise is delivered?
no, like a callback that the promise uses to check if it should act delivered
hmm, not sure if there's something off the shelf for this
yeah alright, guess I'll write it myself
why do you need to call are-we-done-yet?
repeatedly? That could be a single deliver
call to unlock other threads which are waiting for the promise.
@U04V4KLKC there's really only one thread here, and it's managed by do-something
since are-we-done-yet?
is a (non-blocking) function and not a promise, I'd need to loop to see if it's done
is it clojurescript?
essentially what I'm trying to avoid is
(let [*p (promise)]
(future
(while-not (deref *p 0 false)
(when (are-we-done-yet?)
(deliver *p true))))
(do-something *p))
no, regular old Clojure
but it's fine, I'll just write the thing I need
so you have a library that expects a promise provided by your code and you want to realize it only when its needed by the library code?
right, yeah
without doing any unnecessary work
that is how delay works
no, not quite
with delay, I'd still need the loop
and there'd still be a thread
> with delay, I'd still need the loop why? because when you can create a delay you don't have a value for it?
delays are only computed once, and are-we-done-yet?
isn't blocking
so I'd have to do something like
(delay (while (not (are-we-done-yet)))) true)
also, realized?
works differently on delays than on promises
hm... differently how?
well, I could be wrong, but afaik realized?
doesn't trigger the computation
Does a more fully-featured promise abstraction solve this? https://github.com/funcool/promesa
How can i close an “infite” io.reader ? (.close reader)
seems to block :thinking_face:
Parsing json in my case
But i don’t think that matters :thinking_face:
It’s basically having an infinite source of messages
doseq
docs:
Repeatedly executes body (presumably for side-effects) with
bindings and filtering as provided by "for". Does not retain
the head of the sequence. Returns nil.
(let [close? (promise)]
(with-open [rdr (create-a-reader)]
(loop []
(when-not (.deref close? 1 false)
(let [msg (read-from rdr)]
(process-msg msg)
(recur)))))
(reify java.io.Closeable
(close [_]
(deliver close? true))))
with-open will take care about closing the reader
Charred returns an auto-closeable json supplier for these types of purposes - https://cnuernber.github.io/charred/charred.api.html#var-read-json-supplier.
is clojure.core/time
's printed value analogous to the real time displayed by the time
utility or something else?
(source time)
shows it is just Java's System/nanoTime
(and purely an elapsed value from that)
I am forever forgetting about source
:face_palm::skin-tone-2: thanks Sean
Is there a macro-less obvious way to implement a call to for
with a variable number of arguments that I missed or is this an acceptable case for writing a macro?
(defmacro cartesian [& args]
(let [sym-arg-pairs (map #(list (gensym) %) args)
syms (mapv first sym-arg-pairs)]
`(for ~(into [] (reduce concat sym-arg-pairs))
~syms)))
(comment
(= (for [a (range 3)
b (range 3)
c (range 3)
d (range 3)
e (range 3)]
[a b c d e])
(cartesian (range 3)
(range 3)
(range 3)
(range 3)
(range 3)))
*e)
here's a function version of cartesian product
(defn cart [colls]
(if (empty? colls)
'(())
(for [more (cart (rest colls))
x (first colls)]
(cons x more))))
For cartesian product (if that is what you are really after, and it wasn’t just a placeholder example) there is also https://github.com/clojure/math.combinatorics
Also, I can’t help but pitch https://github.com/sicmutils/sicmutils/blob/main/src/sicmutils/util/permute.cljc
why isn’t update-vals in clojure docs? https://clojuredocs.org/clojure.core/update-vals
What's the thread pool size for futures again? Something like logical cores * 2 + 1 or something?
If the size of the thread pool is important to your use case, you should create your own thread pool.
clojure, the language runtime, has 2 threadpools built in, one is unbounded and the other is Runtime.getRuntime().availableProcessors() + 2
core.async has its own threadpools, one is unbounded and the other is maybe capped at 8 (I forget) but there is a system property you can use to change it
the java has also gained a "default" threadpool since clojure was released, and a lot of newer stuff uses that by default if you don't specify a pool to run on (completionstage stuff, the new httpclient)
I think that pool is unbounded? not sure, may be the same as https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/concurrent/ForkJoinPool.html#commonPool()