<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Adam J King</title><link>https://comforting-babka-f6ed67.netlify.app/</link><description>Recent content on Adam J King</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Thu, 26 Feb 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://comforting-babka-f6ed67.netlify.app/index.xml" rel="self" type="application/rss+xml"/><item><title>Pull up a seat with FS2 Streams</title><link>https://comforting-babka-f6ed67.netlify.app/p/pull-up-a-seat-with-fs2-streams/</link><pubDate>Thu, 26 Feb 2026 00:00:00 +0000</pubDate><guid>https://comforting-babka-f6ed67.netlify.app/p/pull-up-a-seat-with-fs2-streams/</guid><description>&lt;p&gt;I usually put &lt;a class="link" href="https://fs2.io/" target="_blank" rel="noopener"
&gt;FS2&lt;/a&gt; amongst my top favourite libraries in my career. This streaming library powers some
of the most popular Scala Typelevel libraries. I often encounter streaming design problems that make me think to
myself, &amp;ldquo;you can do this with a couple lines of FS2&amp;rdquo;. Enough gushing. I typically find that folks who come across FS2
struggle with creating a correct mental model for how a stream in FS2 works.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This article assumes you know about Cats-Effect and FS2 already and would like to dive deeper. Understanding how
FS2 works requires a mental model for how suspended side effects work too.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;On the surface, an FS2 &lt;code&gt;Stream&lt;/code&gt; looks and feels a lot like a collection (like &lt;code&gt;List&lt;/code&gt;). It supports common collections
operations like &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;flatMap&lt;/code&gt;, or &lt;code&gt;filter&lt;/code&gt;. In fact, when we go to complete a stream, we can convert our stream to a
host of different collection types.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;F&lt;/span&gt;, &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="o"&gt;???&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toList&lt;/span&gt; &lt;span class="c1"&gt;// F[List[Int]]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toVector&lt;/span&gt; &lt;span class="c1"&gt;// F[Vector[Int]]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This property of &lt;code&gt;Stream&lt;/code&gt;s makes it easy to understand the API at first, but can make it difficult to understand how it
works internally. A &lt;code&gt;Stream&lt;/code&gt; differs from a &lt;code&gt;List&lt;/code&gt; in that it incorporates an effect type (&lt;code&gt;F[_]&lt;/code&gt;) as part of its
definition (&lt;code&gt;Stream[F, A]&lt;/code&gt;). So why do we need an effect type for our stream definition? A &lt;code&gt;Stream&lt;/code&gt; does not necesserily
hold all elements in memory at once. For example, if we stream an HTTP response, not all body bytes arrive from the
network at the same time.&lt;/p&gt;
&lt;p&gt;When using an effectful type (like &lt;code&gt;IO&lt;/code&gt;), a &lt;code&gt;Stream[IO, A]&lt;/code&gt; actually represents a single &lt;code&gt;IO[Option[A]]&lt;/code&gt; that we call
repeatedly for new results. We refer to this as a &amp;ldquo;pull-based&amp;rdquo; stream because we need to perform an action to retrieve
the next elements of the stream. This works the same as Scala&amp;rsquo;s &lt;code&gt;Iterator&lt;/code&gt; class when lazy generating results. As this
diagram shows, we don&amp;rsquo;t materialise the elements of the &lt;code&gt;Stream&lt;/code&gt; until we reach them.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://comforting-babka-f6ed67.netlify.app/p/pull-up-a-seat-with-fs2-streams/diagram.png"
width="474"
height="550"
srcset="https://comforting-babka-f6ed67.netlify.app/p/pull-up-a-seat-with-fs2-streams/diagram_hu_aea90a6c9034ba7.png 480w, https://comforting-babka-f6ed67.netlify.app/p/pull-up-a-seat-with-fs2-streams/diagram_hu_82442239ac43c8b5.png 1024w"
loading="lazy"
alt="A comparison of the mental models"
class="gallery-image"
data-flex-grow="86"
data-flex-basis="206px"
&gt;&lt;/p&gt;
&lt;p&gt;Another important thing to note about &lt;code&gt;Stream&lt;/code&gt;s; they don&amp;rsquo;t (always) contain single elements. Instead, they contain
multiple groups of elements (&lt;code&gt;Chunk&lt;/code&gt;s), which allows individual pulls to return more than one element. Useful when
working with things like response payloads where consuming bodies byte by byte could harm performance. At one time a
&lt;code&gt;Stream&lt;/code&gt; only holds a single chunk. It pulls a new chunk as necessary when we consume the stream.&lt;/p&gt;
&lt;h2 id="pull-api"&gt;Pull API
&lt;/h2&gt;&lt;p&gt;Armed with this understanding of how a &lt;code&gt;Stream&lt;/code&gt; works we can actually access the underlying representation type,
&lt;code&gt;Pull&lt;/code&gt;. Given an arbitrary stream we can call &lt;code&gt;.pull&lt;/code&gt; to convert a &lt;code&gt;Stream&lt;/code&gt; into a &lt;code&gt;Pull&lt;/code&gt;. This type covers some
internal implementation details but, in a nutshell, a &lt;code&gt;Pull&lt;/code&gt; monad represents a single &amp;ldquo;step&amp;rdquo; of the underlying
representation. For example, a single &lt;code&gt;Pull&lt;/code&gt; might represent polling a connection for new response bytes.&lt;/p&gt;
&lt;p&gt;Technically &lt;code&gt;.pull&lt;/code&gt; returns a &lt;code&gt;ToPull&lt;/code&gt; type. Again, this type covers some internal implementation details but acts as a
springboard for calls like &lt;code&gt;uncons&lt;/code&gt; or &lt;code&gt;peek&lt;/code&gt;, which can optionally consume the current chunk. Operations on this type
always yield a &lt;code&gt;Pull&lt;/code&gt; so for our purposes I will focus on that.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;Pull&lt;/code&gt; type can cause confusion because it has three type parameters.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Pull[F, O, R]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▲ ▲ ▲
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ │ └─────── Return Type
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ └────────── Output Type
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └───────────── Effect Type
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The effect type simply represents the monad in use (e.g. &lt;code&gt;IO&lt;/code&gt;). The output type represents the type of the parent
&lt;code&gt;Stream&lt;/code&gt;. The return type represents a &amp;ldquo;working&amp;rdquo; type to allow use to compose multiple &lt;code&gt;Pull&lt;/code&gt;s into a single stream
step. We can only collapse &lt;code&gt;Pull&lt;/code&gt;s back to a normal &lt;code&gt;Stream&lt;/code&gt; if they return a &lt;code&gt;Unit&lt;/code&gt; type (signalling they are
&amp;ldquo;done&amp;rdquo;), using the &lt;code&gt;Pull.stream&lt;/code&gt; API. This distinction can make &lt;code&gt;Pull&lt;/code&gt; a confusing type to work with at first.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;pull&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Pull&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;, &lt;span class="kt"&gt;String&lt;/span&gt;, &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nc"&gt;Pull&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pure&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;_&lt;/span&gt; &lt;span class="k"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nc"&gt;Pull&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;output1&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;, &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pull&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Pull&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;Pull.output1&lt;/code&gt; emits the given value as an element in our stream. &lt;code&gt;Pull.done&lt;/code&gt; just represents a &lt;code&gt;Pull[F, Nothing, Unit]&lt;/code&gt;
and acts as a convenient way of signalling the current pull has finished outputting any elements.&lt;/p&gt;
&lt;p&gt;You can define custom steps on an existing &lt;code&gt;Stream&lt;/code&gt; by using &lt;code&gt;.repeatPull&lt;/code&gt;. This gives you a high degree of control over
when and how we pull elements from the upstream. This function provides a base &lt;code&gt;ToPull&lt;/code&gt; and asks you to return an
&lt;code&gt;Option[Stream[F, A]]&lt;/code&gt; to indicate if and how the stream should continue. An interesting property of this stage is that
we don&amp;rsquo;t have to return elements from our upstream definition. We can insert new elements or switch to a different
stream entirely.&lt;/p&gt;
&lt;h2 id="motivation-and-summary"&gt;Motivation and Summary
&lt;/h2&gt;&lt;p&gt;FS2&amp;rsquo;s &lt;code&gt;Stream&lt;/code&gt; already provides a lot of powerful abstractions for working on a stream without dipping into the pull
API, so why might you want to?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Performant consumption of chunks.&lt;/strong&gt; Individually stream operations can provide either full chunks (via &lt;code&gt;.chunks&lt;/code&gt;) or
single elements. If you ever want to combine elements across chunks or dynamically merge chunks then you will need to
pull and merge chunks yourself.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Single step logic.&lt;/strong&gt; Sometimes you might want to partially consume a stream before making a decision on how to
process the full stream. The pull API allows you to arbitrarily consume the Stream without committing to a single
strategy.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Full control over execution.&lt;/strong&gt; You might have a sensitive upstream API where you want to carefully choose when
and how effects get executed.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Understanding how a &lt;code&gt;Stream&lt;/code&gt; works underneath can help you intuit what a stream can (and can&amp;rsquo;t) do, as well as
diagnosing any performance issues. The pull API unlocks performance improvements around chunk handling that the
typical &lt;code&gt;Stream&lt;/code&gt; interface might not provide. This might seem like a dense topic at first, but sometimes the best way
to learn something means using it. Then you should find the concepts click into place (and if they don&amp;rsquo;t, I
apologise unreservedly).&lt;/p&gt;</description></item><item><title>On the Dangers of Saying Things</title><link>https://comforting-babka-f6ed67.netlify.app/p/on-the-dangers-of-saying-things/</link><pubDate>Thu, 22 Jan 2026 00:00:00 +0000</pubDate><guid>https://comforting-babka-f6ed67.netlify.app/p/on-the-dangers-of-saying-things/</guid><description>&lt;p&gt;I struggle when it comes to writing something publicly. The things you say reflect on you as an individual, but usually
my reluctance comes from my belief that what I have to say carries little importance. Publishing something publicly
requires the belief that you have something worth sharing. You need to keep believing this even if no one hears it at
all! This strong level of conviction to scream into the void doesn&amp;rsquo;t come naturally. Few people possess that level of
self-assurance, and rarely do people who think they have something worth listening to also care about hearing something
back.&lt;/p&gt;
&lt;p&gt;So why say anything at all? It does have an air of hypocrisy to it, complaining about the self-serving nature of blogging.
Ultimately, I think the act of writing in this form has some benefits. Life doesn&amp;rsquo;t need permission to exist any more
than we need permission to have an opinion. Our views and collected knowledge only strengthen from writing them down,
and publishing them creates pressure to refine and improve our ideas. I&amp;rsquo;m not trying to do anything more here than
say, &amp;ldquo;Hey, I exist, and maybe what I have to say makes you feel like you exist too&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;I am the target reader of this post. I would like to write posts that me from another time might like to read. If you
like them too, then by all means, keep reading.&lt;/p&gt;</description></item><item><title>21st Century Baby Brain</title><link>https://comforting-babka-f6ed67.netlify.app/p/21st-century-baby-brain/</link><pubDate>Fri, 26 Sep 2025 00:00:00 +0000</pubDate><guid>https://comforting-babka-f6ed67.netlify.app/p/21st-century-baby-brain/</guid><description>&lt;p&gt;I recently became a Dad (for the first time). We don’t want to share photos online, but trust me — this baby looks
great. Beautiful blue eyes matched with amber hair, and a screaming cry that bursts both eardrums! While on paternity
leave for the last two months we’ve had to overcome a lot of challenges. A lot of these make me reflect on the way I
operate as a software engineer of nearly a decade now, or even as a human-being of thirty years.&lt;/p&gt;
&lt;h2 id="no-such-thing-as-good-advice"&gt;No such thing as good advice
&lt;/h2&gt;&lt;p&gt;The art of raising a child comes with a lot of advice. Some of it scientifically based, others from years of experience,
and some just plain misguided or wrong. In truth these unique little creatures have many needs, and you can&amp;rsquo;t always
apply the right advice at the right time. While they may share a lot of themes each newborn still has a unique
experience. Our ability to read these cues (or miss them completely) usually becomes the cause of a lot of our
day-to-day frustrations.&lt;/p&gt;
&lt;p&gt;When we meet challenges in our work projects we try to fall back on the rules we know. As developers, we have a special
relationship with rules because we spend most of our days searching for and implementing them. Best practices can even
become our identities as we spend our careers advocating for them. When those rules don&amp;rsquo;t work we get frustrated because
things &lt;em&gt;should&lt;/em&gt; get better, because we did everything “right”. In those situations people often double down, we conclude
that we just didn&amp;rsquo;t implement our rules properly. In truth the rules don&amp;rsquo;t always work. Good advice can fail in the
wrong moment, or it solves the wrong problem.&lt;/p&gt;
&lt;p&gt;In a lot of situations our little one, and us, find ourselves frustrated because we don&amp;rsquo;t understand each other.
Programming errors always have a cause (even when they come from unexpected places). You can debug a program to get to
the root of the problem, but sadly you can&amp;rsquo;t debug a newborn baby. You can&amp;rsquo;t debug a human adult either. Despite this we
still try to apply our rules to everyday interactions in a work project. We want to codify our processes because it
makes us feel better — even when the Agile manifesto tells us to put “people over processes”. Instead, if we listened to
the right cues we might avoid a lot of disagreements over the best way to approach a project.&lt;/p&gt;
&lt;h2 id="you-cant-spoil-a-baby"&gt;You can&amp;rsquo;t spoil a baby
&lt;/h2&gt;&lt;p&gt;Something you should hear a lot as a new parent — “You can’t spoil a baby”. Newborn babies need to know you hear their
cries and will appear when you need them. In those early days they need as much reassurance and love as possible. This
stays true for their childhood, teenage years, and beyond. Children need to know that you understand and accept their
feelings, even if you don’t agree with them. This allows them to build better emotional understanding and therefore
tools to manage their emotions later in life. I think this stays true for adults too, we need to approach conflict in
life with love and understanding. Everyone in the room should want to achieve the best outcome and come together in good
faith. By showing kindness to each other we make it so that people enjoy working with us, and in turn has the desire do
good work.&lt;/p&gt;
&lt;h2 id="no-walking-away"&gt;No walking away
&lt;/h2&gt;&lt;p&gt;This kind of empathy takes an enormous amount of effort to pull off sometimes. Not every tantrum from a baby feels
justified, and in a sleep-deprived state it stings even more. When a programming problem gets us down we can usually
close our laptops and walk away. You can&amp;rsquo;t say the same for a newborn baby, the task at hand still needs doing. We need
to find the will to push through and achieve our goal, or find a compromise that works for the situation. Sometimes for
me that means walking up and down the stairs until she falls asleep.&lt;/p&gt;
&lt;p&gt;The software we build doesn&amp;rsquo;t handle life and death most of the time. But it can handle high stakes decisions; payments
taken, payments not taken, forms submitted, or deadlines missed. I think the best software design stays flexible. It
should cope with all states (illegal or not), all inputs, and where possible all outputs too. We often throw exceptions
or halt our execution flow when things go wrong. Instead, we could return sensible defaults, or define our state model
with more flexibility.&lt;/p&gt;
&lt;h2 id="changing-worlds"&gt;Changing worlds
&lt;/h2&gt;&lt;p&gt;These days some people write their content with ChatGPT, I wrote this with a baby on my lap. She uses fewer emojis than
ChatGPT and hasn&amp;rsquo;t made a single useful suggestion yet! These AI agents have only just exploded into our lives, but for
her, they will seem normal and common-place. In my lifetime we started with basic brick phones, before touchscreen
smartphones became the new thing, and eventually became old too. Things that feel new and exciting to us now might pale
compared to what comes next — something equally exciting and daunting to think about. Life will always keep changing
around us, and I look forward to seeing it all through her eyes.&lt;/p&gt;
&lt;p&gt;Right now, Edith the baby just learned to smile. She loves to stand (with Dad’s support) while smiling and dribbling
everywhere. I return to the working world with a little resentment and a little excitement.&lt;/p&gt;</description></item><item><title>Enjoy The View</title><link>https://comforting-babka-f6ed67.netlify.app/p/enjoy-the-view/</link><pubDate>Tue, 20 Aug 2024 00:00:00 +0000</pubDate><guid>https://comforting-babka-f6ed67.netlify.app/p/enjoy-the-view/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Aimed at relatively new Scala developers or those unfamiliar with Scala collection performance outside the usual &lt;code&gt;List&lt;/code&gt; or &lt;code&gt;Seq&lt;/code&gt; types.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Most of the common collection types in Scala are implemented strictly; this means that at all times every element of the collection is calculated and stored in memory. This can have performance impacts if our collections come from expensive operations, contain a high number of objects, or contain particularly large objects. Some methods such as &lt;code&gt;find&lt;/code&gt; or &lt;code&gt;head&lt;/code&gt; don’t need to read every element of the collection and so using strict collections isn&amp;rsquo;t always necessary. A good example of a collection that benefits from being lazy is the linked list (Scala&amp;rsquo;s &lt;code&gt;List&lt;/code&gt; type). Linked lists are implemented as an element and a pointer to the next element, instead we can make the collection lazy by storing a function for the next pointer instead. See the example below for how this works.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;strictList = 1 -&amp;gt; 2 -&amp;gt; 3 -&amp;gt; 4 -&amp;gt; 5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lazyList = 1 -&amp;gt; (n =&amp;gt; n + 1)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Lists in Scala are not lazy by default and so if we want this behaviour we&amp;rsquo;ll actually need to use &lt;code&gt;List&lt;/code&gt;&amp;rsquo;s sister class &lt;code&gt;LazyList&lt;/code&gt;. This type works a lot like a list but instead the list is stored as an element and a &amp;ldquo;thunk&amp;rdquo;, a function stored on the heap which can be used to calculate the next element. Now when we use our &lt;code&gt;find&lt;/code&gt; or &lt;code&gt;head&lt;/code&gt; operations we only calculate as many elements as we need rather than creating elements unnecessarily. It also opens up some interesting algorithm designs that make use of infinite lists. For example, implementing a &lt;code&gt;zipWithIndex&lt;/code&gt; function would look like this.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;scala&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;LazyList&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;range&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MaxValue&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="n"&gt;zip&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;a cool example&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="n"&gt;toList&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;res0&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;[(&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;, &lt;span class="kt"&gt;Char&lt;/span&gt;&lt;span class="o"&gt;)]&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Notice how the initial &lt;code&gt;LazyList.range(0, Int.MaxValue)&lt;/code&gt; goes all the way to the maximum integer. If you replaced the &lt;code&gt;LazyList&lt;/code&gt; with a regular strict &lt;code&gt;List&lt;/code&gt; you’ll find the program takes a lot longer to complete (if it even does at all).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Lazy lists actually retain their elements after they&amp;rsquo;re calculated so an infinitely sized &lt;code&gt;LazyList&lt;/code&gt; can still risk overrunning your heap memory! If you want to discard elements you&amp;rsquo;ll need to work recursively (with tail call optimization)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What if we&amp;rsquo;re not working with a lazy collection? We can actually work with any collection lazily by using a &lt;code&gt;View&lt;/code&gt;. You can summon a view for a collection by using &lt;code&gt;.view&lt;/code&gt; and continue to use the familiar collection traversal methods. The upside is that every operation we do will be evaluated lazily just like the lazy list!&lt;/p&gt;
&lt;p&gt;This example shows how the order of operations differs between a strict list and a lazy view on the same collection.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;normalList&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;range&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;viewedList&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="n"&gt;normalList&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;tappedList&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="n"&gt;normalList&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tapEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Operation A&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="n"&gt;tapEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Operation B&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;tappedView&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="n"&gt;viewedList&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tapEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Operation A&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="n"&gt;tapEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Operation B&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Not done yet?&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// realise the view
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;tappedView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toList&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Run this snippet and you&amp;rsquo;ll see something interesting.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Operation A
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Operation A
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Operation B
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Operation B
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Not done yet?
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Operation A
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Operation B
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Operation A
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Operation B
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The strict list evaluates the entire list first for the first &lt;code&gt;tapEach&lt;/code&gt;, and then evaluates it again for the second &lt;code&gt;tapEach&lt;/code&gt; (meaning we&amp;rsquo;ve actually crossed the whole list twice). In the view however the operations are actually combined making our update more efficient. Similarly we actually don&amp;rsquo;t see the output of the view until we force it back into a list later on (sometimes referred to as &amp;ldquo;realising&amp;rdquo; the list). A view isn&amp;rsquo;t always the right choice for the job. Views are great for traversal but don&amp;rsquo;t support functions that might access the collection in a random order (for example, sorting). Storing unevaluated thunks can also sometimes be more costly then simply evaluating the collection to begin with, for example situations where we might build a lot of views in one go.&lt;/p&gt;</description></item><item><title>Turning Docker Compositions into Test Suites</title><link>https://comforting-babka-f6ed67.netlify.app/p/turning-docker-compositions-into-test-suites/</link><pubDate>Wed, 06 Sep 2023 00:00:00 +0000</pubDate><guid>https://comforting-babka-f6ed67.netlify.app/p/turning-docker-compositions-into-test-suites/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Use Docker compositions in tests with Test-Containers and ScalaTest.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/AdamJKing/blackbox-testing-sample" target="_blank" rel="noopener"
&gt;Sample code repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We use &lt;code&gt;docker-compose&lt;/code&gt; as a tool that can build, deploy, and manage multiple containers on the same
network. These configurations, known as compositions, remove a lot of the manual setup we often associate with running
infrastructure locally. It provides local versions of dependencies for a faster, more realistic
feedback loop when developing applications.&lt;/p&gt;
&lt;p&gt;Our application level tests could look as simple as this;&lt;/p&gt;
&lt;script src="https://emgithub.com/embed-v2.js?target=https%3A%2F%2Fgithub.com%2FAdamJKing%2Fblackbox-testing-sample%2Fblob%2Feed9b2a6bef9fa2049c9be1908415ee43c3a9347%2Fintegration-tests%2Fsrc%2Ftest%2Fscala%2Fblackbox%2Ftesting%2Fsample%2FAppSpec.scala%23L19-L34&amp;style=a11y-light&amp;type=code&amp;showBorder=on&amp;showLineNumbers=on&amp;showFileMeta=on&amp;showFullPath=on&amp;showCopy=on"&gt;&lt;/script&gt;
&lt;h3 id="test-containers"&gt;Test Containers
&lt;/h3&gt;&lt;p&gt;For our tests we will use &lt;a class="link" href="https://github.com/testcontainers/testcontainers-scala" target="_blank" rel="noopener"
&gt;test-containers&lt;/a&gt;. It offers us;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Access to a Docker context from within tests.&lt;/li&gt;
&lt;li&gt;Integration with test harnesses provided by popular testing frameworks.&lt;/li&gt;
&lt;li&gt;The automatic creation and clean-up of Docker containers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;Test-Containers&lt;/code&gt; has a &lt;code&gt;Docker-Compose&lt;/code&gt; plugin that allows us to run compositions from our tests. First, let’s define a
simple composition file.&lt;/p&gt;
&lt;script src="https://emgithub.com/embed-v2.js?target=https%3A%2F%2Fgithub.com%2FAdamJKing%2Fblackbox-testing-sample%2Fblob%2Feed9b2a6bef9fa2049c9be1908415ee43c3a9347%2Fdocker-compose.yaml&amp;style=a11y-light&amp;type=code&amp;showBorder=on&amp;showLineNumbers=on&amp;showFileMeta=on&amp;showFullPath=on&amp;showCopy=on"&gt;&lt;/script&gt;
&lt;p&gt;I’ve kept it simple by only including our application, but from this point on I can extend it with whatever setup we
need without having to change our tests at all. To start using this in our tests we’ll need to do three things.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Build and publish our application image locally.&lt;/li&gt;
&lt;li&gt;Integrate our composition setup into our test-suite.&lt;/li&gt;
&lt;li&gt;Create a client that can call our application.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="1-build-and-publish-our-application-image-locally"&gt;1. Build and publish our application image locally.
&lt;/h3&gt;&lt;p&gt;Before we can use it in a test suite we need to build our application into a Docker image. Unfortunately
&lt;code&gt;Docker-Compose&lt;/code&gt; does not have a concept of running external build tasks to get an image. That said most build tools
will
allow us to build an image before running our test suite. With SBT
and &lt;a class="link" href="https://www.scala-sbt.org/sbt-native-packager/" target="_blank" rel="noopener"
&gt;native-packager&lt;/a&gt; plugin makes it easy to add our Docker build stage
as a prerequisite for our integration tests.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;An SBT quirk means we need to specify our Docker build step for every test task.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Test / test := (Test / test).dependsOn(server / Docker / publishLocal).value,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Test / testOnly := (Test / testOnly).dependsOn(server / Docker / publishLocal).evaluated,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Test / testQuick := (Test / testQuick).dependsOn(server / Docker / publishLocal).evaluated
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="2-integrate-our-composition-setup-into-our-test-suite"&gt;2. Integrate our composition setup into our test-suite.
&lt;/h3&gt;&lt;p&gt;Next, we add the image details to our tests.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Test-Containers&lt;/code&gt; library has integrations for many test frameworks, which makes this easier, but we’ll still need
to do some wiring. The &lt;code&gt;Docker-Compose&lt;/code&gt; module needs to know the location of our compose-file. You might want to
hard-code this but that affects the portability of your tests. Instead, I recommend passing the location in as a
property and using SBT to locate the file. This has the benefits us by enabling testing against multiple compose-files;
useful for testing against different environments or setups.&lt;/p&gt;
&lt;script src="https://emgithub.com/embed-v2.js?target=https%3A%2F%2Fgithub.com%2FAdamJKing%2Fblackbox-testing-sample%2Fblob%2F388fe4e99956353c83e99f38f60a1c90f903c3fc%2Fbuild.sbt%23L39&amp;style=a11y-light&amp;type=code&amp;showBorder=on&amp;showLineNumbers=on&amp;showFileMeta=on&amp;showFullPath=on&amp;showCopy=on"&gt;&lt;/script&gt;
&lt;p&gt;If you use an IDE to run test-suites you may need to update the runner config before this will work. In IntelliJ, you
can save a run configuration and share it by committing it to the repo.&lt;/p&gt;
&lt;p&gt;We need to wait for our containers to start before using them. We can get around this by making our access details lazy
or use the Test-Containers helpers (which run after the containers finish starting).&lt;/p&gt;
&lt;script src="https://emgithub.com/embed-v2.js?target=https%3A%2F%2Fgithub.com%2FAdamJKing%2Fblackbox-testing-sample%2Fblob%2Fmain%2Fintegration-tests%2Fsrc%2Ftest%2Fscala%2Fblackbox%2Ftesting%2Fsample%2FAppFixture.scala%23L34-L36&amp;style=a11y-light&amp;type=code&amp;showBorder=on&amp;showLineNumbers=on&amp;showFileMeta=on&amp;showFullPath=on&amp;showCopy=on"&gt;&lt;/script&gt;
&lt;p&gt;Lastly we need to instruct Test-Containers on how to use our containers.&lt;/p&gt;
&lt;script src="https://emgithub.com/embed-v2.js?target=https%3A%2F%2Fgithub.com%2FAdamJKing%2Fblackbox-testing-sample%2Fblob%2Fabed609fdbe0c4a17dfedebf7005bbc89eead893%2Fintegration-tests%2Fsrc%2Ftest%2Fscala%2Fblackbox%2Ftesting%2Fsample%2FAppFixture.scala%23L39-L47&amp;style=a11y-light&amp;type=code&amp;showBorder=on&amp;showLineNumbers=on&amp;showFileMeta=on&amp;showFullPath=on&amp;showCopy=on"&gt;&lt;/script&gt;
&lt;p&gt;Some additional considerations you might make;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We added a wait condition for the service’s health-check so that &lt;code&gt;Test-Containers&lt;/code&gt; knows to start testing. For more
complex start conditions check out
the &lt;a class="link" href="https://java.testcontainers.org/features/startup_and_waits/#:~:text=not%20a%20daemon.-,Wait%20Strategies,container%20is%20ready%20for%20use" target="_blank" rel="noopener"
&gt;test-containers documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;We only listen to logs of the main container as logging everything at once creates too much noise. To help with that
we can either:
&lt;ul&gt;
&lt;li&gt;Set noisy application’s log-levels through the Docker environment.&lt;/li&gt;
&lt;li&gt;Use Test-Containers logging classes to build something custom.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="3-create-a-client-to-call-our-application"&gt;3. Create a client to call our application.
&lt;/h3&gt;&lt;p&gt;Finally let’s create our HTTP client to call our service. We have full access to the Docker containers and, if needed,
we can connect to our dependencies directly. Alternatively if you provide clients for your service you can use them
here instead.&lt;/p&gt;
&lt;script src="https://emgithub.com/embed-v2.js?target=https%3A%2F%2Fgithub.com%2FAdamJKing%2Fblackbox-testing-sample%2Fblob%2Fabed609fdbe0c4a17dfedebf7005bbc89eead893%2Fintegration-tests%2Fsrc%2Ftest%2Fscala%2Fblackbox%2Ftesting%2Fsample%2FAppClient.scala%23L12-L24&amp;style=a11y-light&amp;type=code&amp;showBorder=on&amp;showLineNumbers=on&amp;showFileMeta=on&amp;showFullPath=on&amp;showCopy=on"&gt;&lt;/script&gt;
&lt;h2 id="summary"&gt;Summary
&lt;/h2&gt;&lt;p&gt;With our test fixture code in place we can now start writing code without too much concern about the state of our
containers.&lt;/p&gt;
&lt;script src="https://emgithub.com/embed-v2.js?target=https%3A%2F%2Fgithub.com%2FAdamJKing%2Fblackbox-testing-sample%2Fblob%2Feed9b2a6bef9fa2049c9be1908415ee43c3a9347%2Fintegration-tests%2Fsrc%2Ftest%2Fscala%2Fblackbox%2Ftesting%2Fsample%2FAppSpec.scala%23L19-L34&amp;style=a11y-light&amp;type=code&amp;showBorder=on&amp;showLineNumbers=on&amp;showFileMeta=on&amp;showFullPath=on&amp;showCopy=on"&gt;&lt;/script&gt;
&lt;p&gt;Now if we hit a test failure we can launch our &lt;code&gt;docker-composition&lt;/code&gt; to debug our test case. This helps us switch to an
iterative, live feedback loop for manual testing and experimentation.&lt;/p&gt;</description></item><item><title>Custom Request DSLs with Http4s</title><link>https://comforting-babka-f6ed67.netlify.app/p/custom-request-dsls-with-http4s/</link><pubDate>Thu, 30 Mar 2023 00:00:00 +0000</pubDate><guid>https://comforting-babka-f6ed67.netlify.app/p/custom-request-dsls-with-http4s/</guid><description>&lt;blockquote&gt;
&lt;p&gt;How &lt;code&gt;Http4s&lt;/code&gt; leverages Scala’s powerful pattern-matching features, and how you can use them too.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I’ve recently played around with &lt;code&gt;Http4s&lt;/code&gt;’s routing DSL and added some custom mechanics of my own.
Sometimes we want to access request information generically independent if the route’s normal purpose. &lt;code&gt;Http4s&lt;/code&gt;'
&lt;code&gt;AuthedRoutes&lt;/code&gt; serves as a great example for this.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;normal&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;HttpRoutes&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HttpRoutes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;GET&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;user&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nc"&gt;UserId&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;resource&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;authed&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AuthedRoutes&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;UserId&lt;/span&gt;, &lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AuthedRoutes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;GET&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;user&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;resource&amp;#34;&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The second route shows an example of using some new DSL syntax. That small &lt;code&gt;as user&lt;/code&gt; at the end of the second route
definition does a lot of heavy lifting around user authentication. Even better as it modifies the route type itself we
can&amp;rsquo;t forget to add authentication to a route (which we might if added auth route by route).&lt;/p&gt;
&lt;p&gt;For my change I will focus on a relatively neutral change that works with the normal &lt;code&gt;Http4s&lt;/code&gt; request and response
types. Let’s work with a simple requirement.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Modify the route’s behaviour based on the &lt;code&gt;User-Agent&lt;/code&gt; of the caller.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;How might that look? Let’s sketch out a possible DSL.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nc"&gt;HttpRoutes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;Service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Distributor&lt;/span&gt; &lt;span class="n"&gt;using&lt;/span&gt; &lt;span class="nc"&gt;GET&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Root&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;resource&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nc"&gt;IntVar&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;To understand how we might add this we need to explore how &lt;code&gt;Http4s&lt;/code&gt; cleverly uses Scala’s pattern match system. When we
pattern match on a case class Scala invokes a method called &lt;code&gt;unapply&lt;/code&gt; to decide if the given value “matches”. Typically,
that method might look something like this.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;Foo&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;unapply&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Foo&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;In this case the implementation knows how to decompose a &lt;code&gt;Foo&lt;/code&gt; into a &lt;code&gt;Int&lt;/code&gt;, by using the class&amp;rsquo; &lt;code&gt;foo&lt;/code&gt; field. Sometimes
we can&amp;rsquo;t access a field like that (for example, an ADT) and so the optional return type allows us to indicate the match
failed. This method doesn&amp;rsquo;t have to be synthetic and Scala allows us to define our own custom &lt;code&gt;unapply&lt;/code&gt; matchers, which
Scala refers to as &lt;a class="link" href="https://docs.scala-lang.org/tour/extractor-objects.html" target="_blank" rel="noopener"
&gt;extractor objects&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Http4s&lt;/code&gt; itself has some great examples of when we might want to use extractors. The status matcher &lt;code&gt;Succesful&lt;/code&gt; matches
response statuses within the 2xx range, with similar matchers for 4xx and 5xx too. This showcases the first style of
matcher — a way of grouping similar terms in a match.&lt;/p&gt;
&lt;p&gt;You can nest Scala’s matchers in the same way that you can nest data. For example;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;Right&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Some&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// ^ ^
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// | |_ Then it applies the `Option` matcher to the result.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// |
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// |_ First applies the `Either` matcher
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This nesting also powers the familiar list matcher.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;:&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt; &lt;span class="err"&gt;2&lt;/span&gt; &lt;span class="kt"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Nil&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// really looks like
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="k"&gt;:&lt;/span&gt;&lt;span class="kt"&gt;:&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;::&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;::&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;3&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;::&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Nil&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This syntax eliminates the normal bracket syntax see in favour of something more readable. Seeing this you might see how
the &lt;code&gt;Http4s&lt;/code&gt; DSL takes shape. Using DSL objects (made available via the &lt;code&gt;Htp4sDSL&lt;/code&gt;) trait we can string a series of
matchers along to define our expected request structure. Checking the source code gives us an idea on how to structure
our own custom matcher.&lt;/p&gt;
&lt;p&gt;The first component of a &lt;code&gt;Http4s&lt;/code&gt; route:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="cm"&gt;/** HttpMethod extractor:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * {{{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * (request.method, Path(request.path)) match {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * case Method.GET -&amp;gt; Root / &amp;#34;test.json&amp;#34; =&amp;gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * }}}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;unapply&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="o"&gt;]](&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Request&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Some&lt;/span&gt;&lt;span class="o"&gt;[(&lt;/span&gt;&lt;span class="kt"&gt;Method&lt;/span&gt;, &lt;span class="kt"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;)]&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;It takes as input the inbound request itself and outputs two chunks split from the request; the method and the path.
Through the same syntax as &lt;code&gt;::&lt;/code&gt; that we saw earlier; Scala can match the request as a method on the left and a path on
the
right. Scala also allows us to match against specific values, so we can further refine our match with an exact method.
The path however gets fed to another &lt;code&gt;Path&lt;/code&gt; matcher, which decomposes the value further.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;object / {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; def unapply(path: Path): Option[(Path, String)] = ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This path matcher consumes a &lt;code&gt;Path&lt;/code&gt; and splits to a &lt;code&gt;String&lt;/code&gt; component on its right-hand side. The left-hand side
returns another &lt;code&gt;Path&lt;/code&gt; so we can decompose the path string as much as we need to.&lt;/p&gt;
&lt;p&gt;You should see a pattern emerging, and we can start implementing our own. Let’s revisit our desired syntax.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nc"&gt;HttpRoutes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;Service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Distributor&lt;/span&gt; &lt;span class="n"&gt;using&lt;/span&gt; &lt;span class="nc"&gt;GET&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Root&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;resource&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nc"&gt;IntVar&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;We can start building our matcher by looking at our preferred inputs and outputs. In this case we will need:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nc"&gt;Input&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Request&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nc"&gt;Output&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Service&lt;/span&gt; &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Request&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;It takes a request and returns it plus a newly created &lt;code&gt;Service&lt;/code&gt; object. Returning the request enables us to continue
matching on it down the line. A trivial implementation looks a little like this;&lt;/p&gt;
&lt;script src="https://scastie.scala-lang.org/s5swyEcORHGz0wYU67AZ4w.js"&gt;&lt;/script&gt;
&lt;blockquote&gt;
&lt;p&gt;Notice the &lt;code&gt;unapply&lt;/code&gt; method actually has a generic type parameter. Pattern matches do not support type parameters so
if you include them they must be inferred from the matcher input.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The logic here could become more complex if we wanted it to, for example we could extract a specific user-agent version
too. Like &lt;code&gt;AuthedRoutes&lt;/code&gt; the best syntax additions are obvious and limited. By changing the order of the outputs we
could move our syntax the end of the match rather than the front. Experimenting with the values you extract in your
match brings up some interesting possibilities. Take &lt;code&gt;Http4s&lt;/code&gt;&amp;rsquo; &lt;code&gt;-&amp;gt;&amp;gt;&lt;/code&gt; matcher as an example.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;/**&lt;/span&gt; &lt;span class="n"&gt;Extractor&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="n"&gt;an&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;then&lt;/span&gt; &lt;span class="n"&gt;enumerate&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="n"&gt;supported&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;{{{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ne"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;withMethod&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Root&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;test.json&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;withMethod&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GET&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;}}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Returns&lt;/span&gt; &lt;span class="n"&gt;an&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;matched&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;accordance&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;datatracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ietf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;rfc7231&lt;/span&gt;&lt;span class="c1"&gt;#section-4.1 RFC7231]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;unapply&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;Applicative&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;PartialFunction&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;]]]&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="ne"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Returning a partial-function yields control back to the caller, who can add their own custom behaviour. This allows for
some powerful DSLs that can make defining request-dependent service behaviour a breeze.&lt;/p&gt;
&lt;p&gt;The big question is… &lt;em&gt;should you?&lt;/em&gt; This technique can help simplify a lot of code, but it can make
it worse too. Common drawbacks and criticisms of custom DSLs include;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Confusing or pointlessly obtuse syntax (for example, &lt;code&gt;|@|&lt;/code&gt; from cats).&lt;/li&gt;
&lt;li&gt;They can hide too much. Subtle behaviours introduced through syntax can feel “magic”.&lt;/li&gt;
&lt;li&gt;Newcomers need to learn your syntax on top of a language they might not know already.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You and your team decide how far you want to take it. A part of the art of software development includes deciding when
to use these kinds of techniques.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.”
― Antoine de Saint-Exupéry&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Even small uses can benefit from custom matchers, for example this…&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;Left&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Some&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cutOffTime&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;…into this…&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;LiveEvent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Which reduces cramped matchers into something simple and self-documenting.&lt;/p&gt;</description></item><item><title>Archives</title><link>https://comforting-babka-f6ed67.netlify.app/archives/</link><pubDate>Sun, 06 Mar 2022 00:00:00 +0000</pubDate><guid>https://comforting-babka-f6ed67.netlify.app/archives/</guid><description/></item><item><title>Links</title><link>https://comforting-babka-f6ed67.netlify.app/links/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://comforting-babka-f6ed67.netlify.app/links/</guid><description>&lt;p&gt;To use this feature, add &lt;code&gt;links&lt;/code&gt; section to frontmatter.&lt;/p&gt;
&lt;p&gt;This page&amp;rsquo;s frontmatter:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;links&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;GitHub&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;GitHub is the world&amp;#39;s largest software development platform.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;website&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;https://github.com&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;TypeScript&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;website&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;https://www.typescriptlang.org&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ts-logo-128.jpg&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;image&lt;/code&gt; field accepts both local and external images.&lt;/p&gt;</description></item><item><title>Search</title><link>https://comforting-babka-f6ed67.netlify.app/search/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://comforting-babka-f6ed67.netlify.app/search/</guid><description/></item></channel></rss>