Hi All,

I’d like to offer an alternative implementation of clojure.core/transduce.

(I’ve done some searching, and I didn’t find any evidence that this has 
been raised before, but my apologies if I missed anything.)

This alternative is best motivated by example. Consider the following 
transducers:

(defn init-with
  [x]
  (fn [rf]
    (fn
      ([] (rf (rf) x))
      ([result] (rf result))
      ([result input]
         (rf result input)))))

(defn complete-with
  [x]
  (fn [rf]
    (fn
      ([] (rf))
      ([result]
         (rf (rf result x)))
      ([result input]
         (rf result input)))))

(defn dupl
  []
  (fn [rf]
    (fn
      ([] (rf))
      ([result] (rf result))
      ([result input]
         (rf (rf result input)
             input)))))

I have selected these as each illustrate something ‘interesting’ occurring 
in each of the three arities of the reducing functions produced.

Now consider the following transducer, which transforms numeric reducing 
functions.

(def xform (comp
            (map inc)
            (complete-with 10)
            (map inc)
            (init-with 100)
            (map inc)
            (dupl)))

The current behavior of transduce (and into, for illustration purposes) is:

(into [37] xform (range 5))
;=> [37 3 3 4 4 5 5 6 6 7 7 12 12]


(transduce xform + 37 (range 5))
;=> 111

(reduce + (into [37] xform (range 5)))
;=> 111

(transduce xform + (range 5))
;=> 74


So the dupl and complete-with transducers work as I expected, but the 
init-with does not.

What I was hoping for was:

;=> [37 101 101 3 3 4 4 5 5 6 6 7 7 12 12]

;=> 313

;=> 313

;=> 276


This is because transduce and into are currently implemented as something 
equivalent to the following:

(defn curr-transduce
  ([xform f coll]
     (transduce xform f (f) coll))
  ([xform f init coll]
     (let [rf (xform f)]
       (rf (reduce rf init coll)))))


(defn curr-into
  [to xform from]
  (curr-transduce xform conj to from))

In the first arity of transduce, the initial value is taken from the 
reducing function f, not the reducing function produced by (xform f).
And in the second arity, the supplied initial value is supplied directly to 
the reduction.
These both bypass the transducer xform entirely, so the zeroth-arity is 
never invoked and no transformation occurs.

I would like to propose the following alternative implementation:

(defn alt-transduce
  ([xform f coll]
     (let [rf (xform f)]
       (rf (reduce rf (rf) coll))))
  ([xform f init coll]
     (let [rf (xform
               (fn
                 ([] init)
                 ([result] (f result))
                 ([result input] (f result input))))]
       (rf (reduce rf (rf) coll)))))

In the first arity, the initial value is drawn from the transformed 
reducing function, rather than the supplied reducing function.
In the second arity, a reducing function is constructed from f and init to 
be f in arity 1 and 2, but return init in arity 0.
Both cases ensure that the transducer is used to transform all three 
components of the reduction: init, step, and completion.

Because of the way that the second arity is implemented, into doesn’t need 
to change:

(defn alt-into
  [to xform from]
  (alt-transduce xform conj to from))

The effect is that the reducing function passed to xform is conj, except in 
arity 0, where the `to` collection is returned.

These return the results I was originally expecting:

(alt-into [37] xform (range 5))
;=> [37 101 101 3 3 4 4 5 5 6 6 7 7 12 12]

(reduce + (alt-into [37] xform (range 5)))
;=> 313

(alt-transduce xform + 37 (range 5))
;=> 313

(alt-transduce xform + (range 5))
;=> 276


I went though the list of transducers currently provided in clojure.core 
and none of them do anything ‘interesting’ in zeroth-arity init component, 
so the alternate implementation would not change their observable behavior. 
This only affects transducers that do something other than the trivial 
implementation of the zero-arity. The discussion 
https://groups.google.com/d/topic/clojure/M-13lRPfguc/discussion was the 
only related one I could find.

Thanks,
Dan

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to