Skip to content

Instantly share code, notes, and snippets.

@fogus
Forked from ptaoussanis/transducers.clj
Last active August 29, 2015 14:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fogus/1fa1ae879d30158c4e53 to your computer and use it in GitHub Desktop.
Save fogus/1fa1ae879d30158c4e53 to your computer and use it in GitHub Desktop.
;; Still haven't found a brief + approachable overview of Clojure 1.7's new Transducers
;; in the particular way I would have preferred myself - so here goes:
;;; Transducers recap
;; * (fn reducer-fn [] [accumulation] [accumulation next-input]) -> val [1].
;; * (fn transducer-fn [reducer-fn]) -> new-reducer-fn.
;; * So a transducer-fn is a reducer-fn middleware[2], and composes like it.
;; * All (numerous!) benefits[4] fall out of this simple +
;; not-particularly-impressive-looking[3] definition.
;;
;; [1] Exactly as per `reduce` docstring. Note 3 possible arities.
;; [2] Compare: (fn ring-handler-fn [ring-req]) -> ring-resp,
;; (fn ring-middleware-fn [ring-handler]) -> new-ring-handler.
;; [3] It's surprisingly easy to miss how big of a deal transducers are.
;; [4] Benefits include:
;; * Performance:
;; * Can reduce w/o incurring any sequence construction costs.
;; * Can map composed operations w/o incurring >1 sequence construction cost.
;; * Highly efficient filtering + early termination.
;; * All the (terrific) benefits of 'Reducers' (parallel fork-join, etc.).
;;
;; * Convenience:
;; * Easy composition through threading, standard fn `comp`, etc.
;; * Easy construction through single-arity `map`, `filter`, etc.
;; * Entirely eliminates need for special/extra core.async channel transform
;; fns (`map<`, etc.).
;; * Transducer-fns can use internal state to easily build powerful
;; operations (e.g. see `dedupe` source).
;; * Laziness iff you want it.
;; * A transducer is just a regular fn with a particular definition used in
;; a particular way + some supporting utils (notably `sequence` and
;; `transduce`). No complex voodoo, just a clever idea.
;;
;; * Conceptual:
;; * Elegantly & flexibly unifies concepts like:
;; * reducing (ordered-seq -> val),
;; * mapping (ordered-seq -> transformed-ordered-seq),
;; * Reducers (unordered-coll -> val),
;; * laziness,
;; * process composition.
;;;; Transducers API
;;; These all return transducers (remember the definition above!)
(map f)
(filter f)
(remove f)
(take n)
(take-while pred)
(drop n)
(drop-while pred)
(take-nth n)
(replace smap)
(into to xform from)
(partition-by f)
(partition-all f1)
(keep f)
(keep-indexed f)
(flatmap f) ; new, like mapcat
(dedupe f) ; new
(random-sample prob) ; new
;;; And these are utilities (note that they actually take colls)
(flatmap f coll) ; new, like mapcat (mapcat arity wasn't conducive to adding transducer support)
(iteration xform coll) ; new
(transduce xform f coll) ; new, like reduce
(transduce xform f init coll) ; new, like reduce
(sequence xform coll) ; like (lazy) single-coll `map`
(sequence xform & colls) ; like (lazy) multi-coll `map`
;;;; Finally, the identity transducer
(defn identity-transducer [reducer-fn]
(fn new-reducer-fn
;; Remember reducer-fns need 3 arities:
([] (reducer-fn))
([accumulation] accumulation)
([accumulation new-input]
(reducer-fn accumulation new-input))))
(comment (sequence identity-transducer '(1 2 3 4)) ; -> '(1 2 3 4)
)
;;; Homework
;; I found it handy to start with the identity transducer above then
;; comparing to (filter f), (take n), etc.
;; Hope this was useful to someone, cheers! :-)
;; Peter (https://github.com/ptaoussanis)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment