<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Christoph Frick - ofnir.net - Open Source Consultant, Software Developer and System Administrator (Posts about Transducer)</title><link>https://ofnir.net/</link><description></description><atom:link href="https://ofnir.net/categories/transducer.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2026 &lt;a href="mailto:cf@ofnir.net"&gt;Christoph Frick - ofnir.net&lt;/a&gt; </copyright><lastBuildDate>Tue, 21 Apr 2026 11:44:46 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Turning paging into a lazy sequence with Clojure tranducers</title><link>https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/</link><dc:creator>Christoph Frick - ofnir.net</dc:creator><description>&lt;p&gt;When dealing with APIs we are often confronted with some sort of paging to
access lists.  Yet if we need to access all of the data, it would be nice to
have them as a lazy sequence.&lt;/p&gt;
&lt;p&gt;This can be done nicely with transducers:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code clojure"&gt;&lt;a id="rest_code_9de36388cda944e3b17b7d23594fc916-1" name="rest_code_9de36388cda944e3b17b7d23594fc916-1" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_9de36388cda944e3b17b7d23594fc916-1"&gt;&lt;/a&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;page-seq&lt;/span&gt;
&lt;a id="rest_code_9de36388cda944e3b17b7d23594fc916-2" name="rest_code_9de36388cda944e3b17b7d23594fc916-2" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_9de36388cda944e3b17b7d23594fc916-2"&gt;&lt;/a&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;"Flat sequence of items loaded via the given page loading fn"&lt;/span&gt;
&lt;a id="rest_code_9de36388cda944e3b17b7d23594fc916-3" name="rest_code_9de36388cda944e3b17b7d23594fc916-3" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_9de36388cda944e3b17b7d23594fc916-3"&gt;&lt;/a&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;get-page-fn&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;a id="rest_code_9de36388cda944e3b17b7d23594fc916-4" name="rest_code_9de36388cda944e3b17b7d23594fc916-4" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_9de36388cda944e3b17b7d23594fc916-4"&gt;&lt;/a&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;eduction&lt;/span&gt;
&lt;a id="rest_code_9de36388cda944e3b17b7d23594fc916-5" name="rest_code_9de36388cda944e3b17b7d23594fc916-5" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_9de36388cda944e3b17b7d23594fc916-5"&gt;&lt;/a&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;map &lt;/span&gt;&lt;span class="nv"&gt;get-page-fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_9de36388cda944e3b17b7d23594fc916-6" name="rest_code_9de36388cda944e3b17b7d23594fc916-6" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_9de36388cda944e3b17b7d23594fc916-6"&gt;&lt;/a&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;take-while &lt;/span&gt;&lt;span class="nv"&gt;seq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_9de36388cda944e3b17b7d23594fc916-7" name="rest_code_9de36388cda944e3b17b7d23594fc916-7" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_9de36388cda944e3b17b7d23594fc916-7"&gt;&lt;/a&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nv"&gt;cat&lt;/span&gt;
&lt;a id="rest_code_9de36388cda944e3b17b7d23594fc916-8" name="rest_code_9de36388cda944e3b17b7d23594fc916-8" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_9de36388cda944e3b17b7d23594fc916-8"&gt;&lt;/a&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Add some tests:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code clojure"&gt;&lt;a id="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-1" name="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-1" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_d55a19d8f8314bcb8a852af1a768bdf6-1"&gt;&lt;/a&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def &lt;/span&gt;&lt;span class="nv"&gt;pages&lt;/span&gt;
&lt;a id="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-2" name="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-2" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_d55a19d8f8314bcb8a852af1a768bdf6-2"&gt;&lt;/a&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;into &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;partition-all&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range &lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;a id="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-3" name="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-3" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_d55a19d8f8314bcb8a852af1a768bdf6-3"&gt;&lt;/a&gt;
&lt;a id="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-4" name="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-4" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_d55a19d8f8314bcb8a852af1a768bdf6-4"&gt;&lt;/a&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;get-page&lt;/span&gt;
&lt;a id="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-5" name="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-5" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_d55a19d8f8314bcb8a852af1a768bdf6-5"&gt;&lt;/a&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;page&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;a id="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-6" name="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-6" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_d55a19d8f8314bcb8a852af1a768bdf6-6"&gt;&lt;/a&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;println &lt;/span&gt;&lt;span class="s"&gt;"Loading page"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-7" name="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-7" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_d55a19d8f8314bcb8a852af1a768bdf6-7"&gt;&lt;/a&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;nth &lt;/span&gt;&lt;span class="nv"&gt;pages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;page&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
&lt;a id="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-8" name="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-8" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_d55a19d8f8314bcb8a852af1a768bdf6-8"&gt;&lt;/a&gt;
&lt;a id="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-9" name="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-9" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_d55a19d8f8314bcb8a852af1a768bdf6-9"&gt;&lt;/a&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;page-seq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;get-page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-10" name="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-10" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_d55a19d8f8314bcb8a852af1a768bdf6-10"&gt;&lt;/a&gt;&lt;span class="c1"&gt;;Loading page 0&lt;/span&gt;
&lt;a id="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-11" name="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-11" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_d55a19d8f8314bcb8a852af1a768bdf6-11"&gt;&lt;/a&gt;&lt;span class="c1"&gt;;Loading page 1&lt;/span&gt;
&lt;a id="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-12" name="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-12" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_d55a19d8f8314bcb8a852af1a768bdf6-12"&gt;&lt;/a&gt;&lt;span class="c1"&gt;;Loading page 2&lt;/span&gt;
&lt;a id="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-13" name="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-13" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_d55a19d8f8314bcb8a852af1a768bdf6-13"&gt;&lt;/a&gt;&lt;span class="c1"&gt;;Loading page 3&lt;/span&gt;
&lt;a id="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-14" name="rest_code_d55a19d8f8314bcb8a852af1a768bdf6-14" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_d55a19d8f8314bcb8a852af1a768bdf6-14"&gt;&lt;/a&gt;&lt;span class="c1"&gt;; =&amp;gt; (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ... 54 55)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now see, if that is really lazy:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code clojure"&gt;&lt;a id="rest_code_52f009a1f5714bfb87f90b7d2f3341dc-1" name="rest_code_52f009a1f5714bfb87f90b7d2f3341dc-1" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_52f009a1f5714bfb87f90b7d2f3341dc-1"&gt;&lt;/a&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;take &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;page-seq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;get-page&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a id="rest_code_52f009a1f5714bfb87f90b7d2f3341dc-2" name="rest_code_52f009a1f5714bfb87f90b7d2f3341dc-2" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_52f009a1f5714bfb87f90b7d2f3341dc-2"&gt;&lt;/a&gt;&lt;span class="c1"&gt;;Loading page 0&lt;/span&gt;
&lt;a id="rest_code_52f009a1f5714bfb87f90b7d2f3341dc-3" name="rest_code_52f009a1f5714bfb87f90b7d2f3341dc-3" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_52f009a1f5714bfb87f90b7d2f3341dc-3"&gt;&lt;/a&gt;&lt;span class="c1"&gt;;Loading page 1&lt;/span&gt;
&lt;a id="rest_code_52f009a1f5714bfb87f90b7d2f3341dc-4" name="rest_code_52f009a1f5714bfb87f90b7d2f3341dc-4" href="https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/#rest_code_52f009a1f5714bfb87f90b7d2f3341dc-4"&gt;&lt;/a&gt;&lt;span class="c1"&gt;; =&amp;gt; (0 1 2 3 4 5 6 7 8 9)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Well, no.  We should only see &lt;code class="docutils literal"&gt;Loading page 0&lt;/code&gt; and not the one for &lt;code class="docutils literal"&gt;page
1&lt;/code&gt;.  The reason for this is &lt;em&gt;chunking&lt;/em&gt;.  Clojure internally loads itself
chunks of &lt;em&gt;32&lt;/em&gt;.  So side effects will be problematic.  But for just lazily
loading the whole lot having some mismatch in the used paging and the chunking
is good enough for now.&lt;/p&gt;</description><category>Clojure</category><category>Transducer</category><guid>https://ofnir.net/posts/turning-paging-into-a-lazy-sequence-with-clojure-tranducers/</guid><pubDate>Thu, 05 Oct 2017 22:00:00 GMT</pubDate></item></channel></rss>