An F# pipeline usually names both the transformation and its collection context:
Table of contents
On this page
Separate the Transformation
An F# function can make the Seq pipeline reusable. Clojure’s one-argument forms of filter and map instead return a transformation independent of any collection API:
let paidTotals orders =
orders
|> Seq.filter (fun order -> order.Paid)
|> Seq.map (fun order -> order.Total)F#
(def paid-totals
(comp
(filter :paid?)
(map :total)))Clojure
Use transduce to transform and reduce without creating intermediate sequences:
orders |> paidTotals |> Seq.sum
// 55F#
(transduce paid-totals + 0 orders)
;; => 55Clojure
Use the same transducer with into when the result should be a collection:
orders |> paidTotals |> Seq.toList
// [24; 31]F#
(into [] paid-totals orders)
;; => [24 31]Clojure
Transducers composed with comp read in data-flow order here: filter paid orders, then extract each total.
Produce a Sequence When Needed
sequence applies a transducer and exposes a lazy sequence:
paidTotals orders
// seq [24; 31]F#
(sequence paid-totals orders)
;; => (24 31)Clojure
This is useful when downstream code expects sequence operations but the transformation itself should remain reusable.
Stop an Unbounded Input
Some transducers, including take, can signal that reduction is complete:
Seq.initInfinite id
|> Seq.filter (fun value -> value % 2 <> 0)
|> Seq.map (fun value -> value * value)
|> Seq.take 3
|> Seq.toList
// [1; 9; 25]F#
(def first-three-odd-squares
(comp
(filter odd?)
(map #(* % %))
(take 3)))
(into [] first-three-odd-squares (range))
;; => [1 9 25]Clojure
Although (range) is unbounded, take stops the reduction after three accepted values.
When They Earn Their Keep
Use a transducer when a transformation is reused with different sources or destinations, when intermediate allocations matter, or when an API already accepts one. For a single short pipeline over a normal collection, ->> with sequence functions is usually easier to read.