100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Sequences in Clojure

Understand Clojure's sequence abstraction — the uniform interface of first, rest, and cons that lets the same functions work across lists, vectors, maps, and lazy, potentially infinite streams.

Core ConceptsIntermediate10 min readJul 10, 2026
Analogies

The Sequence Abstraction

Clojure defines a uniform abstraction called a sequence, or seq, that any collection can be viewed through. A seq supports three core operations: first returns the first element, rest returns a seq of the remaining elements, and cons prepends an element to produce a new seq. Because vectors, lists, maps, sets, and even strings can all be converted to a seq via the seq function, the same handful of sequence functions work identically regardless of the underlying concrete type — (first {:a 1 :b 2}) returns a map entry like [:a 1], just as (first [1 2 3]) returns 1.

🏏

Cricket analogy: The seq abstraction is like the concept of 'the next ball' applying identically whether you're watching a Test match at Lord's, an ODI, or a T20 — first and rest let you process deliveries the same way regardless of the format underneath.

Laziness and Infinite Sequences

Many sequence-producing functions in Clojure are lazy: they don't compute their elements until those elements are actually needed. range without an upper bound, (range), produces an infinite lazy sequence of increasing integers, and functions like map and filter applied to it also stay lazy, only realizing values as they're consumed. This lets you write (take 5 (map inc (range))), which safely returns (1 2 3 4 5) even though (range) never terminates, because take only pulls the first five values it needs and no more. The lazy-seq macro is the primitive used to build custom lazy sequences by wrapping a computation so it runs only on first access.

🏏

Cricket analogy: Laziness is like a live ball-by-ball commentary feed at the Boxing Day Test that only generates commentary for the next ball when the broadcaster actually calls for it, rather than pre-scripting commentary for an entire never-ending match in advance.

Core Sequence-Processing Functions

A small set of functions built on the seq abstraction cover most everyday data processing: map transforms each element, filter keeps elements matching a predicate, reduce folds a sequence into a single accumulated value, take/drop slice a prefix or suffix, and sort-by orders elements by a derived key. These compose naturally: (->> [5 2 8 1 9] (filter odd?) (map inc) (reduce +)) filters to (5 1 9), increments each to (6 2 10), and sums them to 18, all expressed as a readable left-to-right pipeline using the ->> threading macro.

🏏

Cricket analogy: Chaining filter, map, and reduce is like a national selector first filtering players by 'batting average above 40,' then mapping each to their strike rate, then reducing to a single squad total strike rate — three clear steps in a readable pipeline.

Sequences vs Concrete Collections

It's important to distinguish between a sequence — the abstract, lazy, potentially infinite view — and the concrete collections like vectors and maps that seq functions operate over. Calling a seq function such as map or filter on a vector doesn't return another vector; it returns a lazy sequence. If you need a concrete collection type back, you must convert explicitly, for example with (vec (map inc [1 2 3])) to get a vector, or (into #{} (filter even? [1 2 3 4])) to collect results into a set. This distinction matters for performance and for code that depends on a specific concrete type downstream.

🏏

Cricket analogy: The difference between a seq and a concrete collection is like the difference between live ball-by-ball commentary text during a T20 (a flowing stream) and the final printed scorecard (a fixed, structured document) — you often need to explicitly compile the commentary stream into the scorecard format.

clojure
;; The seq abstraction works uniformly across types
(first [1 2 3])          ;=> 1
(first '(1 2 3))         ;=> 1
(first {:a 1 :b 2})      ;=> [:a 1]
(rest [1 2 3])           ;=> (2 3)

;; Lazy, infinite sequences
(take 5 (map inc (range)))          ;=> (1 2 3 4 5)
(take 3 (filter even? (range)))     ;=> (0 2 4)

;; Composing a pipeline with the ->> threading macro
(->> [5 2 8 1 9]
     (filter odd?)
     (map inc)
     (reduce +))
;=> 18

;; Converting a lazy seq back to a concrete collection
(vec (map inc [1 2 3]))             ;=> [2 3 4]
(into #{} (filter even? [1 2 3 4])) ;=> #{2 4}

The threading macro ->> inserts each preceding expression's result as the last argument of the next form, letting you write data-processing pipelines top-to-bottom in the order operations actually happen — filter, then map, then reduce — instead of nesting them inside-out.

Building an infinite lazy sequence like (range) and calling a non-lazy, fully-consuming function on it directly — such as (count (range)) or (reduce + (range)) — will never terminate. Always bound an infinite sequence with take or a predicate-based function like take-while before realizing it fully.

  • The seq abstraction provides three core operations — first, rest, and cons — that work uniformly across lists, vectors, maps, sets, and strings.
  • Calling seq on a collection produces a logical view; the same processing functions work regardless of the underlying concrete type.
  • Many sequence functions, including map, filter, and range (with no arguments), are lazy and only compute values as they're consumed.
  • take and take-while safely bound infinite lazy sequences by pulling only the elements actually needed.
  • The ->> threading macro lets you write filter/map/reduce pipelines in a readable, left-to-right order.
  • Sequence functions like map and filter return lazy sequences, not the original concrete collection type — use vec or into to convert back.
  • Fully realizing an infinite lazy sequence with functions like count or non-bounded reduce will hang the program.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#ClojureStudyNotes#SequencesInClojure#Sequences#Clojure#Sequence#Abstraction#StudyNotes#SkillVeris#ExamPrep