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"}}}
deps.edn
Extensible Data Notation
{:hello (clojure-version)}
0.1s
Clojure

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
Clojure
(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`?
Clojure
;; "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
Clojure
;; "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 (?)
Clojure
;; "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
Clojure
;; "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
Clojure
;; "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
Clojure

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

;; 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
Clojure
;; 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)))
Clojure

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)

Runtimes (1)