This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-01-15
Channels
- # adventofcode (6)
- # beginners (63)
- # boot (39)
- # cider (25)
- # clara (9)
- # cljs-dev (27)
- # clojure (100)
- # clojure-dev (39)
- # clojure-dusseldorf (7)
- # clojure-italy (32)
- # clojure-russia (23)
- # clojure-spec (28)
- # clojure-uk (51)
- # clojurescript (197)
- # core-async (44)
- # cursive (3)
- # datomic (14)
- # emacs (4)
- # fulcro (27)
- # graphql (23)
- # hoplon (4)
- # jobs (9)
- # juxt (1)
- # leiningen (3)
- # nyc (1)
- # off-topic (6)
- # om (7)
- # onyx (6)
- # parinfer (11)
- # re-frame (23)
- # reagent (15)
- # ring-swagger (1)
- # rum (15)
- # shadow-cljs (37)
- # sql (24)
- # uncomplicate (4)
- # unrepl (17)
I have no skin in this game (so I won't create a ticket for it) since I always pass JS arrays to React but:
React will use ES6 iterators to go through collections. Currently this uses first/next
for all CLJS data structures which obviously isn't optimal. So there is a quite a few ideas that could make this faster (and produce much less garbage). 1. For iterables use -iterable
and adapt it to ES6. 2. Add an iterator for chunked sequences which efficiently walks over the array chunks.
@rauh, don’t our collections return ES6 iterator? we’re not changing how our own interators work
but if there’s some suboptimal about the ES6 iterator we return we can do something about that
@dnolen Yes, they all do and it works. The (es6-iterable TheCljsDataType)
will add the Symbol.iterator
to the prototype which always returns the naive iterator:
(deftype ES6Iterator [^:mutable s]
Object
(next [_]
(if-not (nil? s)
(let [x (first s)]
(set! s (next s))
#js {:value x :done false})
#js {:value nil :done true})))
Our IIterable
are often smarter. They're effcient. But they use Java'y .hasNext / .next
style API which isn't a standard
It'd be an easy change: Check for IIterable
in the es6-iterator
function and adapt it. Or --much more of an overhaul-- make the ES6 iterator implementation the default for the data structures and make the adapter the other way around (thus saving on the extra indirection for ES6 iterators).
Yeah but then we'd bring it a lot more non-DCE'able code and for each datasctructure add 2 iterators: ES6-style and Java-style (which already exist for many).
Easiest could be: Add a param for most iterators: es6?
that check on the call to .next()
if it's supposed to return an ES6 style entry or just the Java-style entry. The extra check on that field should be neglible in performance.
Yeah it wouldn't add much anyway, but it might really not be worth it. Here would be a pretty good start that avoids a lot of the overhead:
(defn es6-iterator
[coll]
(if (iterable? coll)
(let [it (-iterator coll)]
#js{:next (fn []
(if ^boolean (.hasNext it)
#js{:value (.next it), :done false}
#js{:value nil, :done true}))})
;; Fallback to naive first/next iterator:
(ES6Iterator. (seq coll))))
That would avoid writing countless iterators. Since we still need the Java style iterators which are used a bunch around core.
Optim: Should use implements?
instead of iterable?
since we always deal with non-natives here.
but ok, write a thing that wraps the iterator and does the right thing and let’s see some benchmarks
@dnolen Yeah it should work. It's very straight forward. You can eval the other way around with (es6-iterator-seq (es6-iterator some-cljs-seq))
The state is within the CLJS iterator, so we can even leave it inline like this, no reason for a deftype.