Skip to content

Instantly share code, notes, and snippets.

@laem
Created July 1, 2015 12:54
Show Gist options
  • Save laem/45a05c8030f6873d1b64 to your computer and use it in GitHub Desktop.
Save laem/45a05c8030f6873d1b64 to your computer and use it in GitHub Desktop.
Clojure transducers
https://news.ycombinator.com/item?id=9808021
No, reducers are about reducing things. Transducers are about creating reducers that can be used on arbitrary collection-like things.
Reducers come from the observation that many higher order operations on collections can be built on reduce. You just supply the right reducing function and you get the equivalent to 'map' or 'filter'. This is useful because when you start chaining these higher order functions you can gain performance be replacing the multiple function calls with a single reducer based function.
To take the example from the main docs:
(fold + (filter even? (map inc [1 1 1 2])))
Here, each function returns a reducer which is a combination of the original collection ([1 1 1 2]) and a reducing function which will be applied to the collection. Ultimately this code will result in a single call to reduce with a single function applied to [1 1 1 2]. This differs from the 'standard' way of doing this:
(reduce + (filter even? (map inc [1 1 1 2])))
...in that no intermediary representations of the collection are necessary. Neat.
A transducer takes the same idea, but does it in a way that lets you apply this to arbitrary collection-like things, not just seqs. A transducer works by cutting out the original collection; you build the reducing function by chaining transducers together and pass it later another function which will pick apart the collection-like thing.
So:
(into [] (r/filter even? (r/map inc [1 1 1 2])))
Becomes
(into [] (comp (filter even?) (map inc)) [1 1 1 2])
Which is not exciting until you realize that that middle term can be passed to 'chan' al la:
(chan 1 (comp (filter even?) (map inc)))
Which means that everything going through that channel will increased by filtered with 'even?'. Now you have a suite of functions which will take a transducer and use it in a lot of different contexts allowing you use the same logic on streams, sequences, channels etc. This same logic, that you would originally have expressed with a series calls to 'map' 'filter' 'take' and applied only to a sequence.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment