One thing that always made me a little sad about transducers was how map
lost its ability to iterate multiple collections in parallel. This is actually my favorite feature of map
. For example:
(map + (range 5) (range 5 10))
=> (5 7 9 11 13)
One somewhat practical use of this is if you want to compare two sequences, pairwise, using a comparator. Though I wish that every?
took multiple collections, this is an adequate substitute:
(every? true? (map < (range 5) (range 5 10)))
=> true
One somewhat esoteric use of this is to transpose a matrix:
(apply map vector [[1 2 3]
[4 5 6]])
=> [[1 4]
[2 5]
[3 6]]
However, transducers can only be applied to a single source...or can they?
I noticed that map
's transducer takes a fourth arity with a variable number of items. This really surprised me since as far as I could remember there were no transducing contexts that would actually work with multiple sources...or where there?
I looked through transduce
, eduction
, into
, and sequence
and found that sequnece
can take multiple collections.
Neither map
's fourth arity nor sequence
taking multiple collections is mentioned in any of the transducer documentation. The only hint is sequence
's docstring mentions multiple collections, but you'd have look at the source for map
to see the fourth arity.
map
is the only clojure.core
transducer with a fourth arity, and sequence
is the only clojure.core
transducer context to supply multiple sources, so this seems to be of limited use. However, as long as map
is the first stage of your transducer pipeline you can use other transducers with a multi-collection invocation of sequence
:
(sequence (comp (map +)
(filter even?))
(range 5)
(range 5 10)
(range 10 15))
=> (18 24)
This makes me happy!