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.