clojuretransducer

Can someone explain Clojure Transducers to me in Simple terms?


I have tried reading up on this but I still don't understand the value of them or what they replace. And do they make my code shorter, more understandable or what?

Update

A lot of people posted answers, but it would be nice to see examples of with and without transducers for something very simple, which even an idiot like me can understand. Unless of course transducers need a certain high level of understanding, in which case I will never understand them :(


Solution

  • Transducers are recipes of what to do with a sequence of data without knowledge of what the underlying sequence is (how to do it). It can be any seq, async channel or maybe observable.

    They are composable and polymorphic.

    The benefit is, you don't have to implement all standard combinators every time a new data source is added. Again and again. As a result, you as user are able to reuse those recipes on different data sources.

    Prior to version 1.7 of Clojure you had three ways to write dataflow queries:

    1. nested calls

      (reduce + (filter odd? (map #(+ 2 %) (range 0 10))))
      
    2. functional composition

      (def xform
        (comp
          (partial filter odd?)
          (partial map #(+ 2 %))))
      (reduce + (xform (range 0 10)))
      
    3. threading macro

      (defn xform [xs]
        (->> xs
             (map #(+ 2 %))
             (filter odd?)))
      (reduce + (xform (range 0 10)))
      

    With transducers you will write it like:

    (def xform
      (comp
        (map #(+ 2 %))
        (filter odd?)))
    (transduce xform + (range 0 10))
    

    They all do the same. The difference is that you never call transducers directly, you pass them to another function. Transducers know what to do, the function that gets a transducer knows how. The order of combinators is like you write it with threading macro (natural order). Now you can reuse xform with channel:

    (chan 1 xform)