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.