beginners

heyarne 2025-11-18T08:06:35.231809Z

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?

p-himik 2025-11-18T08:16:36.639179Z

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.

heyarne 2025-11-18T08:17:31.036999Z

where does es6-iterator-seq come from?

p-himik 2025-11-18T08:18:51.853129Z

cljs.core

heyarne 2025-11-18T08:19:03.590009Z

oh really? i didn't know!

heyarne 2025-11-18T08:19:05.040539Z

thanks

heyarne 2025-11-18T08:20:42.889279Z

the problem is that this would be an async iterator

heyarne 2025-11-18T08:22:10.536429Z

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
  }
})

p-himik 2025-11-18T08:23:10.820519Z

Ah, of course. In that case, I'd probably use the transducer directly.

heyarne 2025-11-18T08:23:40.520739Z

i think that was what my question was getting at

heyarne 2025-11-18T08:24:04.536069Z

there are plenty examples with a transducer building up an immutable value using conj or similar constructs

heyarne 2025-11-18T08:24:41.167529Z

is there any good example of other processes? i know there's core async, but that's not exactly easy to follow either

heyarne 2025-11-18T08:28:18.633879Z

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))))))))

heyarne 2025-11-18T08:30:59.477949Z

sorry, i edited the above code snippet a couple of times, i hope that was not too confusing

p-himik 2025-11-18T08:32:37.620889Z

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)] ...).

p-himik 2025-11-18T08:33:42.390559Z

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.

2025-11-18T19:59:48.163199Z

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.

👍 2