This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-05-22
Channels
- # announcements (2)
- # aws (5)
- # babashka (17)
- # beginners (108)
- # calva (28)
- # chlorine-clover (7)
- # clj-kondo (14)
- # cljs-dev (9)
- # cljsrn (2)
- # clojure (118)
- # clojure-europe (50)
- # clojure-finland (5)
- # clojure-france (15)
- # clojure-italy (9)
- # clojure-nl (14)
- # clojure-spec (11)
- # clojure-uk (43)
- # clojuredesign-podcast (1)
- # clojurescript (35)
- # clojutre (2)
- # clr (3)
- # community-development (6)
- # conjure (9)
- # core-async (41)
- # cursive (7)
- # data-science (7)
- # datomic (11)
- # events (1)
- # figwheel-main (4)
- # fulcro (20)
- # ghostwheel (9)
- # graalvm (18)
- # helix (46)
- # leiningen (14)
- # observability (2)
- # off-topic (23)
- # pathom (4)
- # re-frame (5)
- # reitit (5)
- # rum (2)
- # shadow-cljs (32)
- # spacemacs (8)
- # specter (5)
- # sql (36)
- # timbre (3)
- # vim (15)
- # xtdb (2)
- # yada (2)
I thought swap! and swap-vals! only ever return after a single successful swap? f
can be retried any number of times, but I do not understand why (do (swap-vals a f) (do-stuff))
would not work.
Assuming f
has no side effects, having put them all into do-stuff
I read their initial question as that they have about 10 different f
functions, and some of them have side effects
Sure, if they can tease apart their side-effecting f
functions into a side-effect free part f
, and a separate side-effecting part do-stuff
, then the existing swap!
should suit their needs. It seems they are looking for any advice that allows them not to tease them apart like that.
I may have missed the context, if you were suggesting changes to hiredman's code snippet above, rather than something to use without his my-swap! or something similar.
Oh, you can be as clear as can be, and readers like me can skip reading a couple of messages of context that critically matter, and get confused. That's on me 🙂
Swap-vals can rerun f
multiple times, and you will never know how many times it ran it before it successfully completes
So you cannot use a function which both changes a value and (as a side effect) queues an action to be run
Compare-and-set is strictly more powerful than swap! And swap-vals because using CAS failures are visible, while with higher level functions like swap! And swap-vals! You only see the success, which means if you have actions you need to do when the cas fails (like clear a queue) you cannot use swap and friends
I know what cas does but I see what you mean, I should actually have read the OPs intent before your code
The conclusion is that @mpenet is correct that one can compare the result of swap-vals! to know if the value has changed after a successful swap and then run a side effect based on it. But @hiredman is also correct if we assume OP needed to take some actions when the swap fails. Is that a good reading of the situation?
And @andy.fingerhut is also correct that one can just wait for swap! to finish and immediately run a side effect after if we neither care about the swap failing or the value having changed
What you lose of you do this is the ability to have f change the value of the atom, and also take action based on the changes it made
For example if the atom is holding a state in some state machine you cannot both step the state machine and run some action based on that step
If you deref you have a race condition, if you use swap-vals you basically have to step twice (once when actually swapping and one again with the old and new value to figure out what the step was and run your action)
Right okay. I get it now. I just wanted to confirm the simpler assessments in case somehow I was missing something about the whole atom machinery
@didibus If f
did not change the value, you can't tell the difference between a successful swap and and failed one tho', right?
(although if f
is truly a pure function, it could either be identity
or else it's at a fixed point)
If you don't want the swap to actually happen (ex can't have it be identity in "failure" case) you need cas, otherwise swap-vals is fine.
deref + cas can be racey tho. I can imagine a scenario where you can have change happen in another thread (if not strictly using my-swap) and you could miss changes happening between the 2, think in dec inc dec type of scenario
But that's pushing it. I guess if you use that kind of code you would avoid doing that
Like, I am shocked at having to explain this, it seems super clear by analogy to the way agents and the stm hold agent sends until the current action or transaction completes
The only place I have ever seen the stm used in production code was because author wanted an atom, but also needed side effects in the swapped function, so they used a ref and an agent
The ref could give them a cas, and the stm holds agent sends until the tx completes (and only sends the sends from the successfully tx)
Same thing, but no stm. And the particular case in our billing code I am thinking of the "atom" is actually an IAtom impl backed by a row in a database so the stm wouldn't solve anything
That sounds interesting and strange. Would love to hear some details behind the decision to not make an explicit protocol about it.
So we're talking of synchronizing the side effect and the swap? Like either the swap succeeds and the side effect succeed, or none of them are performed?
Barring the possibility of unlimited retries because of multiple threads frequently updating an atom, a swap always succeeds, eventually.
I guess, also barring other weird things like the function given to swap going into an infinite loop. Not sure about what hapepns if it throws an exception. I'm assuming in my previous statement neither of those things happens.
And my understanding is that hiredman's code snippet fairly far above now is a description of one way to get the behavior of my-swap!
finishes, and whatever side effect the passed-in function wants to achieve, is only ever done exactly once, after my-swap!
finishes. That technique appears to require you to write the updating function f
in a particular way, i.e. my-swap!
cannot take an arbitrary function with arbitrary side effects and make things work as desired.
I'm thinking, I'd probably just lock if I needed to do side effect + update together atomically.
Hello All, need your help to setup the code scan for Blackduck + Clojure?
Any suggestions how to integrate Blackduck with Clojure?
If we wanted to celebrate a "Clojure Day" once a year, which day would it be?
When is Rich Hickey’s Birthday? 😄
Or maybe 16th October, because that’s the day of the first release
@U04V70XH6 any suggestion ? Do you think it would be possible to have Cognitec make an "official" day?
A question better directed at @U064X3EF3 surely?
I'm not sure Rich would be excited about publicizing his birthday :)
we consider Oct 16 to be Clojure's birthday
I'm getting a NoClassDefFoundError when calling a static method in a class java class imported from a library (this is the class' source, just in case: https://github.com/camunda/camunda-bpm-platform/blob/master/model-api/bpmn-model/src/main/java/org/camunda/bpm/model/bpmn/Bpmn.java). The error is:
1. Unhandled java.lang.NoClassDefFoundError
Could not initialize class org.camunda.bpm.model.bpmn.Bpmn
But of course that class is defined. What I think NoClassDefFoundError means in that case is this class' (Bpmn) initializer is itself trying to access a class that's not in my classpath, due to a missing dependency.
The problem is, I don't know how to figure out what's actually the missing class. Is there a way I can access the name of the class that can't be found?Ok, so it seems I have conflicting dependencies between packages in my project and this library (yay, JVM! :face_with_rolling_eyes:), so the missing class is probably due to a version clash
@vale it depends, If you're using CIDER what you're probably seeing is the output of the stacktrace middleware (https://github.com/clojure-emacs/cider-nrepl/blob/master/src/cider/nrepl/middleware/stacktrace.clj). But I guess this relies on actually catching the exception somewhere and inspecting the object.
If you mean the actual default stacktrace text you get on uncaught exceptions, I think that's on the JVM, or the implementations of the Exception
class (i.e. printStackTrace
?) I'm not really sure about that though
i was wondering what it'd take to format errors better. i can do it in repl with :caught
but what about normal execution?
If I wanted to implement a new associative collection that does some extra work whenever values are assoc
ed and dissoc
ed would the right thing be to implement IPersistentMap
?
depends on how you want to use it
if you need dissoc, then yes, probably IPersistentMap is the right target (if only assoc, then Associative)
while there are good reasons you might want to do this, I think I'd question whether it's really what you want, but hard to tell with no context
We’re trying to choose the right abstraction to represent a particular kind of model from our domain.
The model needs to be able to “incorporate” and “unincorporate” information in the form of Clojure hashmaps.
Initially we went with a Clojure protocol with incorporate
and unincorporate
methods that take a model, a unique identifier, and a map as arguments.
But I’m realizing that if we had the model implement IPersistentMap
by delegating to incorporate
and unincorporate
we could potentially get the model abstraction to play well the existing Clojure library (a-la https://insideclojure.org/2016/03/16/collections/).
Hope that makes sense, @U064X3EF3. 🙂
Are there good heuristics / rules of thumb for when it’s a good idea to implement collections interfaces like IPersistentMap
and when it’s better to go with a different approach? :thinking_face:
From what you describe, I do not see much motivation to move beyond just using Clojure data
I would only do that if you truly have a new data type with different semantics
Seems to me the only time you would do that is when Clojure’s standard data structures don’t work for you.
Well, the motivation is that whenever someone incorporates or unincorporates data you need to make some modifications to other parts of the model.
We could package up all those operations (adding the data and making the corresponding changes to the other parts of the model) into a function / protocol method but then we’d have to reinvent the wheel for operations built on top of Clojure’s assoc
that we’d get for free if we implemented Clojure’s collection interfaces. At least that’s the idea.
while that would be some convenience, I would find the idea of things like into
and merge
invoking arbitrary domain logic to be surprising
Ditto - we have a tool for custom logic on a data structure, that’s a function :)
That makes sense. One argument against this that occurred to me is that it’s likely that we’d wind up violating the performance guarantees that the core collections provide for common operations.
on a related note, what are those performance guarantees for recent clojure versions? the only chart i find is from 2010 and even that doesn't seem to be official
We don’t have one place for that right now (there is a doc site issue to improve some of this)
But generally conj/assoc/disj/dissoc/get/contains? should be effectively constant (really log base 32). count may be linear but things marked Counted should be sublinear (often constant). In general stuff that takes seqables is expected to be linear.
And if you're saying there are different kind of models that need a different logic applied, you could use a multi-method for it
Hi I have a list of strings. I want to join certain of those strings based on a predicate, but the rest I just want to remain as they are, is there a idiomatic way of doing this?
(def strs ["a" "a" "b" "a" "c"])
(->> strs
(group-by
#(boolean
(#{"a"} %)))
((juxt
#(str/join (get % true))
#(get % false)))
flatten)
;;=> ("aaa" "b" "c")
sounds like a job for reduce to me, with a structured accumulator having a key for potentially joinable items and a key for collected items
(reduce f {:strings [] :partial nil} coll)
you'd likely need a wrap up function at the end in case you still had something in :partial
(sometimes instead you can treat the end of the []
as a "staging area" where you often take things out, combine, and put back in...)
Is there a JVM switching tool that people like? I'm on multiple platforms (Debian, Fedora, macOS) and also install GraalVM in ~/.local, so reliably switching between JDKs is extremely obnoxious, especially when I'm chasing down a bug that only appears to exist within 1 version
most people seem to use jenv
@noisesmith Thank, I been trying to work through your answer. I get the destructuring, but what must the function do to reduce - I keep getting out of memory errors
I'd expect the function to be something like:
(fn [{:keys [string partial]} elt]
(if (combine? partial elt)
{:strings strings
:partial (str partial elt)}
{:strings (conj strings elt)
:partial partial}))
but that's really a guess - it depends on your rules for combining(let [sb (StringBuffer.)
pred #(even? (count %))]
(update (reduce (fn [acc s]
(if (pred s)
(update acc :joined #(.append % s))
(update acc :coll conj s)))
{:coll []
:joined sb}
["a" "ab" "abc" "abcd"])
:joined
str))
We’re trying to choose the right abstraction to represent a particular kind of model from our domain.
Are there good heuristics / rules of thumb for when it’s a good idea to implement collections interfaces like IPersistentMap
and when it’s better to go with a different approach? :thinking_face:
@noisesmith Thanks - worked it out - been bashing my head around with loops etc for days.
speaking of, a good criteria is that every usage of loop
that consumes a list in order can be replaced by map
or reduce
(combined with eg. take-while
or reduced
if you need early exit)