This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-07-28
Channels
- # announcements (33)
- # aws (2)
- # babashka (14)
- # beginners (128)
- # calva (34)
- # cestmeetup (3)
- # clj-kondo (12)
- # cljdoc (3)
- # clojure (114)
- # clojure-europe (31)
- # clojure-italy (3)
- # clojure-nl (7)
- # clojure-uk (6)
- # clojurescript (35)
- # conjure (20)
- # cursive (3)
- # data-science (3)
- # datomic (16)
- # docker (13)
- # events (1)
- # figwheel-main (22)
- # fulcro (109)
- # jobs (1)
- # kaocha (8)
- # keechma (1)
- # lambdaisland (5)
- # malli (1)
- # meander (8)
- # mid-cities-meetup (1)
- # off-topic (6)
- # overtone (7)
- # pathom (6)
- # re-frame (2)
- # reitit (9)
- # ring (1)
- # shadow-cljs (92)
- # specter (1)
- # tools-deps (311)
- # xtdb (76)
Just started #community-projects to curate projects that some of us from this group have going. I have been looking for projects to collaborate on so this would be cool for others on the same boat. 🚀
@sveri have you seen https://github.com/ajoberstar/ike.cljj? It has some stuff around Path
No, I have not so far, thank you. Hopefully I won't need it. The io/file problem was the first time I encountered something similar, I hope it stays so 🙂
What is the simplest way to run f
, n
(100) times parallel? The f
is making HTTP request and I want to test server response in easy way. I don’t want to spend to much time on that now to research some tools.
Since installing OpenJDK14 (was using JDK8) I now have to use LEIN_USE_BOOTCLASSPATH=no lein repl
or I get an exception that I can't decipher (https://gist.githubusercontent.com/mmower/a01d6803eeb429c05676e2339676cada/raw/062e7b675d3a52f6e457d9781b939a4f66fd410c/gistfile1.txt) which seems to occur in Fipp but, from my searching, does not implicate Fipp. Is this normal? Should I expect to use this from now on? I confess I couldn't follow the discussion about what's changed/broken.
yes, you should expect this from now on. imho, lein should never have been using bootclasspath in the first place, and the ground is shifting in the jdk
this was always just an abuse of bootclasspath solely to shave a few ms off startup time
https://dev.clojure.org/jira/browse/CLJ-2426 is an occasional pain point for me, especially when interacting with 3rd party libraries that don't particularly use / are aware of metadata-based protocol extension It seems a quite simple bug to tackle, and also a frustrating one because it means that a newer, useful feature will be sort of a "second-class citizen". One would expect clojure.core features to play well with each other, offering a consistent experience It doesn't seem consistent to offer a feature but not to support it in some places?
this is not a simple bug and not even something that we're sure we want to do
I'd appreciate if these considerations were shared somewhere
For example I take https://clojure.org/reference/protocols#_extend_via_metadata at face value - it offers me a feature, I use it. Maybe it's meant to be used very occasionally?
Maybe it has some complexities that CLJ-2426 made evident?
(So far I haven't come to actually think this. I have happily used both the feature and my own satisfies?
with an extra check for IMetas. Sadly I cannot convince the world to use my own satisfies?
)
in the case of external or direct extensions there are single categoric things we can check to answer the question of satisfies?
(either an entry in the protocol data or instanceof Java checks). with metadata extension, you have to look up the protocol methods and check whether one? all? are present in the metadata, and you need to do this check before external extensions, so it has a potentially large performance cost to add this check (which is an existing separate issue)
in general, leaning on satisfies? too much is usually a smell - you should instead be leaning on protocol polymorphism to handle conditional cases
you can only use metadata extension if the protocol is declared to support it so this is not even an option in most cases
> so it has a potentially large performance cost to add this check (which is an existing separate issue)
(what's the separate issue?)
One can consider that satisfies?
is an occasional tool and a pretty unlikely one to belong to high-performance code paths.
If one was concerned with performance, I reckon one wouldn't use satisfies?
to begin with? Favoring clojure's dispatch system instead
I realise my input is limited here, but one can consider the tradeoff of improving the performance of a function that has been 'slow' for 10 years vs. having a consistent feature set
As you say, leaning on satisfies? too much is usually a smell
so making it the deal breaker seems a bit odd
going back to my original comment, the status of this is "not sure what we want to do"
Is there a convenient way to get clojure.test to generate human-readable strings?
I guess I’ll have to do something like
(with-redefs [gen/string-ascii (gen/fmap my-string-generator ...))]
...)
I wrote a (small) function to cache a function for a number of seconds (as defined by the function). Does Clojure (or a lib) already have this? Here is the code: https://gist.github.com/ivarref/a9592d3ef4b3fb6c03c6ca8cd2c3c210 Any thoughts or comments? Thanks.
https://github.com/clojure/core.cache has a ttl strategy
Right thanks. But I think there you must set TTL up front when creating the cache, that is TTL may not vary
impl looks reasonable, maybe I wouldn't use locking
since it's plausible that cached fns could invoke each other, causing a deadlock
Maybe you use locking
for not hitting a rate-limited API? In which case I'd tackle that separately
I think locking creates a re-entrant lock, but yes it's better to avoid locking in general
it's a special form, looks like it's directly using some functionality of ASM.java https://github.com/clojure/clojure/blob/f9b04ae5f7fd9f11ea7a431675f4ec2d23f295f5/src/jvm/clojure/lang/Compiler.java#L2114
Personally I don't quite understand core.cache that well, so I usually default to using something like caffiene
(defn cache-function [f minutes-to-cache]
(let [cache (-> (Caffeine/newBuilder)
(.expireAfterWrite (Duration/ofMinutes (long minutes-to-cache)))
(.build (reify CacheLoader
(load [_ args]
(apply f args)))))]
(fn [& args]
(.get cache args))))
But that doesn't support per-entry ttl either
https://github.com/ben-manes/caffeine/issues/114Caffeine.newBuilder()
.expireAfter(new Expiry<Key, Graph>() {
public long expireAfterCreate(Key key, Graph graph, long currentTime) {
return (graph instanceof NullGraph)
? TimeUnit.MINUTES.toNanos(1)
: TimeUnit.MINUTES.toNanos(10);
}
public long expireAfterUpdate(Key key, Graph graph,
long currentTime, long currentDuration) {
return currentDuration;
}
public long expireAfterRead(Key key, Graph graph,
long currentTime, long currentDuration) {
return currentDuration;
}
})
.build(key -> createExpensiveGraph(key));
(defn cache-single-arg-function [f]
(let [cache (-> (Caffeine/newBuilder)
(.expireAfter
(reify Expiry
(expireAfterCreate [_ {:keys [ttl]} _ _]
ttl)
(expireAfterUpdate [_ _ _ _ current-duration]
current-duration)
(expireAfterRead [_ _ _ _ current-duration]
current-duration)))
(.build
(reify CacheLoader
(load [_ k]
(f k)))))]
(fn [k]
(.get cache k))))
That should work @UGJE0MM0W
This is the library i am using [com.github.ben-manes.caffeine/caffeine "2.8.4"]
Thanks for all the input! @U45T93RA6 Re locking: yes, the idea was that for my case when a token expires, I'd rather not have multiple invocations of the "expensive" and cached function in a burst
I ended up rolling my own mini library: https://github.com/ivarref/memoize-ttl
let's say there is a package called x. now a process imports x and two other packages that depend on x (possibly different versions of x). will there be three separate instances of x running? will they be qualified through different namespaces/classpaths?
first step is to make sure that they are both using the same version
it's impossible to have two versions of x loaded, without jumping through hoops (unless using js / cljs)
there’s some caveats here, but, essentially you can only have one version of a lib at one time
the job of your package manager is to pick one
that's the primary thing it does
lein, deps.edn, maven, etc. each have their own set of rules, and have ways of overriding those rules
outside of pathological conditions, or finicky special cases, there's no masking
I googled around and I found this quote: > Leiningen and Maven, when there is a conflict always pick the version that is closest to the root of the dependency tree; where as `tools.deps` always picks the newest.
leiningen special cases clojure.core also
leiningen also lets plugins manipulate the dep tree (this can include overriding or removing deps)
deps.edn has profiles, but that only allows merge-over and accretion IIRC - you can't exclude in a profile something that was present in the base(?)
does it mean then that under the default behaviour of deps, a package might break if there is a newer version of its dependencies called by the neighbouring packages?
You mean if something else in your project is updated and now relies on a newer version of that shared dependency? Yes, that's possible. And it's possible with Maven-based resolution too.
If you have a top-level dependency on it, that version takes precedence in both Maven and t.d.a. resolution tho'.
often enough that I've in many cases migrated away from libraries because they use jackson
jersey/jackson has literally become a trigger word in our team. Now one of our runtimes decided to upgrade from jersey or jackson 1 to 2. It turns out, one of the reliant components cannot do the upgrade in time, so the runtime will deliver both. Ah and did you know that http://apache.commons.io in it's current 2.6 version has separate versions for it's packages that go down to 1.4.x? /sorry for the rant
I can relate, I think we've all seen it
it's very rare in the clojure world to see this sort of breakage from anything outside jackson though
hello everyone,
simple question:
Im asserting stuff in test, basically comparing two maps, the on from sut
and the other one is the expected value
is there a idiomatic way of checking if on map contains the other? what I want to avoid is breaking that test when sut
adds another key to the result
I have a submap? function I often use in tests
@plins, this recently came up. you may find the discussion useful, https://clojurians.slack.com/archives/C03S1KBA2/p1595479081182300
there's a lot of potential choices about how this works, and I'm not sure I've made the same identical ones every time I've used it, but maybe something that will ultimately end up in core or something
feel free to borrow and modify as you like
the version I usually use (in unit tests in particular) is (= (select-keys m2 (keys m1)) m1)
I usually wanted it for nested maps too though
oh - that's different, yeah
I'm working on an inherited codebase and I found some code for peeking/popping a Queue that is summarized in this StackOverflow answer: https://stackoverflow.com/a/29616612/4750575
Is this bad practice? It seems like totally incorrect usage of a ref
-- there's only 1 identity at play, not multiple
(defn dequeue!
"Given a ref of PersistentQueue, pop the queue and change the queue"
[queue-ref]
(dosync
(let [val (peek @queue-ref)]
(alter queue-ref pop)
val)))
(let [q (ref clojure.lang.PersistentQueue/EMPTY)]
(dosync (alter q conj 1 2 3)
(alter q conj 5))
(fu/dequeue! q)
=> 1
(seq @q)
=> (2 3 4 5))
@ccann Yeah, that seems excessive, but its not incorrect since changes to a ref are ordered
The code in question actually has an atom containing a map where the values are ref
s to PersistentQueues.
Every request thread dereferences the atom, finds the queue it wants to update, and then runs dequeue!
@emccue that code would be erroneous with an atom, a deref inside a function setting the value is a race condition
I was thinking of modifying the code to just use swap-vals!
on an atom containing a map with values of just PersistentQueues
but I'm not sure how to compare the performance of every thread swapping the same atom versus every thread dereferencing the same atom and then altering a queue ref
then you can start to write an impl using an atom/arrayblockingqueue/whatever and do profiling
deref is cheap, swap and alter are slightly more expensive, especially if there is contention (multiple threads trying to set the same container)
IMHO the only reason to use refs (rather than an atom containing a map with all your values nested in it) is if there is too much contention / retry activity on the atom, refs allow coordination while reducing contention
but clojure apps typically don't demand that much of their mutable stores
I would definitely look at swapping out the usage of PersistentQueue for some java queue (linkedblockingqueue or whatever) if you don't need immutable queues
I would even look at replacing the outer atom + map with a concurrenthashmap, if you are never using it as a value
when you nest mutable references (atoms, refs, agents, etc) like that, you mostly lose what is great about the clojure kind of identity model (mutable identities pointing to immutable values)
yeah absolutely, seeing refs nested in an atom was the "head scratcher" moment that prompted investigation
at least they recognized the inherent race conditions that can arise when trying to dequeue from an immutable queue in an atom and used a ref