is there a good guide somewhere on how to build integrations using transducers? i'm working with indexeddb in the browser, and i'd like to write a wrapper that allows me to iterate asynchronously over a cursor and deal with the individual values using an xf (filtering and mapping them); at the end of iteration i want to resolve a promise with the result. is there a straight-forward optional-add! that i could implement that just calls the xf and acts accordingly?
I'd just use some existing code (or write my own) that wraps the required IndexedDB operations with a ES6 iterator and then use es6-iterator-seq to turn it into a seq. Then it's smooth sailing with the seq abstraction and all the built-in functions.
where does es6-iterator-seq come from?
cljs.core
oh really? i didn't know!
thanks
the problem is that this would be an async iterator
because the way you're supposed to iterate through a cursor is basically
cursorRequest.addEventListener('success', function (ev) {
const cursor = ev.target.value;
if (cursor) {
// still within range; get current value using `cursor.value`
cursor.continue(); // advances cursor, causing the callback to be called again
}
})Ah, of course. In that case, I'd probably use the transducer directly.
i think that was what my question was getting at
there are plenty examples with a transducer building up an immutable value using conj or similar constructs
is there any good example of other processes? i know there's core async, but that's not exactly easy to follow either
this is what i have:
(defn transduce-cursor [xf cursor-request]
(js/Promise
(fn [resolve _]
(let [result (volatile! [])]
(.addEventListener cursor-request "success"
(fn [ev]
(if-let [cursor (-> ev .-target .-result)]
(let [step (xf @result (.-value cursor))]
(vreset! result step)
(.continue cursor))
(resolve @result))))))))sorry, i edited the above code snippet a couple of times, i hope that was not too confusing
Yes, that's exactly how I'd do it.
Except that xf in your code is actually a reducing function, not a transducer, so I'd name it just f or rf.
To use a transducer, you feed it a reducing function to get a new reducing function: (let [rf (xform rf)] ...).
You could of course also do it via core.async.
The code would not become any simpler though and you'd have an extra dependency and a much harder time debugging stuff.
If you want similar behavior to transduce, you could do something like this (untested):
(defn transduce-cursor
([xf rf cursor-request]
(transduce-cursor xf rf (rf) cursor-request))
([xf rf init cursor-request]
(js/Promise
(fn [resolve _]
(let [result (volatile! init)
f (xf rf)]
(.addEventListener cursor-request "success"
(fn [ev]
(if-some [cursor (-> ev .-target .-result)]
(let [step (f @result (.-value cursor))]
(if (reduced? step)
(resolve (f (unreduced step)))
(do (vreset! result step)
(.continue cursor))))
(resolve (f @result))))))))))
Differences from your implementation:
• Takes a transducer and a reducing function.
• Takes an optional init argument.
• Checks for reduced?
• Calls the completion arity of the reducing function at the end.