I would actually consider this a core.async defect - the interpretation of literals should not be different in go blocks. I would open a core.async issue with all the details of this thread summarized.
David On Thu, Dec 25, 2014 at 12:14 PM, Sven Richter <[email protected]> wrote: > Hi Thomas, > > Thank you for investigating in this. > Your explanation makes perfect sense, given one has the knowledge about the > internals of clj / cljs and its usage in core.async. > > You are absolutely right about the questionable usage of a set at all for > this usecase. I used a vector until I tried to make it a sorted set, which > did not work. > > I am also uncertain if this is a bug or not, however, from plainly using the > language and the library it is not obvious that a different implementation is > used inside the go block for {}. > > @dnolen in case you are reading this, should I open a defect for this in > core.async? > > Best Regards, > Sven > > Am Donnerstag, 25. Dezember 2014 11:34:50 UTC+1 schrieb Thomas Heller: >> Hey, >> >> I figured it out. Fun puzzle. ;) >> >> As expected core.async is not the real villain here, the behavior sure is >> odd but I'm not sure it is a bug. >> >> The issue is that {:name "test"} inside a go block always gets turned into a >> hash-map, while outside the better option array-map is chosen by the >> compiler due to its size. Now sorted-set tries to sort its entries the >> compare fails cause we have different types. >> >> Clojure: >> (sorted-set (hash-map :test "hello") (array-map :test "world")) >> => ClassCastException clojure.lang.PersistentArrayMap cannot be cast to >> java.lang.Comparable clojure.lang.Util.compare (Util.java:153) >> >> ClojureScript: >> (sorted-set (hash-map :test "hello") (array-map :test "world")) >> => Uncaught Error: compare on non-nil objects of different types >> >> >> Feels weird in the beginning but given that sorting is otherwise basically >> undefined (by which key?) it probably is the only correct behavior. If you >> supply a comparator for sorted set, everything works as expected. >> >> (sorted-set-by (comparator (fn [a b] (compare (:test a) (:test b)))) >> (hash-map :test "hello") >> (array-map :test "world")) >> >> >> Probably also better to start out with the sorted-set in your root atom, >> rather than just replacing the initial vector at some point in time. But >> given that you want to do UI work, I would suggest staying away from >> sorted-set altogether and use a vector instead which you sort after doing an >> insert. I doubt a cursor can point at a specific element in the set cause it >> is not addressable by index. >> >> HTH, >> /thomas >> >> >> >> On Wednesday, December 24, 2014 10:50:33 PM UTC+1, Sven Richter wrote: >> > Hi Thomas, >> > >> > Thanks for taking the time to answer me. >> > >> > Ok, this is the session namespace, reduced to the relevant parts: >> > (ns de.sveri.structconverter.session >> > (:require [reagent.cursor :refer [cur]] >> > [reagent.core :refer [atom]])) >> > >> > (def state (atom {:cur-csv-page nil :files [] :view-state >> > {:transform-texteditor-style {:display "none"} :delete-modal-file "iae"} >> > :transformations []})) >> > >> > (def transformations-cur (cur state [:transformations])) >> > >> > And this is referred by @sess/transformations-cur. >> > >> > Adding the log statement produces this output: >> > before swap #{{:name "wer"}} >> > >> > Which is expected, I am using this atom to display some elements in a >> > select element. It's all working, only time it does not work, is if it >> > runs inside the go block. >> > >> > I have played around with it a bit more, so there are two aspects. >> > >> > First, how is sess/transformations-cur initialized: >> > >> > If I put the data into a set like this: >> > (when ok (reset! sess/transformations-cur (into (sorted-set) >> > (:transformations resp)))) >> > >> > And then later try to conj something with the said function in the go >> > block: >> > (swap! sess/transformations-cur conj {:name trans-name}) >> > >> > I get the error. >> > >> > On the other hand if I initialize sess/transformations-cur like this: >> > (when ok (reset! sess/transformations-cur (:transformations resp))) >> > ;(leaving out the set) >> > >> > And then later update it like this in the go block: >> > (swap! sess/transformations-cur conj {:name trans-name}) ; same code as >> > before it does work. >> > >> > Please note that the updates are triggered manually, so there is enough >> > time inbetween for every action to finish. >> > >> > And the second aspect is that updates outside of the go block always work, >> > no matter if it is a set or not. >> > >> > If it's still hard to follow I might put together a small example. >> > >> > Best Regards, >> > Sven >> > >> > >> > >> > Am Mittwoch, 24. Dezember 2014 22:14:02 UTC+1 schrieb Thomas Heller: >> > > That code doesn't help much either since there is still no way to tell >> > > what sess/transformations-cur is. >> > > >> > > I'd suggest printing the value before trying to swap! it, I see no >> > > reason anything in there would confuse core.async. >> > > >> > > (defn save-transformation [_] >> > > (go (let [trans-name (hel/get-value "transformation-name") >> > > [ok _] (<! (hel/post-async->ch "/cvs/save-transformation" >> > > {:name trans-name >> > > :data >> > > @sess/transform-history-cur}))] >> > > (if ok >> > > (do (.log js/console "before swap" (pr-str >> > > @sess/transformations-cur)) >> > > (swap! sess/transformations-cur conj {:name "foo-name"}) >> > > (h/show-success-message "notification-div" "Transformation >> > > Saved.")) >> > > (h/show-error-message "notification-div" "Could not save >> > > Transformation. Something went wrong.")))) >> > > >> > > ;; this immediately executes after the go block starts >> > > ;; this will most likely happen before (if ok ...) >> > > ;; if sess/transformations-cur is a set, adding the same obj twice >> > > will have no effect? >> > > (swap! sess/transformations-cur conj {:name "foo-name"})) >> > > >> > > >> > > Remember that it is async, so if something does something to >> > > sess/transformations-cur and leaves it in an unusable state you will get >> > > errors. It all depends on the speed of the subsequent steps and who gets >> > > there first. >> > > >> > > Maybe a simple (add-watch sess/transformations-cur (fn [_ _ _ new] (prn >> > > [:swapped new])) would help tracking down the issue as well (I assume >> > > its an Atom?). But CLJS core.async is a lot more fragile than CLJ so it >> > > might actually be a bug, although the operation is quite simple so I'd >> > > suspect some sort of ordering issue. >> > > >> > > HTH, >> > > /thomas >> > > >> > > On Wednesday, December 24, 2014 3:12:07 PM UTC+1, Sven Richter wrote: >> > > > Hi Thomas, >> > > > >> > > > the code I pasted was maybe a bit misleading. >> > > > >> > > > Function one: >> > > > (defn save-transformation [_] >> > > > (go (let [trans-name (hel/get-value "transformation-name") >> > > > [ok _] (<! (hel/post-async->ch "/cvs/save-transformation" >> > > > {:name trans-name >> > > > :data >> > > > @sess/transform-history-cur}))] >> > > > (if ok (do(swap! sess/transformations-cur conj {:name >> > > > "foo-name"}) >> > > > (h/show-success-message "notification-div" >> > > > "Transformation Saved.")) >> > > > (h/show-error-message "notification-div" "Could not >> > > > save Transformation. Something went wrong.")))) >> > > > (swap! sess/transformations-cur conj {:name "foo-name"})) >> > > > >> > > > Function two: >> > > > (defn save-transformation [_] >> > > > (go (let [trans-name (hel/get-value "transformation-name") >> > > > [ok _] (<! (hel/post-async->ch "/cvs/save-transformation" >> > > > {:name trans-name >> > > > :data >> > > > @sess/transform-history-cur}))] >> > > > (if ok (do nil ) >> > > > (h/show-error-message "notification-div" "Could not >> > > > save Transformation. Something went wrong.")))) >> > > > (swap! sess/transformations-cur conj {:name "foo-name"})) >> > > > >> > > > Function two works, function one does not. The only difference is when >> > > > the swap on the cursor happens, either inside the go block (won't >> > > > work) or outside the go block (does work). >> > > > >> > > > This is the asnyc code I am calling in both cases: >> > > > >> > > > (defn post-async->ch [url method content] >> > > > (let [ch (chan 1)] >> > > > (ajax/ajax-request >> > > > {:uri url >> > > > :method method >> > > > :params content >> > > > :format (ajax/transit-request-format) >> > > > :response-format (ajax/transit-response-format) >> > > > :handler (fn [resp](put! ch resp))}) >> > > > ch)) >> > > > >> > > > The error message indeed seems weird, but everything I tried so far >> > > > indicates a bug or a missing feature in core.async. >> > > > >> > > > In the meantime I even refactored my code to remove every core.async >> > > > bit from the ajax calls and it works as expected then (by working with >> > > > callbacks instead). >> > > > >> > > > Best Regards, >> > > > Sven >> > > > >> > > > Am Mittwoch, 24. Dezember 2014 14:23:35 UTC+1 schrieb Thomas Heller: >> > > > > Cannot say without the rest of the code but I what is in >> > > > > (:transformations resp)? sorted-set doesn't work if one item doesn't >> > > > > compare to another (eg. numbers vs maps). >> > > > > >> > > > > Suppose: >> > > > > >> > > > > (def a (atom #{})) >> > > > > => (var user/a) >> > > > > (reset! a (into (sorted-set) [1 2 2 2 3])) >> > > > > => #{1 2 3} >> > > > > (conj @a {:name "test"}) >> > > > > ClassCastException clojure.lang.PersistentArrayMap cannot be cast to >> > > > > java.lang.Comparable clojure.lang.Util.compare (Util.java:153) >> > > > > >> > > > > Doesn't look like a core.async issue? >> > > > > >> > > > > >> > > > > HTH, >> > > > > /thomas >> > > > > >> > > > > >> > > > > On Wednesday, December 24, 2014 11:19:04 AM UTC+1, Sven Richter >> > > > > wrote: >> > > > > > Hi, >> > > > > > >> > > > > > Using the latest core.async (v0.1.346.0-17112a-alpha) updating a >> > > > > > sorted set results in an error. >> > > > > > >> > > > > > I have this code: >> > > > > > >> > > > > > (defn get-transformations [] >> > > > > > (go (let [[ok resp] (<! (h/get-async >> > > > > > "/csv/all-transformations"))] >> > > > > > ;(when ok (reset! sess/transformations-cur >> > > > > > (:transformations resp)) ;works >> > > > > > (when ok (reset! sess/transformations-cur (into >> > > > > > (sorted-set) (:transformations resp))) ;does not work >> > > > > > (println (conj @sess/transformations-cur {:name >> > > > > > "test"})))))) >> > > > > > >> > > > > > where transformations-cur is a reagent cursor on a reagent atom. >> > > > > > >> > > > > > The second reset throws this error (Actually the error occurs on >> > > > > > updating the cursor (conj @sess/transformations-cur {:name >> > > > > > "test"})): >> > > > > > Uncaught Error: compare on non-nil objects of different types in >> > > > > > ioc_helpers:41 >> > > > > > >> > > > > > Are sorted sets not supported? >> > > > > > >> > > > > > How do others keep there sets / lists sorted in the UI? >> > > > > > >> > > > > > Of course I could sort it every time I display it, but it seems to >> > > > > > be more correct to keep it sorted inside the state. >> > > > > > >> > > > > > Best Regards, >> > > > > > Sven > > -- > Note that posts from new members are moderated - please be patient with your > first post. > --- > You received this message because you are subscribed to the Google Groups > "ClojureScript" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected]. > To post to this group, send email to [email protected]. > Visit this group at http://groups.google.com/group/clojurescript. -- Note that posts from new members are moderated - please be patient with your first post. --- You received this message because you are subscribed to the Google Groups "ClojureScript" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To post to this group, send email to [email protected]. Visit this group at http://groups.google.com/group/clojurescript.
