Clojure Koans 26 Transducers Notebook
Original Clojure Koans: https://github.com/functional-koans/clojure-koans/
Please note there are spoilers for the Clojure Koans below.
{:deps {org.clojure/clojure {:mvn/version "1.10.1"}
;; complient is used for autocompletion
;; add your libs here (and restart the runtime to pick up changes)
compliment/compliment {:mvn/version "0.3.9"}}}
{:hello (clojure-version)}
Initial Thoughts
Transducers are, off hand, a lot like macros and multi-methods for me. They seem to be useful and interesting, and also quite different than what I'm used to using in other languages. I hope that, by the end of this Koans sections, I can understand and even enjoy using transducers.
(def example-transducer
(map inc))
;; skill 26001: Create a simple transducer
(def transforms
(comp (map inc)
(filter even?)))
;; skill 26002: Combine sequence operations together using `comp` function to be composed as a transducer
;; question 26015: Is `transforms` above a transducer and if yes why? If it is not a transducer, then what is `transforms`?
;; "A sequence operation with only one argument often returns a transducer"
(= [2 3 4]
(sequence example-transducer [1 2 3]))
;; question 26001: What exactly is happening here?
;; question 26002: Where is the transducer being returned here?
;; question 26003: What are the differences between `(sequence example-transducer [1 2 3])` and `(map inc [1 2 3])`?
;; question 26004: Why does the text above say "sequence operation with only one argument" when the above `sequence` operation seems to take two arugments (`example-transducer` and the vector [1 2 3])?
;; skill 26003: Use a simple transducer via the `sequence` function (?)
;; question 26005: What does the `sequence` function do?
;; answer 26005a: "Coerces coll to a (possibly empty) sequence, if it is not already
;; one. Will not force a lazy seq. (sequence nil) yields (), When a
;; transducer is supplied, returns a lazy sequence of applications of
;; the transform to the items in coll(s), i.e. to the set of first
;; items of each coll, followed by the set of second
;; items in each coll, until any one of the colls is exhausted. Any
;; remaining items in other colls are ignored. The transform should accept
;; number-of-colls arguments"
;; question 26006: What is an "xform"? See docs: https://clojuredocs.org/clojure.core/sequence
;; answer 26006a: "A transducer (sometimes referred to as xform or xf) is a transformation from one reducing function to another" source: https://clojure.org/reference/transducers
;; question 26007: What are the differences between the `seq` and `sequence` functions? See https://clojuredocs.org/clojure.core/sequence and https://clojuredocs.org/clojure.core/seq
;; "Consider that sequence operations can be composed as transducers"
(= [2 4]
(transduce transforms conj [1 2 3]))
;; description: TBD...
;; question 26008: What exactly does the `transduce` function do?
;; question 26009: What exactly is the `conj` function doing here?
;; question 26010: What exactly is a "transducer" in plain words (eli5)?
;; question 26011: What does it mean to compose sequence operations as transducers?
;; skill 26004: Compose sequence operations as a transducer & then use the transducer with the `transduce` function (?)
;; "We can do this eagerly"
(= [2 4]
(into [] transforms [1 2 3]))
;; question 26012: What exactly is happening here?
;; skill 26005: Eagerly apply (?) a transducer over a sequence into a new seuence
;; "Or lazily"
(= [2 4]
(sequence transforms [1 2 3]))
;; question 26013: What exactly is happening here?
;; skill 26006: Lazily apply (?) a transducer over a sequence into a new seuence
;; "The transduce function can combine mapping and reduction"
(= 6
(transduce transforms + [1 2 3]))
;; question 26014: What exacty is happening here?
;; skill 26007: Apply both a mapping over a sequence as well as a reduction via a transducer composition of a map function and a reduce function
Closing Thoughts
I think I am starting to understand the "essence" of transducers, at least conceptually. When it comes to making transducers, using transducers, identifying what is a transducer, and identifying what isn't a transducer, I still believe I have a ways to go.
To-Do's
Read through reference on transducers to answer questions in this notebook: https://clojure.org/reference/transducers
Add demos for "The Seq library" from https://clojure.org/reference/sequences
Confirm there is a skill to make a transducer
Confirm there is a skill to use a transducer
Confirm that there are clear examples of simple transducers
Confirm that there are examples of intermediate complexity transducers
Confirm that there are clear descriptions for the various kinds of transducers
Reference the "essence" of this summary: https://rakhim.org/summary-of-transducers-a-talk-by-rich-hickey/ !!!
Reference awesome side-by-side example from here: https://stackoverflow.com/questions/26317325/can-someone-explain-clojure-transducers-to-me-in-simple-terms
Practice my understanding here: http://ianrumford.github.io/clojure/transducers/reducers/2014/08/08/Some-trivial-examples-of-using-Clojure-Transducers.html
Read awesome looking essay on transducers to see if conceptually you are understanding more: https://bendyworks.com/blog/transducers-clojures-next-big-idea
;; reference: https://clojure.org/reference/transducers
(filter odd?) ;; returns a transducer that filters odd
(map inc) ;; returns a mapping transducer for incrementing
(take 5) ;; returns a transducer that will take the first 5 values
;; reference: https://clojure.org/reference/transducers
;; The transducer xf is a transformation stack that will be applied by a process to a series of input elements.
(def xf
(comp
(filter odd?)
(map inc)
(take 5)))
;; The transformation above is equivalent to the sequence transformation:
(defn seq-transform [col]
(->> coll
(filter odd?)
(map inc)
(take 5)))
The following functions produce a transducer when the input collection is omitted: map cat mapcat filter remove take take-while take-nth drop drop-while replace partition-by partition-all keep keep-indexed map-indexed distinct interpose dedupe random-sample (source: https://clojure.org/reference/transducers)