"Mastering Clojure Concepts: Advanced Flashcards"

Table of Contents

Advanced Clojure Flashcards

Transducers   drill advanced_clojure

Front

What are transducers in Clojure and how do they differ from regular sequence operations?

Back

Transducers are composable algorithmic transformations that are independent of their input and output sources. They are decoupled from the process of traversal and accumulation, allowing for more efficient and reusable transformations across various contexts (sequences, channels, observables, etc.).

Example of using a transducer:

(def xf (comp (filter even?) (map #(* % 2))))
(transduce xf + (range 10))

Core.async   drill advanced_clojure

Front

Explain the purpose and basic usage of core.async in Clojure.

Back

clojure.core.async is a Clojure library for asynchronous programming using channels and go blocks. It provides a way to write asynchronous code that looks and behaves like synchronous code, making it easier to reason about and maintain.

Key concepts:

  • Channels (chan): for communicating between processes
  • go blocks: for running concurrent processes
  • <! and >!: for taking from and putting onto channels within go blocks
  • <!! and >!!: for taking from and putting onto channels outside go blocks

Example:

(require '[clojure.core.async :as async :refer [chan go >! <!]])

(let [c (chan)]
  (go (>! c "Hello"))
  (go (println (<! c))))

Protocols and Multimethods   drill advanced_clojure

Front

Compare and contrast protocols and multimethods in Clojure.

Back

Protocols:

  • Define a set of abstract operations
  • Dispatch on a single argument (the type of the first argument)
  • More performant than multimethods
  • Cannot be modified after definition

Multimethods:

  • Allow for multiple dispatch (can dispatch on multiple arguments)
  • More flexible dispatch options (can use any function for dispatch)
  • Can be extended after definition
  • Slightly less performant than protocols

Examples:

Protocol:

(defprotocol Drawable
  (draw [this]))

(defrecord Circle [radius]
  Drawable
  (draw [this] (str "Drawing a circle with radius " (:radius this))))

Multimethod:

(defmulti area :shape)
(defmethod area :rectangle [rect]
  (* (:width rect) (:height rect)))
(defmethod area :circle [circ]
  (* Math/PI (:radius circ) (:radius circ)))

Macros   drill advanced_clojure

Front

What are macros in Clojure, and what are some best practices for using them?

Back

Macros are functions that manipulate code as data, allowing for powerful metaprogramming capabilities. They run at compile-time and can generate or transform code.

Best practices:

  1. Use macros sparingly; prefer functions when possible
  2. Follow the "code as data" principle
  3. Use syntax-quote (`) for template creation
  4. Use gensyms to avoid symbol capture
  5. Implement a "macro-writing macro" for repetitive macro patterns

Example macro:

(defmacro unless [test & body]
  `(if (not ~test)
     (do ~@body)))

(unless false
  (println "This will be printed"))

Transients   drill advanced_clojure

Front

What are transients in Clojure and when should they be used?

Back

Transients are mutable versions of Clojure's persistent data structures that can be used for performance optimization in specific scenarios.

Key points:

  • Created with (transient coll)
  • Modified with assoc!, conj!, dissoc!
  • Converted back to persistent with (persistent! tcoll)
  • Should only be used within a single thread
  • Useful for batch updates to large collections

Use transients when:

  1. You need to perform many updates to a collection
  2. The intermediate states are not observed
  3. The performance gain is significant and necessary

Example:

(defn vector-of-squares [n]
  (persistent!
   (loop [i 0
          v (transient [])]
     (if (< i n)
       (recur (inc i) (conj! v (* i i)))
       v))))

Reducers   drill advanced_clojure

Front

Explain the concept of reducers in Clojure and how they differ from regular sequence operations.

Back

Reducers are an alternative approach to sequence processing in Clojure that can offer better performance for certain operations.

Key differences:

  1. Lazy vs. Eager: Reducers are eager, while regular sequence operations are lazy
  2. Fusion: Reducers fuse multiple operations into a single pass
  3. Parallelism: Reducers can automatically parallelize operations on supported collections
  4. Performance: Reducers can be more efficient for large collections or complex transformations

Use reducers when processing large collections with multiple transformations and when laziness is not required.

Example:

(require '[clojure.core.reducers :as r])

(def numbers (vec (range 1000000)))

(time (->> numbers
           (filter even?)
           (map #(* % 2))
           (reduce +)))

(time (->> numbers
           (r/filter even?)
           (r/map #(* % 2))
           (r/fold +)))

Clojure.spec   drill advanced_clojure

Front

What is clojure.spec and what are its main use cases?

Back

clojure.spec.alpha is a library for describing the structure of data and functions in Clojure. It provides a way to define specifications for data shapes and validate them.

Main use cases:

  1. Data validation
  2. Generative testing
  3. Error reporting
  4. Runtime type checking
  5. Documentation
  6. Destructuring

Key functions:

  • s/def: Define a spec
  • s/valid?: Check if data conforms to a spec
  • s/explain: Provide a human-readable explanation of spec violations
  • s/gen: Generate sample data that conforms to a spec

Example:

(require '[clojure.spec.alpha :as s])

(s/def ::age (s/and int? #(>= % 0)))
(s/def ::name string?)
(s/def ::person (s/keys :req [::name ::age]))

(s/valid? ::person {::name "Alice" ::age 30})  ; true
(s/explain ::person {::name "Bob" ::age -5})   ; age fails spec

Metadata   drill advanced_clojure

Front

What is metadata in Clojure and how is it used?

Back

Metadata is data about data in Clojure. It allows attaching additional information to objects without affecting their value.

Key points:

  • Added with (with-meta obj metadata) or ^metadata obj syntax
  • Retrieved with (meta obj)
  • Does not affect equality comparisons
  • Can be used on symbols, collections, and functions

Common uses:

  1. Documentation (e.g., docstrings)
  2. Type hints for performance optimization
  3. Deprecation warnings
  4. Test metadata
  5. Custom annotations for libraries or frameworks

Example:

(def numbers ^{:created-by "Alice"} [1 2 3 4 5])

(meta numbers)  ; {:created-by "Alice"}

(defn ^:deprecated old-function []
  (println "This function is deprecated"))

(meta #'old-function)  ; {:deprecated true}

Author: Jason Walsh

j@wal.sh

Last Updated: 2024-08-14 06:08:49