Skip to content

Instantly share code, notes, and snippets.

@pjstadig
Last active June 8, 2021 13:22
Show Gist options
  • Star 25 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save pjstadig/c27366b1cba7a820d07ffdc957fc86c0 to your computer and use it in GitHub Desktop.
Save pjstadig/c27366b1cba7a820d07ffdc957fc86c0 to your computer and use it in GitHub Desktop.
The secret feature of transducers that no one talks about!

The Pledge

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

The Turn

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.

The Prestige

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment