A quick tour of Clojure Transducers with core.async with Dr. Seuss as a guide.
Follow along at home by:
lein new green-eggs
- modify your project.clj to include the following:
1 2 3 4 5 6 7
- Start up a repl and hack in!
Green Eggs and Ham
Transducers are a new feature of Clojure 1.7. Instead of trying to explain them with words, let’s take a look of them in action. First we need some data. Let’s def a vector of all the places you could try green eggs and ham.
1 2 3 4 5 6 7 8 9 10 11 12
Next, let’s create a function that will transform the places into a “I would not eat them …” sentence.
1 2 3 4 5
We also need a function to take this result and actually try the green eggs and ham.
1 2 3 4 5
Now we have two transformations that we can apply to the vector of green-eggs-n-ham strings. One of the really nice things about transducers is that you can describe and compose this transformation without a data structure present.
1 2 3 4
We can run the transformation of the transducers against the data in a few ways.
- into: Non-lazy turn the transformation into a collection
- sequence: Same thing but lazy
- transduce: Acts like reduce on the all the transformed elements
- With core.async channels doing the transformations.
Let’s look at the green eggs and ham example for each one of these ways:
Into takes a transducer and collection to work on and returns the vector we asked for:
1 2 3 4 5 6 7 8 9
Sequence takes similar arguments, but as promised, returns a lazy sequence that we can interact with.
1 2 3 4 5
If we want to finally arrange all our sentences in the vectors into one string, we would use reduce. The way to do this with transducers is to use transduce. It takes a function of two arguments to perform the reduce, as well as an initial data input.
1 2 3 4 5 6 7 8 9 10 11 12 13
Core.async has a really nice way to define channels with a transducer that will transform each element on the channel.
Let’s define another channel to reduce the results of the sam-i-am-chan to a string.
Finally, let’s actually put the green-eggs-n-ham data onto the sam-i-am-chan and let the data transformations flow….
At last, we can get our result off the result channel and revel in the beauty of asynchronous data transducers.
1 2 3 4 5 6 7 8 9 10 11
Transducers are elegant and powerful, just like the rest of Clojure. Try them, you will like them :)