"Mastering Clojure Concepts: Advanced Flashcards"
Table of Contents
- Advanced Clojure Flashcards
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:
- Use macros sparingly; prefer functions when possible
- Follow the "code as data" principle
- Use syntax-quote (`) for template creation
- Use gensyms to avoid symbol capture
- 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:
- You need to perform many updates to a collection
- The intermediate states are not observed
- 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:
- Lazy vs. Eager: Reducers are eager, while regular sequence operations are lazy
- Fusion: Reducers fuse multiple operations into a single pass
- Parallelism: Reducers can automatically parallelize operations on supported collections
- 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:
- Data validation
- Generative testing
- Error reporting
- Runtime type checking
- Documentation
- 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:
- Documentation (e.g., docstrings)
- Type hints for performance optimization
- Deprecation warnings
- Test metadata
- 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}