Clojure: Transducers

Hacker News Top Tools

Summary

Official Clojure documentation explains transducers—composable, stateful transformation functions that decouple sequence processing from specific collection types.

No content available
Original Article
View Cached Full Text

Cached at: 04/21/26, 04:01 PM

# Clojure - Transducers Source: [https://clojure.org/reference/transducers](https://clojure.org/reference/transducers) Transducers have the following shape \(custom code in "…​"\): ``` (fn [rf] (fn ([] ...) ([result] ...) ([result input] ...))) ``` Many of the core sequence functions \(like map, filter, etc\) take operation\-specific arguments \(a predicate, function, count, etc\) and return a transducer of this shape closing over those arguments\. In some cases, like**cat**, the core function*is*a transducer function and does not take an**rf**\. The inner function is defined with 3 arities used for different purposes: - **Init**\(arity 0\) \- should call the init arity on the nested transform**rf**, which will eventually call out to the transducing process\. - **Step**\(arity 2\) \- this is a standard reduction function but it is expected to call the**rf**step arity 0 or more times as appropriate in the transducer\. For example, filter will choose \(based on the predicate\) whether to call**rf**or not\. map will always call it exactly once\. cat may call it many times depending on the inputs\. - **Completion**\(arity 1\) \- some processes will not end, but for those that do \(like**transduce**\), the completion arity is used to produce a final value and/or flush state\. This arity must call the**rf**completion arity exactly once\. An example use of**completion**is**partition\-all**, which must flush any remaining elements at the end of the input\. The[completing](https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/completing)function can be used to convert a reducing function to a transducing function by adding a default completion arity\. ### [https://clojure.org/reference/transducers#_early_termination](https://clojure.org/reference/transducers#_early_termination)Early termination Clojure has a mechanism for specifying early termination of a reduce: - [reduced](https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/reduced)\- takes a value and returns a*reduced*value indicating reduction should stop - [reduced?](https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/reduced?)\- returns true if the value was created with*reduced* - [deref](https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/deref)or @ can be used to retrieve the value inside a*reduced* A process that uses transducers must check for and stop when the step function returns a reduced value \(more on that in Creating Transducible Processes\)\. Additionally, a transducer step function that uses a nested reduce must check for and convey reduced values when they are encountered\. \(See the implementation of cat for an example\.\) ### [https://clojure.org/reference/transducers#_transducers_with_reduction_state](https://clojure.org/reference/transducers#_transducers_with_reduction_state)Transducers with reduction state Some transducers \(such as**take**,**partition\-all**, etc\) require state during the reduction process\. This state is created each time the transducible process applies the transducer\. For example, consider the dedupe transducer that collapses a series of duplicate values into a single value\. This transducer must remember the previous value to determine whether the current value should be passed on: ``` (defn dedupe [] (fn [xf] (let [prev (volatile! ::none)] (fn ([] (xf)) ([result] (xf result)) ([result input] (let [prior @prev] (vreset! prev input) (if (= prior input) result (xf result input)))))))) ``` In dedupe,**prev**is a stateful container that stores the previous value during the reduction\. The prev value is a volatile for performance, but it could also be an atom\. The prev value will not be initialized until the transducing process starts \(in a call to**transduce**for example\)\. The stateful interactions are therefore contained within the context of the transducible process\. In the completion step, a transducer with reduction state should flush state prior to calling the nested transformer’s completion function, unless it has previously seen a reduced value from the nested step in which case pending state should be discarded\.

Similar Articles

Dependently typed Clojure DSL with a Lean4 compatible kernel

Lobsters Hottest

Ansatz is a Clojure library that implements Lean 4's Calculus of Inductive Constructions kernel, enabling Clojure programmers to write dependently typed, verified code with proofs compatible with Mathlib and compile to JVM bytecode.

Clojure is almost as fast as C (with some help)

Lobsters Hottest

This article details how Clojure, with the JVM's Vector API and careful optimization, achieved frame rates within 20% of C for a 3D stress test, demonstrating that a dynamic language can approach low-level performance on hot loops.

The Transformer Pill

Reddit r/ArtificialInteligence

A reflection on the broad implications of transformer architectures beyond LLMs, including potential impacts on linguistics, genetics, and causal modeling, comparing their significance to the Haber-Bosch process.