-
-
Save fogus/1fa1ae879d30158c4e53 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; 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