Hi Alex. I know transients aren't bash-in-place, as they may return new references, but that is not the problem I'm having here. If you bash in place, you would get unexpected results, but still the same result every time. The problem here is that the 'value' of the transient is changing underneath you because of another thread.
Let me change the example a bit: (let [v (transient {:a 0}) f1 (future (reduce #(assoc! % (+ (:a %) %2)))) f2 (future (reduce #(assoc! % (+ (:a %) %2))))] @f1 @f2 ; wait for futures (persistent! f1)) This will also give varying results on each run. If you were to add a (println (:a %)) in the reducing fn of f1 you would see the results are not the same each time. Op dinsdag 4 november 2014 06:13:00 UTC+1 schreef Alex Miller: > > > > On Monday, November 3, 2014 10:44:19 PM UTC-6, Atamert Ölçgen wrote: >> >> Thanks Alex! >> >> Now that I took a second look at Daniel's code, it seems assoc! is used >> like swap!, as if it would modify m in place. So I would expect, if it runs >> without errors, result to be {:a 0}. >> > > Right, that is an incorrect usage - it will actually modify with changes > though, but not in expected ways (this is independent of the change we're > discussing - you can get the same behavior in a single thread modifying a > transient without reusing the return). > > Given that transients are values (not reference types like ref or atom) I >> can't think of a case where they can be modified concurrently. >> > > You can, but not without going pretty far out of normal Clojure code. > > >> >> >> On Tue, Nov 4, 2014 at 11:19 AM, Alex Miller <al...@puredanger.com> >> wrote: >> >>> >>> >>> On Monday, November 3, 2014 9:00:10 PM UTC-6, Atamert Ölçgen wrote: >>>> >>>> >>>> >>>> On Mon, Nov 3, 2014 at 5:57 PM, Daniel Marjenburgh <dmarje...@gmail.com >>>> > wrote: >>>> >>>>> Hi, >>>>> >>>>> I just want to address this issue (CLJ-1498 >>>>> <http://dev.clojure.org/jira/browse/CLJ-1498>). It was accepted in >>>>> 1.7-alpha2 and I haven't seen a lot of discussion around it, even though >>>>> it's quite a big change. >>>>> >>>>> With this change the following code is possible: >>>>> >>>> >>>> With persistents the result would be the same, every time. If this is >>>> now valid Clojure, I didn't run it myself, we are sacrificing consistency. >>>> I don't understand what we're getting in return. >>>> >>>> Even the simple example in the ticket (with one future) doesn't make a >>>> lot of sense to me. >>>> >>>> Am I missing something obvious? >>>> >>> >>> Transients always expect thread isolation. In the past this was locked >>> to a single thread (the one that made the transient). That restriction now >>> extends to being used by multiple threads, but isolation should still be >>> maintained for proper use. >>> >>> What we are gaining is the ability to use transient collections in go >>> blocks with core.async, where the thread being used is just one pool out of >>> a thread. For example (just typing this not running it, so excuse any >>> typos): >>> >>> (defn drain [ch coll] >>> (let [t (transient []] >>> (go-loop >>> (if-some [v (<! ch)] >>> (do (conj! t v) (recur)) >>> (persistent! t))))) >>> >>> This doesn't necessarily work in core.async because each time the <! >>> parks, it may be woken up with a different thread from the pool. The >>> transient change allows this kind of code to succeed. >>> >>> >>>> >>>>> (let [m (transient {:a 0}) >>>>> futs (for [n (range 100)] >>>>> (future (assoc! m :a (inc (:a m)))))] >>>>> (mapv deref futs) ; wait until futures are done >>>>> (persistent! m)) >>>>> >>>>> >>>>> The results will vary per run, where it used to throw >>>>> an IllegalAccessError: "Transient used by non-owner thread". >>>>> >>>>> I understand the problems of not being able to have 1 go routine >>>>> access a transient, even though it would be safe, but this solution feels >>>>> like it's throwing out the baby with the bathwater. Basically, it's doing >>>>> away with what the following block on clojure.org >>>>> <http://clojure.org/transients> says: >>>>> >>>>> *Transients enforce thread isolation**.* Because each result of a >>>>>> transient operation shares (mutable) structure with the previous, it >>>>>> would >>>>>> be very dangerous if more than one thread were to manipulate a transient >>>>>> at >>>>>> once. In order to prevent this, transients will detect any (read or >>>>>> write) >>>>>> use from a thread other than the one that created them and throw an >>>>>> exception. >>>>> >>>>> >>>>> >>>>>> >>>>>> This may not sound like a concurrency story, but single-thread >>>>>> isolation is actually a very useful concurrency semantic. The whole >>>>>> point >>>>>> of using transients is that doing so is safe, because their use is an >>>>>> isolated implementation detail of otherwise functional code. Having that >>>>>> be >>>>>> enforced means that some things that would normally be very hard to make >>>>>> safe with ordinary mutable data structures become easy. >>>>> >>>>> >>>>> I don't have a good solution for dealing with transients and logical >>>>> threads, but I would much prefer keeping the semantics of transients as >>>>> they are and maybe pass an option to transient to disable owner checking. >>>>> >>>>> -- >>>>> You received this message because you are subscribed to the Google >>>>> Groups "Clojure" group. >>>>> To post to this group, send email to clo...@googlegroups.com >>>>> Note that posts from new members are moderated - please be patient >>>>> with your first post. >>>>> To unsubscribe from this group, send email to >>>>> clojure+u...@googlegroups.com >>>>> For more options, visit this group at >>>>> http://groups.google.com/group/clojure?hl=en >>>>> --- >>>>> You received this message because you are subscribed to the Google >>>>> Groups "Clojure" group. >>>>> To unsubscribe from this group and stop receiving emails from it, send >>>>> an email to clojure+u...@googlegroups.com. >>>>> For more options, visit https://groups.google.com/d/optout. >>>>> >>>> >>>> >>>> >>>> -- >>>> Kind Regards, >>>> Atamert Ölçgen >>>> >>>> -+- >>>> --+ >>>> +++ >>>> >>>> www.muhuk.com >>>> >>> -- >>> You received this message because you are subscribed to the Google >>> Groups "Clojure" group. >>> To post to this group, send email to clo...@googlegroups.com >>> Note that posts from new members are moderated - please be patient with >>> your first post. >>> To unsubscribe from this group, send email to >>> clojure+u...@googlegroups.com >>> For more options, visit this group at >>> http://groups.google.com/group/clojure?hl=en >>> --- >>> You received this message because you are subscribed to the Google >>> Groups "Clojure" group. >>> To unsubscribe from this group and stop receiving emails from it, send >>> an email to clojure+u...@googlegroups.com. >>> For more options, visit https://groups.google.com/d/optout. >>> >> >> >> >> -- >> Kind Regards, >> Atamert Ölçgen >> >> -+- >> --+ >> +++ >> >> www.muhuk.com >> > -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.