Instantly share code, notes, and snippets.

# yang-wei/README.md Last active Aug 14, 2016

What's so great about transducer ?

This is not a introduction post to Clojure transducer. Instead there are a lots of great introduction post out there. This post aims to clarify the greatness of transduer. In the introduction blog, this is how they describe:

Transducers are composable algorithmic transformations. They are independent from the context of their input and output sources and specify only the essence of the transformation in terms of an individual element. Because transducers are decoupled from input or output sources, they can be used in many different processes - collections, streams, channels, observables, etc. Transducers compose directly, without awareness of input or creation of intermediate aggregates.

For me transducer does 2 great things

• Parallel process
• Independence input source, therefore reuseable

### Parallel process

Think about typical mapping. Let's say we have a list of number which represent our company payout.

```(def January-payout
[10, 5, 15, 20, 4])```

Before payout, we also need to include a few taxes.

```(defn payout-with-tax
[xs]
(->>
xs

Normally, you won't do this because it loop 2 times. Instead we will either

```(def add-all-taxes (comp add-goverment-tax))
(defn payout-with-tax
[xs]
(->>
xs

Or,

```(defn payout-with-tax
[xs]
(->>
xs

We make sure we don't do extra loop. Then what if your company just decide to not transfer the payouts which is under 10. Easy,

```(defn great-payout-with-tax
[xs]
(->>
xs
(filter #(> % 10))

Now we have 2 loops. What if I tell you we can make it into 1 loop with transducer ? In Clojure if you pass in one argument to `map` or `filter`, they return a transduer. (You can check the source of map with `(source map)`)

```(defn add-all-taxes
[x]
(* x 1.08))

(defn great-payout?
[x]
(> x 10))

;; our transducer
(def payoutForm
(comp
(filter great-payout?) ;; <-- a transducer
(map add-all-taxes)))  ;; <-- also a transducer

(defn great-payout-with-tax
[xs]
(->>
xs
(into [] payoutForm)))

;; or
(defn great-payout-with-tax
[xs]
(into [] payoutForm xs))```

By now, we have 1 loop back =) We used `into []` to build our `payoutForm` transformation into a collection but as you can see, our `payoutForm` transducer doesn't really care about it. That's what I mean independence input source.

### Independence input source

Basically, our `payoutForm` doesn't care what is the input source and how we process the final output. In the example above, our input came from `January-payout` which is a normal list while our output is another list built up from `into []`.

We can also process lazy-seq.

```(def lazy-January-payout
(lazy-seq January-payout))

(take 1 (sequence payoutForm lazy-January-payout))```

The possibility is infinite. Channel can also be the input:

```(let [chan (async/chan 1 payoutForm)]
(async/take! chan println)
(async/put! chan 15))

;; 16.200000000000003```

### Composable algorithm

Our `payoutForm` can be reused to do other thing, like summarizing the total payout of month.

```(reduce (payoutForm +) 0 January-payout)
;; 37.800000000000004
;; don't forget Clojure has a transduce function
(transduce payoutForm + 0 January-payout)
;; 37.800000000000004```

Or we can build up another process to sum up many payout:

```(def January-payout
[10, 5, 15, 20, 4])

(def February-payout
[100, 50, 5, 7, 40])

(def payoutForm
(comp
(filter great-payout?)

(def payoutsForm
(comp
(mapcat identity)
payoutForm))

(transduce payoutsForm + 0 [January-payout February-payout])
;; 243.0```

Tada ! Building up example like this is totally easy but in real life is transducer really will be useful ? Indeed. Hopefully I can explore a more real world like example in my side project.

Reference: