<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Leo Orpilla III</title>
    <link href="https://ldgrp.me/atom.xml" rel="self" />
    <link href="https://ldgrp.me" />
    <id>https://ldgrp.me/atom.xml</id>
    <author>
        <name>Leo Orpilla III</name>
        
        <email>leo@ldgrp.me</email>
        
    </author>
    <updated>2023-08-19T00:00:00Z</updated>
    <entry>
    <title>Live-reloading Pandoc + Reveal.js</title>
    <link href="https://ldgrp.me/posts/pandoc-revealjs-livereload.html" />
    <id>https://ldgrp.me/posts/pandoc-revealjs-livereload.html</id>
    <published>2023-08-19T00:00:00Z</published>
    <updated>2023-08-19T00:00:00Z</updated>
    <summary type="html"><![CDATA[<article class="post">
    <section class="header mt-4 mb-6">
        <h1 class="mr-8 sm:mr-10">Live-reloading Pandoc + Reveal.js</h1>
        <div class="flex gap-4 -mt-3">
        <div class="text-sm font-mono text-gray-600 dark:text-gray-300">
            Posted on 19 Aug 2023
        </div>
        
        </div>
    </section>
    <section>
        <p>I’ve been using markdown + <a href="https://pandoc.org/">pandoc</a> + <a href="https://revealjs.com/">reveal.js</a> to create presentations at work.</p>
<p>Pandoc doesn’t have a live-reload feature so I wrote a small script that watches
for changes in the markdown file and recompiles the presentation.</p>
<h2 id="fswatch">fswatch</h2>
<p>I decided to use <a href="https://emcrisostomo.github.io/fswatch/">fswatch</a> to listen for changes in the markdown file.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ex">fswatch</span> <span class="at">-0</span> presentation.md <span class="kw">|</span> <span class="fu">xargs</span> <span class="at">-0</span> <span class="at">-I</span> {} sh <span class="at">-c</span> <span class="st">&quot;pandoc --to revealjs -s --output presentation.html {}&quot;</span></span></code></pre></div>
<ul>
<li><code>fswatch -0</code> watches for changes in the markdown file and outputs
the name of the file that changed followed by the null character.</li>
<li><code>xargs -0</code> reads null-terminated lines from stdin.</li>
<li><code>xargs -I {}</code> replaces <code>{}</code> in the following command with the input.</li>
<li><code>pandoc --to revealjs -s --output presentation.html {}</code> compiles the markdown file
to reveal.js.</li>
</ul>
<p>This gets us compile-on-save, but we still need to refresh the browser to see
the changes.</p>
<h2 id="websockets">Websockets</h2>
<p>We can extend the script to start a websocket server that sends a message to the
browser when the markdown file changes.</p>
<p>I used <a href="https://github.com/vi/websocat">websocat</a> to start the websocket server from the command line.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="ex">fswatch</span> <span class="at">-0</span> presentation.md <span class="kw">|</span> <span class="fu">xargs</span> <span class="at">-0</span> <span class="at">-I</span> {} sh <span class="at">-c</span> <span class="st">&quot;pandoc --to revealjs -s --output presentation.html {} &amp;&amp; echo reload&quot;</span> <span class="kw">|</span> <span class="ex">websocat</span> <span class="at">-s</span> 56789</span></code></pre></div>
<ul>
<li><code>echo reload</code> is sent to stdout after the markdown file is compiled.</li>
<li><code>websocat -s 56789</code> starts a websocket server on port 56789.</li>
</ul>
<p>The echo message is piped to websocat.</p>
<h3 id="browser">Browser</h3>
<p>Pandoc supports YAML frontmatter in markdown files. The <code>header-includes</code> field
can be used to include arbitrary HTML in the <code>&lt;head&gt;</code> tag of the generated HTML.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="pp">---</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="fu">title</span><span class="kw">:</span><span class="at"> My presentation</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="fu">header-includes</span><span class="kw">: </span><span class="ch">|</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>  &lt;script&gt;</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>    function connect() {</span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>      const ws = new WebSocket(&quot;ws://localhost:56789&quot;);</span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>      ws.onopen = () =&gt; setTimeout(() =&gt; ws.send(&quot;keepalive&quot;), 30000);</span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>      ws.onclose = () =&gt; setTimeout(connect, 1000);</span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a>      ws.onmessage = () =&gt; location.reload();</span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>    }</span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a>    connect();</span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a>  &lt;/script&gt;</span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a><span class="pp">---</span></span></code></pre></div>
<p>This script connects to the websocket server and reloads the page when it receives
any message. A keepalive message is sent every 30 seconds to keep the connection
alive. When the connection is closed, the script tries to reconnect every second.</p>
<p>This is a <em>really</em> cursed hack but it got me up and running with the tools
I already had.</p>
        
        Last modified 2023-08-19
        
    </section>
</article>]]></summary>
</entry>
<entry>
    <title>Building a websocket echo server in 33 lines of code</title>
    <link href="https://ldgrp.me/posts/serverless-echo.html" />
    <id>https://ldgrp.me/posts/serverless-echo.html</id>
    <published>2023-07-18T00:00:00Z</published>
    <updated>2023-07-18T00:00:00Z</updated>
    <summary type="html"><![CDATA[<article class="post">
    <section class="header mt-4 mb-6">
        <h1 class="mr-8 sm:mr-10">Building a websocket echo server in 33 lines of code</h1>
        <div class="flex gap-4 -mt-3">
        <div class="text-sm font-mono text-gray-600 dark:text-gray-300">
            Posted on 18 Jul 2023
        </div>
        
        </div>
    </section>
    <section>
        <p>The code below shows how to build a simple websocket echo server with AWS Lambda and API Gateway, with all the infrastructure management handled by Pulumi.
A GitHub repository with the full code is available <a href="https://github.com/ldgrp/serverless-ws-echo">here</a>.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode typescript"><code class="sourceCode typescript"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> <span class="op">*</span> <span class="im">as</span> pulumi <span class="im">from</span> <span class="st">&quot;@pulumi/pulumi&quot;</span><span class="op">;</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> <span class="op">*</span> <span class="im">as</span> aws <span class="im">from</span> <span class="st">&quot;@pulumi/aws&quot;</span><span class="op">;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> echoHandler <span class="op">=</span> <span class="kw">new</span> aws<span class="op">.</span><span class="at">lambda</span><span class="op">.</span><span class="fu">CallbackFunction</span>(<span class="st">&quot;echoHandler&quot;</span><span class="op">,</span> {</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>  callback<span class="op">:</span> <span class="kw">async</span> (event<span class="op">:</span> <span class="dt">any</span>) <span class="kw">=&gt;</span> ({ statusCode<span class="op">:</span> <span class="dv">200</span><span class="op">,</span> body<span class="op">:</span> <span class="bu">event</span><span class="op">.</span><span class="at">body</span> })</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> api <span class="op">=</span> <span class="kw">new</span> aws<span class="op">.</span><span class="at">apigatewayv2</span><span class="op">.</span><span class="fu">Api</span>(<span class="st">&quot;api&quot;</span><span class="op">,</span> { protocolType<span class="op">:</span> <span class="st">&quot;WEBSOCKET&quot;</span> })<span class="op">;</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> integration <span class="op">=</span> <span class="kw">new</span> aws<span class="op">.</span><span class="at">apigatewayv2</span><span class="op">.</span><span class="fu">Integration</span>(<span class="st">&quot;defaultIntegration&quot;</span><span class="op">,</span> {</span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a>  apiId<span class="op">:</span> api<span class="op">.</span><span class="at">id</span><span class="op">,</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a>  integrationType<span class="op">:</span> <span class="st">&quot;AWS_PROXY&quot;</span><span class="op">,</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a>  integrationUri<span class="op">:</span> echoHandler<span class="op">.</span><span class="at">invokeArn</span><span class="op">,</span></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> defaultRoute <span class="op">=</span> <span class="kw">new</span> aws<span class="op">.</span><span class="at">apigatewayv2</span><span class="op">.</span><span class="fu">Route</span>(<span class="st">&quot;defaultRoute&quot;</span><span class="op">,</span> {</span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a>  apiId<span class="op">:</span> api<span class="op">.</span><span class="at">id</span><span class="op">,</span></span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a>  routeKey<span class="op">:</span> <span class="st">&quot;$default&quot;</span><span class="op">,</span></span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a>  target<span class="op">:</span> pulumi<span class="op">.</span><span class="fu">interpolate</span><span class="vs">`integrations/</span><span class="sc">${</span>integration<span class="op">.</span><span class="at">id</span><span class="sc">}</span><span class="vs">`</span><span class="op">,</span></span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span>
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> deployment <span class="op">=</span> <span class="kw">new</span> aws<span class="op">.</span><span class="at">apigatewayv2</span><span class="op">.</span><span class="fu">Deployment</span>(</span>
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a>  <span class="st">&quot;deployment&quot;</span><span class="op">,</span></span>
<span id="cb1-24"><a href="#cb1-24" aria-hidden="true" tabindex="-1"></a>  { apiId<span class="op">:</span> api<span class="op">.</span><span class="at">id</span> }<span class="op">,</span></span>
<span id="cb1-25"><a href="#cb1-25" aria-hidden="true" tabindex="-1"></a>  { dependsOn<span class="op">:</span> [defaultRoute]}</span>
<span id="cb1-26"><a href="#cb1-26" aria-hidden="true" tabindex="-1"></a>)<span class="op">;</span></span>
<span id="cb1-27"><a href="#cb1-27" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-28"><a href="#cb1-28" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> stage <span class="op">=</span> <span class="kw">new</span> aws<span class="op">.</span><span class="at">apigatewayv2</span><span class="op">.</span><span class="fu">Stage</span>(<span class="st">&quot;stage&quot;</span><span class="op">,</span> {</span>
<span id="cb1-29"><a href="#cb1-29" aria-hidden="true" tabindex="-1"></a>  apiId<span class="op">:</span> api<span class="op">.</span><span class="at">id</span><span class="op">,</span></span>
<span id="cb1-30"><a href="#cb1-30" aria-hidden="true" tabindex="-1"></a>  name<span class="op">:</span> <span class="st">&quot;dev&quot;</span><span class="op">,</span></span>
<span id="cb1-31"><a href="#cb1-31" aria-hidden="true" tabindex="-1"></a>  deploymentId<span class="op">:</span> deployment<span class="op">.</span><span class="at">id</span><span class="op">,</span></span>
<span id="cb1-32"><a href="#cb1-32" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span>
<span id="cb1-33"><a href="#cb1-33" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-34"><a href="#cb1-34" aria-hidden="true" tabindex="-1"></a><span class="im">export</span> <span class="kw">const</span> url <span class="op">=</span> pulumi<span class="op">.</span><span class="fu">interpolate</span><span class="vs">`</span><span class="sc">${</span>api<span class="op">.</span><span class="at">apiEndpoint</span><span class="sc">}</span><span class="vs">/</span><span class="sc">${</span>stage<span class="op">.</span><span class="at">name</span><span class="sc">}</span><span class="vs">`</span><span class="op">;</span></span></code></pre></div>
<ol type="1">
<li><code>echoHandler</code> defines a Lambda function that returns the body of the request (the echo).</li>
<li><code>api</code> creates a new API Gateway configured for websockets.</li>
<li><code>integration</code> associates the Lambda function with the API Gateway. It also configures the integration type to be <code>AWS_PROXY</code>, which means that the Lambda function will receive the entire request object.</li>
<li><code>defaultRoute</code> handles incoming requests that don’t match any other route. In this case, it matches all requests.</li>
<li><code>deployment</code> creates a new deployment of the API Gateway. This is required before we can create a stage.</li>
<li><code>stage</code> represents a state of the API Gateway. In this case, it’s just the <code>dev</code> stage but we could create multiple stages for different environments (e.g. <code>prod</code>).</li>
<li>Finally, <code>url</code> is the URL of the API gateway combined with the stage name. This is the URL that clients can connect to.</li>
</ol>
        
        Last modified 2023-07-18
        
    </section>
</article>]]></summary>
</entry>
<entry>
    <title>Unfolding Paginated APIs in Haskell</title>
    <link href="https://ldgrp.me/posts/unfold.html" />
    <id>https://ldgrp.me/posts/unfold.html</id>
    <published>2020-10-01T00:00:00Z</published>
    <updated>2020-10-01T00:00:00Z</updated>
    <summary type="html"><![CDATA[<article class="post">
    <section class="header mt-4 mb-6">
        <h1 class="mr-8 sm:mr-10">Unfolding Paginated APIs in Haskell</h1>
        <div class="flex gap-4 -mt-3">
        <div class="text-sm font-mono text-gray-600 dark:text-gray-300">
            Posted on 01 Oct 2020
        </div>
        
        </div>
    </section>
    <section>
        <p>Cursor-based pagination is a common strategy for breaking up
query results into smaller chunks. Each chunk contains a portion
of the results, and a reference to the last item in the chunk.</p>
<p>For example, the following request will get 2 accounts <em>after</em> the
cursor <code>x</code></p>
<div class="sourceCode" id="cb1"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>GET /accounts?page[size]=2&amp;page[after]=x</span></code></pre></div>
<p>We can express this idea with a Haskell data type</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Cursor</span> <span class="ot">=</span> <span class="dt">Int</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Paginated</span> a </span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>  <span class="ot">=</span> <span class="dt">Initial</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">Chunk</span> {<span class="ot"> _content ::</span> [a] ,<span class="ot"> _cursor  ::</span> <span class="dt">Cursor</span> }</span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">Final</span> [a]</span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>  <span class="kw">deriving</span> <span class="dt">Show</span></span></code></pre></div>
<p>Let’s also create a dummy endpoint.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- | Returns a paginated list of integers from 1 to 15</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>endpoint</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="ot">  ::</span> <span class="dt">Maybe</span> <span class="dt">Int</span>     <span class="co">-- page size </span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>  <span class="ot">-&gt;</span> <span class="dt">Maybe</span> <span class="dt">Cursor</span>  <span class="co">-- page after</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>  <span class="ot">-&gt;</span> <span class="dt">Maybe</span> ([<span class="dt">Int</span>], <span class="dt">Maybe</span> <span class="dt">Cursor</span>)</span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>endpoint size after <span class="ot">=</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>  <span class="kw">let</span> size&#39; <span class="ot">=</span> <span class="fu">maybe</span> <span class="dv">6</span> <span class="fu">id</span> size</span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>      after&#39; <span class="ot">=</span> <span class="fu">maybe</span> <span class="dv">0</span> <span class="fu">id</span> after</span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a>      result <span class="ot">=</span> <span class="fu">take</span> size&#39; [after&#39;<span class="op">+</span><span class="dv">1</span><span class="op">..</span><span class="dv">15</span>]</span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>      newAfter&#39; <span class="ot">=</span> headMaybe (<span class="fu">drop</span> (size&#39;<span class="op">-</span><span class="dv">1</span>) result)</span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a>   <span class="kw">in</span> <span class="kw">if</span> <span class="fu">null</span> result <span class="kw">then</span> <span class="dt">Nothing</span> <span class="kw">else</span> <span class="dt">Just</span> (result, newAfter&#39;)</span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a>      </span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a><span class="ot">headMaybe ::</span> [a] <span class="ot">-&gt;</span> <span class="dt">Maybe</span> a</span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a>headMaybe (x<span class="op">:</span>_xs) <span class="ot">=</span> <span class="dt">Just</span> x</span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a>headMaybe [] <span class="ot">=</span> <span class="dt">Nothing</span></span></code></pre></div>
<p>Requests to our endpoint look like</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="ot">request ::</span> <span class="dt">Paginated</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Paginated</span> <span class="dt">Int</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>request <span class="dt">Initial</span> <span class="ot">=</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">case</span> endpoint <span class="dt">Nothing</span> <span class="dt">Nothing</span> <span class="kw">of</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Just</span> (result, <span class="dt">Just</span> newCursor) <span class="ot">-&gt;</span> <span class="dt">Chunk</span> result newCursor</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Just</span> (result, <span class="dt">Nothing</span>) <span class="ot">-&gt;</span> <span class="dt">Final</span> result</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Nothing</span> <span class="ot">-&gt;</span> <span class="dt">Final</span> []</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>request c<span class="op">@</span>(<span class="dt">Chunk</span> acc cursor) <span class="ot">=</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>  <span class="kw">case</span> endpoint <span class="dt">Nothing</span> (<span class="dt">Just</span> cursor) <span class="kw">of</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Just</span> (result, <span class="dt">Just</span> newCursor) <span class="ot">-&gt;</span> <span class="dt">Chunk</span> result newCursor</span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Just</span> (result, <span class="dt">Nothing</span>) <span class="ot">-&gt;</span> <span class="dt">Final</span> result</span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Nothing</span> <span class="ot">-&gt;</span> <span class="dt">Final</span> []</span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a>request e<span class="op">@</span>(<span class="dt">Final</span> _acc) <span class="ot">=</span> e</span></code></pre></div>
<p>We are pattern matching on the input <code>Paginated Int</code> to determine the
behaviour of <code>request</code>. <code>Inital</code>ly, we query the endpoint to obtain a page.
This can either be <code>Chunk</code> or <code>Final</code>.</p>
<p>If we have a <code>Chunk</code>, we can obtain another <code>Chunk</code>, or a <code>Final</code>.
Note that we are not concatenating the pages in this function.
We are simply returning the <em>next</em> state from our current state.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="ot">run ::</span> <span class="dt">IO</span> ()</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>run <span class="ot">=</span> <span class="fu">print</span> (<span class="fu">take</span> <span class="dv">4</span> (<span class="fu">iterate</span> request <span class="dt">Initial</span>))</span></code></pre></div>
<div class="sourceCode" id="cb6"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="op">*</span><span class="dt">Main</span><span class="op">&gt;</span> run</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>[ </span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">Initial</span>,</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>  <span class="dt">Chunk</span> {_content <span class="ot">=</span> [<span class="dv">1</span>,<span class="dv">2</span>,<span class="dv">3</span>,<span class="dv">4</span>,<span class="dv">5</span>,<span class="dv">6</span>], _cursor <span class="ot">=</span> <span class="dv">6</span>},</span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>  <span class="dt">Chunk</span> {_content <span class="ot">=</span> [<span class="dv">7</span>,<span class="dv">8</span>,<span class="dv">9</span>,<span class="dv">10</span>,<span class="dv">11</span>,<span class="dv">12</span>], _cursor <span class="ot">=</span> <span class="dv">12</span>},</span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>  <span class="dt">Final</span> [<span class="dv">13</span>,<span class="dv">14</span>,<span class="dv">15</span>]</span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>]</span></code></pre></div>
<p>It works! Notice how this list is generated from an initial value. The next
value is obtained by applying a function to the previous value. This is
precisely what <code>iterate</code> does.</p>
<h2 id="unfoldr">Unfoldr</h2>
<p>We can also use recursion to generate the full response.
This function will take an initial value and return a list of chunks
(represented as lists of <code>Int</code>).</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="ot">recursiveRequest ::</span> (<span class="dt">Maybe</span> <span class="dt">Cursor</span> <span class="ot">-&gt;</span> <span class="dt">Maybe</span> ([<span class="dt">Int</span>], <span class="dt">Maybe</span> <span class="dt">Cursor</span>))</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>                  <span class="ot">-&gt;</span> <span class="dt">Maybe</span> <span class="dt">Cursor</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>                  <span class="ot">-&gt;</span> [[<span class="dt">Int</span>]]</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>recursiveRequest f c <span class="ot">=</span> <span class="kw">case</span> f c <span class="kw">of</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>  <span class="dt">Just</span> (r, <span class="dt">Just</span> c&#39;) <span class="ot">-&gt;</span> r <span class="op">:</span> recursiveRequest f (<span class="dt">Just</span> c&#39;)</span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a>  <span class="dt">Just</span> (r, <span class="dt">Nothing</span>) <span class="ot">-&gt;</span> [r]</span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a>  <span class="dt">Nothing</span> <span class="ot">-&gt;</span> []</span></code></pre></div>
<p>Let’s make this function <em>generic</em>. Replace the <code>[Int]</code>s with <code>a</code>s
and the <code>Maybe Cursor</code>s with <code>b</code>s.</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="ot">recursiveRequest ::</span> (<span class="dt">Maybe</span> <span class="dt">Cursor</span> <span class="ot">-&gt;</span> <span class="dt">Maybe</span> ([<span class="dt">Int</span>], <span class="dt">Maybe</span> <span class="dt">Cursor</span>)) </span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>                 <span class="ot">-&gt;</span> <span class="dt">Maybe</span> <span class="dt">Cursor</span> </span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>                 <span class="ot">-&gt;</span> [[<span class="dt">Int</span>]]</span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a><span class="ot">genericRequest   ::</span> (b            <span class="ot">-&gt;</span> <span class="dt">Maybe</span> ( a   , b           )) </span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a>                 <span class="ot">-&gt;</span> b            </span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a>                 <span class="ot">-&gt;</span> [  a  ]</span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a>genericRequest f b <span class="ot">=</span> <span class="kw">case</span> f b <span class="kw">of</span></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a>  <span class="dt">Just</span> (a, b&#39;) <span class="ot">-&gt;</span> a <span class="op">:</span> genericRequest f b&#39;</span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a>  <span class="dt">Nothing</span> <span class="ot">-&gt;</span> []</span></code></pre></div>
<p>The first argument is a function that will return an <code>a</code> and a new <code>b</code>.
The second argument is a <code>b</code>. We apply <code>b</code> to the function, collect the generated <code>a</code>
and use the new <code>b</code> to call the function again.</p>
<p>Let’s query the endpoint with this function.</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- this program does not terminate</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="ot">run ::</span> <span class="dt">IO</span> ()</span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>run <span class="ot">=</span> <span class="fu">print</span> (genericRequest (endpoint <span class="dt">Nothing</span>) <span class="dt">Nothing</span>)</span></code></pre></div>
<p>The main problem here is the type of <code>b</code>.
In this context, it is a <code>Maybe Cursor</code> which is not enough to encode
being in the start, middle or end of the recursion. We need a more
expressive type for <code>b</code>.</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">State</span> a <span class="ot">=</span> <span class="dt">Start</span> <span class="op">|</span> <span class="dt">Next</span> a <span class="op">|</span> <span class="dt">End</span></span></code></pre></div>
<p>Recall the type of our endpoint. When <code>Nothing</code> is applied to this function,
it returns a function of type <code>Maybe Cursor -&gt; Maybe ([Int], Maybe Cursor)</code>.</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="op">*</span><span class="dt">Main</span><span class="op">&gt;</span> <span class="op">:</span>t endpoint</span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a><span class="ot">endpoint ::</span> <span class="dt">Maybe</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Maybe</span> <span class="dt">Cursor</span> <span class="ot">-&gt;</span> <span class="dt">Maybe</span> ([<span class="dt">Int</span>], <span class="dt">Maybe</span> <span class="dt">Cursor</span>)</span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a><span class="op">*</span><span class="dt">Main</span><span class="op">&gt;</span> <span class="op">:</span>t endpoint <span class="dt">Nothing</span></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a>endpoint <span class="dt">Nothing</span><span class="ot"> ::</span> <span class="dt">Maybe</span> <span class="dt">Cursor</span> <span class="ot">-&gt;</span> <span class="dt">Maybe</span> ([<span class="dt">Int</span>], <span class="dt">Maybe</span> <span class="dt">Cursor</span>)</span></code></pre></div>
<p>Naively using this function as the first argument of <code>genericRequest</code> will result in an infinite
loop. We need to wrap it with another function that is able to keep track of our state.</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="ot">wrapped ::</span> (<span class="dt">Maybe</span> <span class="dt">Cursor</span> <span class="ot">-&gt;</span> <span class="dt">Maybe</span> ([<span class="dt">Int</span>], <span class="dt">Maybe</span> <span class="dt">Cursor</span>)) </span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>        <span class="ot">-&gt;</span>  <span class="dt">State</span> <span class="dt">Cursor</span> </span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a>        <span class="ot">-&gt;</span> <span class="dt">Maybe</span> ([<span class="dt">Int</span>], <span class="dt">State</span> <span class="dt">Cursor</span>)</span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a>wrapped f <span class="dt">Start</span> <span class="ot">=</span> <span class="kw">case</span> f <span class="dt">Nothing</span> <span class="kw">of</span></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a>  <span class="dt">Just</span> (r, <span class="dt">Just</span> b&#39;) <span class="ot">-&gt;</span> <span class="dt">Just</span> (r, <span class="dt">Next</span> b&#39;)</span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a>  <span class="dt">Just</span> (r, <span class="dt">Nothing</span>) <span class="ot">-&gt;</span> <span class="dt">Just</span> (r, <span class="dt">End</span>)</span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a>  <span class="dt">Nothing</span> <span class="ot">-&gt;</span> <span class="dt">Nothing</span></span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a>wrapped f (<span class="dt">Next</span> b) <span class="ot">=</span> <span class="kw">case</span> f (<span class="dt">Just</span> b) <span class="kw">of</span></span>
<span id="cb12-9"><a href="#cb12-9" aria-hidden="true" tabindex="-1"></a>  <span class="dt">Just</span> (r, <span class="dt">Just</span> b&#39;) <span class="ot">-&gt;</span> <span class="dt">Just</span> (r, <span class="dt">Next</span> b&#39;)</span>
<span id="cb12-10"><a href="#cb12-10" aria-hidden="true" tabindex="-1"></a>  <span class="dt">Just</span> (r, <span class="dt">Nothing</span>) <span class="ot">-&gt;</span> <span class="dt">Just</span> (r, <span class="dt">End</span>)</span>
<span id="cb12-11"><a href="#cb12-11" aria-hidden="true" tabindex="-1"></a>  <span class="dt">Nothing</span> <span class="ot">-&gt;</span> <span class="dt">Nothing</span></span>
<span id="cb12-12"><a href="#cb12-12" aria-hidden="true" tabindex="-1"></a>wrapped f <span class="dt">End</span> <span class="ot">=</span> <span class="dt">Nothing</span></span></code></pre></div>
<p>Huh, this looks a lot like our initial <code>request</code>.
Let’s take a step back and understand what this function is really doing.</p>
<p>If the endpoint returns a cursor <code>Just b'</code>, we want to encode this state as <code>Next b'</code>.
Otherwise, there are no more pages and we encode the state as <code>End</code>.</p>
<p>If we define a mapping between <code>Maybe</code> and <code>State</code>, we can write <code>wrapped</code> succintly
by updating the second value of the returned tuple.</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="ot">maybeToState ::</span> <span class="dt">Maybe</span> a <span class="ot">-&gt;</span> <span class="dt">State</span> a</span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a>maybeToState (<span class="dt">Just</span> x) <span class="ot">=</span> <span class="dt">Next</span> x</span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a>maybeToState <span class="dt">Nothing</span> <span class="ot">=</span> <span class="dt">End</span></span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a><span class="ot">wrapped ::</span> (<span class="dt">Maybe</span> <span class="dt">Cursor</span> <span class="ot">-&gt;</span> <span class="dt">Maybe</span> ([<span class="dt">Int</span>], <span class="dt">Maybe</span> <span class="dt">Cursor</span>)) <span class="ot">-&gt;</span>  <span class="dt">State</span> <span class="dt">Cursor</span> <span class="ot">-&gt;</span> <span class="dt">Maybe</span> ([<span class="dt">Int</span>], <span class="dt">State</span> <span class="dt">Cursor</span>)</span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a>wrapped f <span class="dt">Start</span> <span class="ot">=</span> <span class="fu">fmap</span> (\(a, b&#39;) <span class="ot">-&gt;</span> (a, maybeToState b&#39;)) (f <span class="dt">Nothing</span>)</span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a>wrapped f (<span class="dt">Next</span> b) <span class="ot">=</span> <span class="fu">fmap</span> (\(a, b&#39;) <span class="ot">-&gt;</span> (a, maybeToState b&#39;)) (f (<span class="dt">Just</span> b))</span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a>wrapped f <span class="dt">End</span> <span class="ot">=</span> <span class="dt">Nothing</span></span></code></pre></div>
<p>But wait, there’s more! We can make this function generic, and use <code>Control.Arrow.second</code>
instead of the lambda. In summary, we have</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="ot">genericRequest ::</span> (b <span class="ot">-&gt;</span> <span class="dt">Maybe</span> (a, b)) <span class="ot">-&gt;</span> b <span class="ot">-&gt;</span> [a]</span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a>genericRequest f b <span class="ot">=</span> <span class="kw">case</span> f b <span class="kw">of</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">Just</span> (a, b&#39;) <span class="ot">-&gt;</span> a <span class="op">:</span> genericRequest f b&#39;</span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a>  <span class="dt">Nothing</span> <span class="ot">-&gt;</span> []</span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a><span class="ot">genericWrapped ::</span> (<span class="dt">Maybe</span> b <span class="ot">-&gt;</span> <span class="dt">Maybe</span> (a, <span class="dt">Maybe</span> b)) <span class="ot">-&gt;</span>  <span class="dt">State</span> b <span class="ot">-&gt;</span> <span class="dt">Maybe</span> (a, <span class="dt">State</span> b)</span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a>genericWrapped f <span class="dt">Start</span> <span class="ot">=</span> <span class="fu">fmap</span> (second maybeToState) (f <span class="dt">Nothing</span>)</span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a>genericWrapped f (<span class="dt">Next</span> b) <span class="ot">=</span> <span class="fu">fmap</span> (second maybeToState) (f (<span class="dt">Just</span> b))</span>
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true" tabindex="-1"></a>genericWrapped f <span class="dt">End</span> <span class="ot">=</span> <span class="dt">Nothing</span></span>
<span id="cb14-10"><a href="#cb14-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-11"><a href="#cb14-11" aria-hidden="true" tabindex="-1"></a><span class="ot">maybeToState ::</span> <span class="dt">Maybe</span> a <span class="ot">-&gt;</span> <span class="dt">State</span> a</span>
<span id="cb14-12"><a href="#cb14-12" aria-hidden="true" tabindex="-1"></a>maybeToState (<span class="dt">Just</span> x) <span class="ot">=</span> <span class="dt">Next</span> x</span>
<span id="cb14-13"><a href="#cb14-13" aria-hidden="true" tabindex="-1"></a>maybeToState <span class="dt">Nothing</span> <span class="ot">=</span> <span class="dt">End</span></span>
<span id="cb14-14"><a href="#cb14-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-15"><a href="#cb14-15" aria-hidden="true" tabindex="-1"></a><span class="ot">run ::</span> <span class="dt">IO</span> ()</span>
<span id="cb14-16"><a href="#cb14-16" aria-hidden="true" tabindex="-1"></a>run <span class="ot">=</span> <span class="fu">print</span> (genericRequest (genericWrapped (endpoint <span class="dt">Nothing</span>)) <span class="dt">Start</span>)</span></code></pre></div>
<div class="sourceCode" id="cb15"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="op">*</span><span class="dt">Main</span><span class="op">&gt;</span> run</span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a>[[<span class="dv">1</span>,<span class="dv">2</span>,<span class="dv">3</span>,<span class="dv">4</span>,<span class="dv">5</span>,<span class="dv">6</span>],[<span class="dv">7</span>,<span class="dv">8</span>,<span class="dv">9</span>,<span class="dv">10</span>,<span class="dv">11</span>,<span class="dv">12</span>],[<span class="dv">13</span>,<span class="dv">14</span>,<span class="dv">15</span>]]</span></code></pre></div>
<p>Again, the idea here is that we are generating a list of things from a value.
The type of <code>genericRequest</code> seems suspicious. Let’s <a href="https://hoogle.haskell.org/">Hoogle</a> it.</p>
<figure>
<img src="/images/unfold-hoogle.png" alt="something something tool support" />
<figcaption aria-hidden="true">something something tool support</figcaption>
</figure>
<p>It turns out we have stumbled upon <code>unfoldr</code>.</p>
<blockquote>
<p>while <code>foldr</code> reduces a list to a summary value, <code>unfoldr</code> builds a list from a seed value</p>
</blockquote>
<p>We can get rid of <code>genericRequest</code> altogether and use <code>unfoldr</code> directly</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="ot">run ::</span> <span class="dt">IO</span> ()</span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a>run <span class="ot">=</span> <span class="fu">print</span> (unfoldr (genericWrapped (endpoint <span class="dt">Nothing</span>)) <span class="dt">Start</span>)</span></code></pre></div>
<div class="sourceCode" id="cb17"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="op">*</span><span class="dt">Main</span><span class="op">&gt;</span> run</span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a>[[<span class="dv">1</span>,<span class="dv">2</span>,<span class="dv">3</span>,<span class="dv">4</span>,<span class="dv">5</span>,<span class="dv">6</span>],[<span class="dv">7</span>,<span class="dv">8</span>,<span class="dv">9</span>,<span class="dv">10</span>,<span class="dv">11</span>,<span class="dv">12</span>],[<span class="dv">13</span>,<span class="dv">14</span>,<span class="dv">15</span>]]</span></code></pre></div>
        
    </section>
</article>]]></summary>
</entry>
<entry>
    <title>Factoring APIs in servant-client</title>
    <link href="https://ldgrp.me/posts/factoring-apis.html" />
    <id>https://ldgrp.me/posts/factoring-apis.html</id>
    <published>2020-08-14T00:00:00Z</published>
    <updated>2020-08-14T00:00:00Z</updated>
    <summary type="html"><![CDATA[<article class="post">
    <section class="header mt-4 mb-6">
        <h1 class="mr-8 sm:mr-10">Factoring APIs in servant-client</h1>
        <div class="flex gap-4 -mt-3">
        <div class="text-sm font-mono text-gray-600 dark:text-gray-300">
            Posted on 14 Aug 2020
        </div>
        
        </div>
    </section>
    <section>
        <p>I am currently writing a Haskell client
<a href="https://github.com/ldgrp/up-api-haskell">library</a> for the Up Bank API with
<a href="https://hackage.haskell.org/package/servant-client"><code>servant-client</code></a>.</p>
<p>Endpoints require an <code>Authentication</code> request header with the format
<code>Authorization: TOKEN_TYPE Token</code></p>
<p>With servant, we can represent this with the <code>Header</code> combinator and add it to
every endpoint. For example, the type of an API is as follows.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">API</span> <span class="ot">=</span> </span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>       <span class="dt">Header</span> <span class="st">&quot;Authorization&quot;</span> <span class="dt">String</span> <span class="op">:&gt;</span> <span class="dt">Capture</span> <span class="st">&quot;x&quot;</span> <span class="dt">Int</span> <span class="op">:&gt;</span> <span class="dt">Get</span> &#39;[<span class="dt">JSON</span>] <span class="dt">X</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>  <span class="op">:&lt;|&gt;</span> <span class="dt">Header</span> <span class="st">&quot;Authorization&quot;</span> <span class="dt">String</span> <span class="op">:&gt;</span> <span class="dt">Capture</span> <span class="st">&quot;y&quot;</span> <span class="dt">Int</span> <span class="op">:&gt;</span> <span class="dt">Get</span> &#39;[<span class="dt">JSON</span>] <span class="dt">Y</span></span></code></pre></div>
<p>We can factor out the <code>Header</code>s, similar to factoring in algebra <code>f g + f h = f (g + h)</code></p>
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">FactoredAPI</span> <span class="ot">=</span>  <span class="dt">Header</span> <span class="st">&quot;Authorization&quot;</span> <span class="dt">String</span> <span class="op">:&gt;</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>  (    <span class="dt">Capture</span> <span class="st">&quot;x&quot;</span> <span class="dt">Int</span> <span class="op">:&gt;</span> <span class="dt">Get</span> &#39;[<span class="dt">JSON</span>] <span class="dt">X</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>  <span class="op">:&lt;|&gt;</span> <span class="dt">Capture</span> <span class="st">&quot;y&quot;</span> <span class="dt">Int</span> <span class="op">:&gt;</span> <span class="dt">Get</span> &#39;[<span class="dt">JSON</span>] <span class="dt">Y</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>  )</span></code></pre></div>
<h2 id="clients">Clients</h2>
<p><code>servant-client</code> provides the function <code>client</code> which automagically generates
implementations and allows us to pattern match on the client functions of the API.</p>
<p>An implementation of the unfactored client API is as follows.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="ot">getX ::</span> <span class="dt">Maybe</span> <span class="dt">String</span>  <span class="co">-- ^ token</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>     <span class="ot">-&gt;</span> <span class="dt">Int</span>           <span class="co">-- ^ x</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>     <span class="ot">-&gt;</span> <span class="dt">ClientM</span> <span class="dt">X</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="ot">getY ::</span> <span class="dt">Maybe</span> <span class="dt">String</span>  <span class="co">-- ^ token</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>     <span class="ot">-&gt;</span> <span class="dt">Int</span>           <span class="co">-- ^ y</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>     <span class="ot">-&gt;</span> <span class="dt">ClientM</span> <span class="dt">Y</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>             </span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a><span class="ot">api ::</span> <span class="dt">Proxy</span> <span class="dt">API</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>api <span class="ot">=</span> <span class="dt">Proxy</span></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a>             </span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a>getX <span class="op">:&lt;|&gt;</span> getY <span class="ot">=</span> client api</span></code></pre></div>
<p>Let’s look at the type of the unfactored client API.
It returns two client functions combined with <code>:&lt;|&gt;</code>.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>ghci<span class="op">&gt;</span> <span class="op">:</span>t client (<span class="dt">Proxy</span><span class="ot"> ::</span> <span class="dt">Proxy</span> <span class="dt">API</span>)</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>client (<span class="dt">Proxy</span><span class="ot"> ::</span> <span class="dt">Proxy</span> <span class="dt">FactoredAPI</span>)</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="ot">  ::</span> (<span class="dt">Maybe</span> [<span class="dt">Char</span>] <span class="ot">-&gt;</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">ClientM</span> <span class="dt">X</span>)</span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>        <span class="op">:&lt;|&gt;</span> (<span class="dt">Maybe</span> [<span class="dt">Char</span>] <span class="ot">-&gt;</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">ClientM</span> <span class="dt">Y</span>)</span></code></pre></div>
<p>Whereas the client of the factored API returns a function that takes
a token and returns two client functions.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>ghci<span class="op">&gt;</span> <span class="op">:</span>t client (<span class="dt">Proxy</span><span class="ot"> ::</span> <span class="dt">Proxy</span> <span class="dt">FactoredAPI</span>)</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>client (<span class="dt">Proxy</span><span class="ot"> ::</span> <span class="dt">Proxy</span> <span class="dt">FactoredAPI</span>)</span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="ot">  ::</span> <span class="dt">Maybe</span> [<span class="dt">Char</span>]</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>     <span class="ot">-&gt;</span> (<span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">ClientM</span> <span class="dt">X</span>)</span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>        <span class="op">:&lt;|&gt;</span> (<span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">ClientM</span> <span class="dt">Y</span>)</span></code></pre></div>
<p>An implementation of the factored API is then</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="ot">getX&#39; ::</span> <span class="dt">Int</span>  <span class="co">-- ^ x</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>      <span class="ot">-&gt;</span> <span class="dt">ClientM</span> <span class="dt">X</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a><span class="ot">getY&#39; ::</span> <span class="dt">Int</span>  <span class="co">-- ^ y</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>      <span class="ot">-&gt;</span> <span class="dt">ClientM</span> <span class="dt">Y</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>             </span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a><span class="ot">api&#39; ::</span> <span class="dt">Proxy</span> <span class="dt">FactoredAPI</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>api&#39; <span class="ot">=</span> <span class="dt">Proxy</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a>             </span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a>getX&#39; <span class="op">:&lt;|&gt;</span> getY&#39; <span class="ot">=</span> client api&#39; token</span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a>  <span class="kw">where</span><span class="ot"> token ::</span> <span class="dt">Maybe</span> <span class="dt">String</span></span></code></pre></div>
<p>As expected, <code>client api' token</code> will return two client functions.</p>
        
    </section>
</article>]]></summary>
</entry>

</feed>
