<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Daniel Beskin&apos;s Blog</title><description>A blog on software and functional programming</description><link>https://blog.daniel-beskin.com</link><item><title>I Am Not a Functional Programmer</title><link>https://blog.daniel-beskin.com/2026-01-28-i-am-not-a-functional-programmer</link><guid isPermaLink="true">https://blog.daniel-beskin.com/2026-01-28-i-am-not-a-functional-programmer</guid><description>Despite rumors to the contrary, I am not actually a functional programmer. True, I sometimes slip and fall and an &quot;applicative functor&quot; would come out from under my breath. But surely you shouldn&apos;t…</description><pubDate>Wed, 28 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://blog.daniel-beskin.com/social-cards/2026-01-28-i-am-not-a-functional-programmer.png&quot; alt=&quot;Cover image for I Am Not a Functional Programmer&quot; style=&quot;width: 100%; height: auto; margin-bottom: 2rem;&quot;&gt;&lt;p&gt;Despite rumors to the &lt;a href=&quot;tags/functional-programming&quot;&gt;contrary&lt;/a&gt;, I am not actually a functional programmer. True, I sometimes slip and fall and an &quot;&lt;a href=&quot;https://traverse.consulting/&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;applicative functor&lt;/a&gt;&quot; would come out from under my breath. But surely you shouldn&apos;t judge me for this minor tick. So no, I&apos;m not a functional programmer, I&apos;m just trying to be reasonable&lt;sup&gt;&lt;a href=&quot;#user-content-fn-pun&quot; id=&quot;user-content-fnref-pun&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;We all want higher code quality, better testability, and less maintenance burden. Don&apos;t we? It just so happens that techniques from FP are really good at achieving these goals. We don&apos;t even have to mention functional programming to anyone, we can just write better code and call it a day&lt;sup&gt;&lt;a href=&quot;#user-content-fn-naming&quot; id=&quot;user-content-fnref-naming&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h2 id=&quot;spooky-action-at-a-distance&quot;&gt;Spooky Action at a Distance&lt;/h2&gt;
&lt;p&gt;Did it ever happen to you that you needed to implement some minor fix in one place in your codebase, and then suddenly code in another seemingly unrelated place started breaking? If you&apos;re lucky the breakage is something loud and clear like an exception. If you&apos;re less fortunate it will be some kind of silent and subtle logic bug that takes time and effort to pinpoint.&lt;/p&gt;
&lt;p&gt;When this happens we can say that our code suffers from &quot;spooky action at a distance&quot;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-spooky&quot; id=&quot;user-content-fnref-spooky&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;. I think that these days it&apos;s universally accepted that spooky action at a distance is bad for everyone involved. Who wants to live in a world where small changes have unexpected consequences all over the place?&lt;/p&gt;
&lt;p&gt;If we accept the premise that minimizing spooky action at a distance is a net positive for the maintainability of our code, the next question becomes, what are the sources of this malaise?&lt;/p&gt;
&lt;p&gt;One prominent source of spooky action is shared mutable data (both variables and data structures), especially high-visibility or even global data. When you mutate data what do you know about all the other potential users of that data? Are you willing to check them one by one to make sure you didn&apos;t break any invariants? Can you even tell who they are? Can you?&lt;/p&gt;
&lt;p&gt;If you&apos;re like most programmers you probably can&apos;t, or won&apos;t. You make the changes, cross your fingers, and pray for the best. But if we recognize all the pain and suffering that shared mutation brings into our lives (and I didn&apos;t even mention concurrency) a reasonable conclusion would be to avoid it like the plague. Mark everything as &lt;code&gt;const&lt;/code&gt;/&lt;code&gt;final&lt;/code&gt;, favor immutable data structures, and breathe a sigh of relief.&lt;/p&gt;
&lt;p&gt;You&apos;ll be surprised how good it feels. No need to defensively copy anything, no need to fear the consequences of mutating this or that input (you can&apos;t). Debugging becomes easier. Sure, you&apos;ll have to learn some new ways of solving problems, but spooky action at a distance is scientifically guaranteed to drop by at least 50%.&lt;/p&gt;
&lt;h2 id=&quot;mocking-the-world&quot;&gt;Mocking the World&lt;/h2&gt;
&lt;p&gt;Most programs do more than mutating variables. They do a whole lot of things, like talking to databases, or sending HTTP requests, and many other interactions with the big bad world. And that&apos;s perfectly normal. We&apos;re all conscientious developers though, and before we ship anything we must verify that it actually works as stated. Some of us even write tests to try and prove that, or so I hear.&lt;/p&gt;
&lt;p&gt;But my god, writing tests has become so tedious...&lt;/p&gt;
&lt;p&gt;Even for the smallest test that validates some simple business rule you have to set up 15 different mocks/spies/doubles/clones/whatever. One for the &lt;code&gt;DbFactoryProxyFactory&lt;/code&gt;, another for the &lt;code&gt;HttpCoordinationOrchestratorManipulator&lt;/code&gt;, and the list goes on and on. Usually these mocks tend to be &lt;a href=&quot;https://blog.daniel-beskin.com/2024-05-09-purify-tests&quot;&gt;annoyingly mutable&lt;/a&gt; and prone to race conditions.&lt;/p&gt;
&lt;p&gt;Sure, these days you can delegate some/most of this tedium to your handy AI, it won&apos;t complain (yet&lt;sup&gt;&lt;a href=&quot;#user-content-fn-ugly&quot; id=&quot;user-content-fnref-ugly&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;). Unfortunately, despite a common misconception, tests are not &quot;write only&quot;, they are also a form of executable system documentation. Even if you didn&apos;t have to write all that noise yourself, you might need to read it one day and the signal to noise ratio makes it a huge waste of time just trying to locate the code that actually matters.&lt;/p&gt;
&lt;p&gt;If we take a closer look at the sort of code that forces us to write these unpleasant tests, we are likely to find that it entangles&lt;sup&gt;&lt;a href=&quot;#user-content-fn-quantum&quot; id=&quot;user-content-fnref-quantum&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; too many things together. Or at least two of them: business logic and interactions with the external world.&lt;/p&gt;
&lt;p&gt;Unless you&apos;re doing some very complex external interactions it&apos;s quite likely that you should focus most of your testing effort on the business logic. Presumably that&apos;s also where most of the value for the business lies as well. Too bad it&apos;s so difficult.&lt;/p&gt;
&lt;p&gt;Since most of the complex mocking described above comes from the bits of code that interact with the external world, this should give us plenty of motivation to disentangle the business logic from the interaction code. Business logic tends to be simple in-memory computation (at least conceptually), testing it in isolation should be much simpler. Set up some inputs, assert some outputs, done. Written this way, the signal to noise ratio likely to go way up.&lt;/p&gt;
&lt;p&gt;The actual process of disentangling code might not be easy. It might even require some deep design changes in how you structure your code. But if you&apos;re serious about testing it is well worth the effort. And as the people from the cult of TDD will tell you, code that is easy to test tends to be of all around higher quality. You get this for free just by focusing on less tedious tests.&lt;/p&gt;
&lt;h2 id=&quot;feeling-a-bit-dry&quot;&gt;Feeling a Bit DRY&lt;/h2&gt;
&lt;p&gt;I don&apos;t like repeating myself&lt;sup&gt;&lt;a href=&quot;#user-content-fn-blog&quot; id=&quot;user-content-fnref-blog&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;, both because I&apos;m lazy and because it just itches me the wrong way to see the same code repeated again, and again, and again... I suspect that many programmers share this tendency naturally. And even if not, we can probably agree that more code equals more liability (tests, maintenance, bugs) and so when it doesn&apos;t obstruct readability, reducing code volume&lt;sup&gt;&lt;a href=&quot;#user-content-fn-golf&quot; id=&quot;user-content-fnref-golf&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;7&lt;/a&gt;&lt;/sup&gt; is probably for the best.&lt;/p&gt;
&lt;p&gt;Suppose that you&apos;re coding yet another shopping cart backend for whatever. You now create the cart checkout process. This would&apos;ve been straightforward but there are quite a few failure modes&lt;sup&gt;&lt;a href=&quot;#user-content-fn-rollbacks&quot; id=&quot;user-content-fnref-rollbacks&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;8&lt;/a&gt;&lt;/sup&gt;, and your team is obsessed with logging and error enrichment. So you end up with this code:&lt;/p&gt;
&lt;pre data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;processCustomerCheckout&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;r1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Validating inventory...&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;try&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;r1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;validateInventory&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Validating inventory failed:&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;Validating inventory: &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;r2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Calculating shipping...&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;try&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;r2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;calculateShipping&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;r1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Calculating shipping failed:&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;Calculating shipping: &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;r3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Processing payment...&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;try&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;r3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;processPayment&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;r2&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Processing payment failed:&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;Processing payment: &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;confirmOrder&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;r3&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are four steps to the checkout process: validating the inventory (1), calculating shipping costs (2), processing the payment (3), and confirming the order (4). Unfortunately all the error handling and logging that&apos;s interspersed between the steps makes it very difficult to actually see what&apos;s actually going on.&lt;/p&gt;
&lt;p&gt;But it&apos;s the repetitiveness that really gets me, I have to DRY it out to oblivion.&lt;/p&gt;
&lt;p&gt;If we look at what&apos;s fixed vs. changing we see that in each step we process the order and produce the output for the next step, and every step is wrapped with almost identical logging and error handling.&lt;/p&gt;
&lt;p&gt;One common way to reduce simple repetition like this is to use an anonymous function to extract the part that changes in the different steps&lt;sup&gt;&lt;a href=&quot;#user-content-fn-templatemethod&quot; id=&quot;user-content-fnref-templatemethod&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;9&lt;/a&gt;&lt;/sup&gt;. So we move the logging and error handling into a helper function like so:&lt;/p&gt;
&lt;pre data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;withLogging&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;stepFn&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stepName&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;stepName&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;try&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stepFn&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;stepName&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; failed:&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;stepName&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This new &lt;code&gt;withLogging&lt;/code&gt; helper function (1) takes a function argument for the step (&lt;code&gt;stepFn&lt;/code&gt;), the name for the current step, and the original &lt;code&gt;order&lt;/code&gt; input. It then applies the logging and error handling logic from before: log and call the &lt;code&gt;stepFn&lt;/code&gt; function on the order argument (2). If there is an exception (3), log, &quot;enrich&quot;, and rethrow it (4).&lt;/p&gt;
&lt;p&gt;We can now use &lt;code&gt;withLogging&lt;/code&gt; to wrap around our original steps and see the repetition melt away:&lt;/p&gt;
&lt;pre data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;processCustomerCheckout&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;r1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;withLogging&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;validateInventory&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Validating inventory&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;r2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;withLogging&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;calculateShipping&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Calculating shipping&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;r1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;r3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;withLogging&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;processPayment&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Processing payment&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;r2&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;confirmOrder&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;r3&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Much better, it&apos;s now easier to spot the building blocks of the flow. But it&apos;s still kind of annoying. With the plumbing still sticking out and obfuscating the main logic. Do we actually care about the logging strings? Or the fact that logging is present here?&lt;/p&gt;
&lt;p&gt;These are all good questions, but for me this is still just repetitive. So we&apos;ll use a bigger hammer to take it out. Let&apos;s hide away the logging completely by wrapping away the original functions:&lt;/p&gt;
&lt;pre data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;withLogging&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;stepFn&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stepName&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;stepName&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;try&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stepFn&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;stepName&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; failed:&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;stepName&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This &lt;code&gt;withLogging&lt;/code&gt; variant is very similar to the first one, except that it&apos;s no longer taking the &lt;code&gt;order&lt;/code&gt; as a top-level argument (1). Instead it produces a new function (2) that expects the &lt;code&gt;order&lt;/code&gt; argument and wraps around the invocation of &lt;code&gt;stepFn&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-decorator&quot; id=&quot;user-content-fnref-decorator&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;10&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;We use the new &lt;code&gt;withLogging&lt;/code&gt; function by pre-applying it to our building blocks:&lt;/p&gt;
&lt;pre data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;checkInventory&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;withLogging&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;validateInventory&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Validating inventory&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;withShipping&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;withLogging&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;calculateShipping&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Calculating shipping&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;withPayment&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;withLogging&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;processPayment&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Processing payment&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the wrapped building blocks we can rewrite the flow as follows:&lt;/p&gt;
&lt;pre data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;processCustomerCheckout&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;r1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;checkInventory&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;r2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;withShipping&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;r1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;r3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;withPayment&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;r2&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;confirmOrder&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;r3&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without all the logging noise, this is almost what we want, but for the last bit of obfuscation.&lt;/p&gt;
&lt;p&gt;Do we enjoy passing around the &lt;code&gt;r1, r2, r3&lt;/code&gt; variables? It&apos;s not just annoying, but also error prone, what if we add another step and forget to update the numbering appropriately&lt;sup&gt;&lt;a href=&quot;#user-content-fn-samevar&quot; id=&quot;user-content-fnref-samevar&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;11&lt;/a&gt;&lt;/sup&gt;?&lt;/p&gt;
&lt;p&gt;What we really want is to just say &quot;here are the steps, pipe them together&quot;, this should eliminate the last bit of repetition:&lt;/p&gt;
&lt;pre data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pipe&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;&lt;span&gt;fns&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;initialValue&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fns&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;reduce&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;initialValue&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are going a bit meta, but given a list of functions we create a new function that applies them one after the other starting with some &lt;code&gt;initialValue&lt;/code&gt;. Scary though it might be, here&apos;s the result:&lt;/p&gt;
&lt;pre data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;processCustomerCheckout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pipe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;checkInventory&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;withShipping&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;withPayment&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;confirmOrder&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Look at this beauty. No repetition, only business logic. Each building block is now a nice, testable, and reusable component.&lt;/p&gt;
&lt;p&gt;Speaking of reusable, it is now very easy to mix and match the components as we deem appropriate:&lt;/p&gt;
&lt;pre data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;processStorePickup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pipe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;checkInventory&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;withPayment&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;confirmOrder&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;generateQuote&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pipe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;checkInventory&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;createQuote&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We now support two new flows. Store pickup (1) no longer requires shipping, so we omit that step, and create another pipeline. Another flow just generates quotes (2), which requires no payments or shipping, but we add a new building block for quote generation. This is just as seamless as before, yet another clean and testable pipeline.&lt;/p&gt;
&lt;p&gt;Chasing DRY sure got us somewhere.&lt;/p&gt;
&lt;h2 id=&quot;im-just-trying-to-be-reasonable&quot;&gt;I&apos;m Just Trying to Be Reasonable&lt;/h2&gt;
&lt;p&gt;I don&apos;t think that many people would find what I wrote so far particularly controversial. It&apos;s the sort of stuff that might come up in a code or design review.&lt;/p&gt;
&lt;p&gt;Who would object to improving the maintainability of their code? Making code more testable? Great. Scratching the DRY itch? That&apos;s how I pass most of my days.&lt;/p&gt;
&lt;p&gt;All I&apos;m trying to do is be reasonable, and improve the code I write. The only difference between what I&apos;m doing and a random bunch of &quot;best practices&quot; for code improvement is the source of these practices. Without mentioning it, every single one of them is informed by functional programming.&lt;/p&gt;
&lt;p&gt;The fear of spooky action at a distance led us to immutability, the default in functional languages. The search for testability led us to the segregation of side-effecting code from pure code (business logic). Take this a step further and we get to &quot;&lt;a href=&quot;https://www.destroyallsoftware.com/screencasts/catalog/functional-core-imperative-shell&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;functional core, imperative shell&lt;/a&gt;&quot;. Lastly, the need for DRY sneakily got us into using &lt;a href=&quot;https://en.wikipedia.org/wiki/Higher-order_function&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;higher-order functions&lt;/a&gt; (&lt;code&gt;withLogging&lt;/code&gt; v1), &lt;a href=&quot;https://en.wikipedia.org/wiki/Partial_application&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;partial function application&lt;/a&gt; (&lt;code&gt;withLogging&lt;/code&gt; v2), and a &lt;a href=&quot;https://stackoverflow.com/questions/7533837/explanation-of-combinators-for-the-working-man&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;functional combinator&lt;/a&gt; (&lt;code&gt;pipe&lt;/code&gt;). All hallmarks of the functional style.&lt;/p&gt;
&lt;p&gt;And this is just scratching the surface. Dig a bit deeper and you&apos;ll find more tools that take advantage of immutability, more techniques to control side-effects, more ways to combine functions in useful ways. And much, much more.&lt;/p&gt;
&lt;p&gt;Applied judiciously, techniques from functional programming will lead you towards code that is easier to reason about locally&lt;sup&gt;&lt;a href=&quot;#user-content-fn-rust&quot; id=&quot;user-content-fnref-rust&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;12&lt;/a&gt;&lt;/sup&gt;, easier to reuse, easier to refactor, easier to test, and generally easier to maintain. All relevant concerns for the plain old programmer, no academic gymnastics involved.&lt;/p&gt;
&lt;p&gt;Yeah, but.&lt;/p&gt;
&lt;h2 id=&quot;the-monad-elephant-in-the-room&quot;&gt;The Monad Elephant in the Room&lt;/h2&gt;
&lt;p&gt;It&apos;s much more common to hear about functional programming alongside intimidating academese like monads, functors, category theory, and oh my... It might seem that the typical functional programmer is stuck in an ivory tower onanistically trying to force mathematical abstractions down the CI pipeline.&lt;/p&gt;
&lt;p&gt;I won&apos;t argue that this isn&apos;t a real phenomenon. And I can&apos;t say I&apos;m completely innocent of doing just &lt;a href=&quot;https://www.youtube.com/watch?v=UwYLaGzhDb4&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;that&lt;/a&gt; myself. But it&apos;s not entirely our fault.&lt;/p&gt;
&lt;p&gt;Once you start with the basics of untangling the spaghetti of rampant side-effects, the clarity you obtain reveals the deeper repetitive structures of your code&lt;sup&gt;&lt;a href=&quot;#user-content-fn-eagleeyed&quot; id=&quot;user-content-fnref-eagleeyed&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;13&lt;/a&gt;&lt;/sup&gt;. And it becomes yet another thing you want to DRY. And you know who has a hundreds of years old track record of systematically discovering and categorizing repetitive structures? Mathematicians, that&apos;s who.&lt;/p&gt;
&lt;p&gt;So it&apos;s not that surprising that some of the structures that appear in programming overlap with structures that mathematicians already discovered. Once you see it, it&apos;s hard to unsee. And that&apos;s how you rediscover &lt;a href=&quot;https://en.wikipedia.org/wiki/Monad_(functional_programming)&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;monads&lt;/a&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-fromphilosophy&quot; id=&quot;user-content-fnref-fromphilosophy&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;14&lt;/a&gt;&lt;/sup&gt;, and then the &lt;a href=&quot;https://wiki.haskell.org/index.php?title=Typeclassopedia#:~:text=The%20type%20classes&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;rest&lt;/a&gt; of the **** owl&lt;sup&gt;&lt;a href=&quot;#user-content-fn-meme&quot; id=&quot;user-content-fnref-meme&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;15&lt;/a&gt;&lt;/sup&gt;. Nobody&apos;s forcing us down that path, but these abstractions are so powerful and reusable that it&apos;s a slippery slope. One that&apos;s both fun and practically useful.&lt;/p&gt;
&lt;p&gt;So no, I&apos;m not a functional programmer, I just happen to wield a very powerful toolbox to make code better. And you?&lt;/p&gt;
&lt;div class=&quot;mx-auto w-full max-w-md&quot;&gt;
&lt;img alt=&quot;cheshire-cat&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;841&quot; height=&quot;959&quot; src=&quot;https://blog.daniel-beskin.com/_astro/cheshire-cat.C9DnXIle_CWF5C.webp&quot;&gt;
&lt;/div&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-pun&quot;&gt;
&lt;p&gt;Pun intended. &lt;a href=&quot;#user-content-fnref-pun&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-naming&quot;&gt;
&lt;p&gt;Although I could argue that naming things is better than not. There&apos;s a reason why classic OOP design patterns are so popular. A name is a powerful thing. &lt;a href=&quot;#user-content-fnref-naming&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-spooky&quot;&gt;
&lt;p&gt;Originally from &lt;a href=&quot;https://en.wikipedia.org/wiki/Action_at_a_distance#%22Spooky_action_at_a_distance%22&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;discussions&lt;/a&gt; about physics. &lt;a href=&quot;#user-content-fnref-spooky&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-ugly&quot;&gt;
&lt;p&gt;But will likely produce exceedingly ugly code that you will most definitely ignore, &quot;it just tests, who cares...&quot;. &lt;a href=&quot;#user-content-fnref-ugly&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-quantum&quot;&gt;
&lt;p&gt;Continuing the quantum physics-inspired &lt;a href=&quot;https://en.wikipedia.org/wiki/Quantum_entanglement&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;theme&lt;/a&gt;... &lt;a href=&quot;#user-content-fnref-quantum&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-blog&quot;&gt;
&lt;p&gt;Despite my whole blog possibly being an indication of the opposite. &lt;a href=&quot;#user-content-fnref-blog&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 6&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-golf&quot;&gt;
&lt;p&gt;Not in the &quot;code golf&quot; sense, reducing the absolute number of characters is not what I&apos;m aiming at. &lt;a href=&quot;#user-content-fnref-golf&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 7&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-rollbacks&quot;&gt;
&lt;p&gt;In real life you&apos;ll probably need to handle rollbacks and the like to make this thing actually transactional, but that goes way beyond the scope of this post. &lt;a href=&quot;#user-content-fnref-rollbacks&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 8&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-templatemethod&quot;&gt;
&lt;p&gt;Compare with the classic &lt;a href=&quot;https://en.wikipedia.org/wiki/Template_method_pattern&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;Template Method&lt;/a&gt; design pattern. &lt;a href=&quot;#user-content-fnref-templatemethod&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 9&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-decorator&quot;&gt;
&lt;p&gt;Compare with the classic &lt;a href=&quot;https://en.wikipedia.org/wiki/Decorator_pattern&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;Decorator&lt;/a&gt; design pattern. &lt;a href=&quot;#user-content-fnref-decorator&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 10&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-samevar&quot;&gt;
&lt;p&gt;Yeah, yeah, this is JavaScript, you can just reuse the same variable multiple times. But given what I wrote before I err on the side of using &lt;code&gt;const&lt;/code&gt; where possible. And even if not, this is just an example, so chill... &lt;a href=&quot;#user-content-fnref-samevar&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 11&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-rust&quot;&gt;
&lt;p&gt;Although functional programming is not the only way to attain local reasoning. See for &lt;a href=&quot;https://graydon2.dreamwidth.org/312681.html&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;example&lt;/a&gt; how the imperative Rust can take you in the direction of local reasoning. &lt;a href=&quot;#user-content-fnref-rust&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 12&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-eagleeyed&quot;&gt;
&lt;p&gt;The eagle-eyed viewers amongst you might&apos;ve noticed that we got very close to rediscovering the &lt;a href=&quot;https://www.scala-lang.org/api/current/scala/util/Try.html&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;Try&lt;/code&gt;&lt;/a&gt;/&lt;a href=&quot;https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-result/&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;Result&lt;/code&gt;&lt;/a&gt; type from functional programming, along with the monadic operations it supports. &lt;a href=&quot;#user-content-fnref-eagleeyed&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 13&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-fromphilosophy&quot;&gt;
&lt;p&gt;Although &quot;monad&quot; is not really an FP term, or even a mathematical term. It seems to have come to us from &lt;a href=&quot;https://en.wikipedia.org/wiki/Monad_(philosophy)&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;philosophy&lt;/a&gt;. And it appears that Microsoft had them all &lt;a href=&quot;https://en.wikipedia.org/wiki/PowerShell#Monad&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;along&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-fromphilosophy&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 14&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-meme&quot;&gt;
&lt;p&gt;In case this is somehow unfamiliar, I&apos;m referring to this wonderful &lt;a href=&quot;https://www.reddit.com/r/pics/comments/d3zhx/how_to_draw_an_owl/&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;meme&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-meme&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 15&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><author>Daniel Beskin</author></item><item><title>The Compiler Is Your Best Friend, Stop Lying to It</title><link>https://blog.daniel-beskin.com/2025-12-22-the-compiler-is-your-best-friend-stop-lying-to-it</link><guid isPermaLink="true">https://blog.daniel-beskin.com/2025-12-22-the-compiler-is-your-best-friend-stop-lying-to-it</guid><description>The compiler is a powerful tool, yet many developers have a painful relationship with it. Can we do better?</description><pubDate>Mon, 22 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://blog.daniel-beskin.com/social-cards/2025-12-22-the-compiler-is-your-best-friend-stop-lying-to-it.png&quot; alt=&quot;Cover image for The Compiler Is Your Best Friend, Stop Lying to It&quot; style=&quot;width: 100%; height: auto; margin-bottom: 2rem;&quot;&gt;&lt;p&gt;Note: this is a &quot;script&quot; for a podcast that I recently recorded, as such it&apos;s more conversational and less technical than the usual content of this blog. Not a single code block in sight...&lt;/p&gt;
&lt;h2 id=&quot;prologue&quot;&gt;Prologue&lt;/h2&gt;
&lt;p&gt;Imagine you wake up one night to find out that production is crashing. It takes you a while to unearth the root cause. It turns out to be a null pointer exception deep inside a service. It crashed the whole system. How did it get there? Where did it come from?&lt;/p&gt;
&lt;p&gt;Now imagine a different story. One day a developer had to struggle for a whole of 20 minutes fixing some informative compilation errors. And that&apos;s it, that&apos;s the whole story. Nobody had to wake up at night, production never crashed.&lt;/p&gt;
&lt;p&gt;One of these stories is a story of lies and deceit, the other a story of dialogue and cooperation. Today we will learn how to stop lying to the compiler, and how to get a good night&apos;s sleep.&lt;/p&gt;
&lt;h2 id=&quot;part-i-the-compiler&quot;&gt;Part I: The Compiler&lt;/h2&gt;
&lt;p&gt;(If you&apos;re familiar at high-level with how compilers work, feel free to skip to the next &lt;a href=&quot;#part-ii-lying-to-the-compiler&quot;&gt;part&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;But first things first. What is a compiler?&lt;/p&gt;
&lt;p&gt;In the broadest sense, a compiler is a function, that takes an input in one format and produces an output in another format. This definition is so broad as to be useless... Although, if you take this definition seriously, it has some interesting implications on how we approach solving problems in everyday code. But I digress.&lt;/p&gt;
&lt;p&gt;Let&apos;s try again, more concretely this time. A typical compiler takes source code in some programming language and produces an output in some other language. For example, a C compiler takes files written in C, and produces assembly code.&lt;/p&gt;
&lt;p&gt;A typical compiler pipeline might have the following steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Parsing the source code into an intermediate representation, an abstract syntax tree (AST)&lt;/li&gt;
&lt;li&gt;Typechecking the code, make sure that all the types line up&lt;/li&gt;
&lt;li&gt;Optimization, going over the AST and finding opportunities to make the code more efficient&lt;/li&gt;
&lt;li&gt;Code generation, converting the AST into the output format, like machine code.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As we will soon see, the typechecking step is probably the most relevant for most developers.&lt;/p&gt;
&lt;p&gt;Like most things, nothing is that simple. Different languages have differently flavored compilers, doing a wide range of things. This is not a thorough review on all things compilers, so instead, let&apos;s take a sample of languages and see what kind of compilers they have.&lt;/p&gt;
&lt;h3 id=&quot;rust&quot;&gt;Rust&lt;/h3&gt;
&lt;p&gt;Rust is similar to C, in that it compiles directly to machine code. To distinguish this &quot;classic&quot; compiler from the rest, we call this style an &quot;ahead of time&quot; compiler. This means that everything that the compiler does starts and ends before runtime. Once we created the binary output, the work of the compiler is done.&lt;/p&gt;
&lt;p&gt;But, this doesn&apos;t mean that the job of the compiler is easy. Rust is quite sophisticated, apart from parsing the source code the compiler needs to do things like macro expansion, type checking, borrow checking, and lots, and lots of optimizations.&lt;/p&gt;
&lt;p&gt;Optimization, and generally performance is a common benefit of using compiled languages. Compiled languages tend to be faster than languages that don&apos;t use a classic compiler, but are instead dynamically interpreted on the fly (like Python). And Rust is famous for its optimizations in the style of &quot;zero cost abstractions&quot;.&lt;/p&gt;
&lt;p&gt;Zero cost abstraction means that despite Rust being a low-level language, in many cases one can write pretty high-level code and still get the performance of handwritten low-level code. This is possible because the compiler analyzes the code, and manages to translate a high-level construct like &lt;code&gt;map&lt;/code&gt; on an iterator into a low-level, fast loop. This includes optimizations like inlining and loop fusion.&lt;/p&gt;
&lt;p&gt;As a developer writing in Rust though, the thing that you would notice the most during development is all the memory safety checks the compiler does for you at every step.&lt;/p&gt;
&lt;p&gt;Rust makes it possible to safely manage memory without using a garbage collector, probably one of the biggest pain points of using low-level languages like C and C++. It boils down to the fact that many of the common memory issues that we can experience, things like dangling pointers, double freeing memory, and data races, all stem from the same thing: uncontrolled sharing of mutable state. For example, if two different variables share a pointer to the same data, if one of them frees the pointer, the other will end up with an invalid pointer without knowing it. Likely breaking something upon pointer dereference.&lt;/p&gt;
&lt;p&gt;So Rust forbids us from freely sharing pointers. Each pointer has exactly one owner at every moment of time. Once the owner goes out of scope, memory is freed automatically. Since there are no other owners, this is safe and cannot lead to dangling pointers and the like.&lt;/p&gt;
&lt;p&gt;What makes Rust special is that all of this happens at compile-time. The compiler tracks pointer ownership, using something called a borrow checker, forbidding us from doing anything that violates the single ownership of pointers. Memory unsafe code will simply fail to compile.&lt;/p&gt;
&lt;p&gt;Rust&apos;s powerful type system is one very compelling (and painful) reason to use it. A broken elevator and walking 21 floors up the stairs can really motivate you to invent such a safe language.&lt;/p&gt;
&lt;p&gt;Then we have Java.&lt;/p&gt;
&lt;h3 id=&quot;java&quot;&gt;Java&lt;/h3&gt;
&lt;p&gt;People often think of Java as a static, compiled language. But actually, the Java compiler doesn&apos;t produce machine code. Instead, it produces bytecode. An intermediate (stack-based) language that is then &lt;em&gt;dynamically&lt;/em&gt; interpreted by the Java Virtual Machine (JVM) when the bytecode is executed.&lt;/p&gt;
&lt;p&gt;The reason for going through all this trouble is portability. By using intermediate bytecode, Java can be compiled once on any platform, but then executed on any other platform that has an implementation of the JVM. That&apos;s cool and all, but didn&apos;t we just say that interpreted languages are slow?&lt;/p&gt;
&lt;p&gt;True, in the early days of Java, people were worried that running such a &quot;dynamic&quot; language will be too slow to be useful, and surely will never be able to compete with something like C++. And that&apos;s why we have &quot;just in time&quot; compilation. Or JIT for short.&lt;/p&gt;
&lt;p&gt;The Java compiler does a lot less than Rust&apos;s. The translation from Java to bytecode is comparatively simple. The compiler doesn&apos;t make optimizations as far reaching as the Rust compiler. And if we left things that way Java would indeed be quite slow.&lt;/p&gt;
&lt;p&gt;The real fun begins at runtime. The Java runtime has a JIT compiler enabled. The JIT compiler monitors the execution of the bytecode, as it gets slowly interpreted step by step. After a while, the JIT starts recognizing various hotspots in the code, like long loops, or common branching points. It then takes the bytecode for the hotspot and compiles it down to an efficient machine code representation. From that point onwards, that segment of code should run much more quickly.&lt;/p&gt;
&lt;p&gt;Like a sophisticated ahead of time (AOT) compiler, the JIT compiler can apply nontrivial optimizations to the code. But unlike an AOT compiler, the JIT compiler has access to the actual usage patterns at runtime. Meaning that it can be more aggressive in optimizing the real bottlenecks in code. Things like dynamic dispatch in an object-oriented class hierarchy can be converted to static dispatch at runtime.&lt;/p&gt;
&lt;p&gt;If you ever wondered why your JVM app takes time to &quot;warm up&quot; before it&apos;s actually fast, the JIT compiler is your answer. It takes time for the JIT compiler to recognize and compile the actual hotspots in code.&lt;/p&gt;
&lt;p&gt;These days, Java has an AOT compiler as well, called Graal. As a result we can use the very same Java code and produce two completely different artifacts, either in bytecode or machine code (with some limitations).&lt;/p&gt;
&lt;p&gt;More generally compilers can have more than one compilation target, or &quot;backends&quot;. For example, both Scala and Kotlin can be compiled to either JVM bytecode, JavaScript, and native machine code. To support such use cases, and to avoid repetition between different backends, the compiler will have some intermediate representation of the language that is being compiled, the AST. This way, most of the compiler&apos;s work like parsing, typechecking, and various optimizations can produce one AST, but then that one AST can be converted into different outputs.&lt;/p&gt;
&lt;p&gt;Despite all that complex work being done by the various compilers, most of the interaction that Java programmers have with the compiler is with the type checker. Java&apos;s type-system is not nearly as strict as Rust&apos;s, often being more annoying than helpful. So it&apos;s not uncommon for us the developers to &quot;know better&quot; than the compiler and use a cast every now and then. These are the first signs of trouble...&lt;/p&gt;
&lt;p&gt;Seeing that a compiler can be quite the complex beast, you would be quite correct in asking:&lt;/p&gt;
&lt;h3 id=&quot;aside-who-compiles-the-compilers&quot;&gt;Aside: Who Compiles the Compilers?&lt;/h3&gt;
&lt;p&gt;Or more specifically, in what language is the compiler written? It would seem natural for the developers of a new language X to write the compiler in X. Surely it&apos;s their favorite language. At the very least, it&apos;s a good idea for them to dogfood their own creation.&lt;/p&gt;
&lt;p&gt;Among people who write compilers, writing the compiler in the very same language it&apos;s supposed to compile is considered an important milestone. It shows that the language is mature enough to be able to implement such a complex piece of code. When a language does it, it is said to be &quot;self-hosting&quot;.&lt;/p&gt;
&lt;p&gt;Here we have a paradox. If I have a compiler written in X, to be able to use it I first need to compile it, and for that I need a compiler for language X. But I don&apos;t have one compiled, I need to compile it first... And so we find ourselves with a chicken and egg problem.&lt;/p&gt;
&lt;p&gt;The way to get out of this conundrum is by a procedure called &quot;bootstrapping&quot;. Here things are going to get a bit meta.&lt;/p&gt;
&lt;p&gt;It goes like this. First write a compiler in language X, the &quot;bootstrapp&lt;strong&gt;ed&lt;/strong&gt; compiler&quot;. Ideally you do that using as few features of X as possible. Then choose another existing language Y (can be assembly or some high-level language), and write a compiler for X in Y, the &quot;bootstrapp&lt;strong&gt;ing&lt;/strong&gt; compiler&quot;. It doesn&apos;t have to handle all of the features of X, only the ones that were used in the code for the bootstrapp&lt;strong&gt;ed&lt;/strong&gt; compiler. Once you run the bootstrapp&lt;strong&gt;ing&lt;/strong&gt; compiler on the code for the bootstrapp&lt;strong&gt;ed&lt;/strong&gt; compiler, congratulations, you now have a brand new self-hosting compiler for X. From this point onwards, you can improve the compiler and add features to it all without leaving the comfort of your favorite language X.&lt;/p&gt;
&lt;p&gt;We can follow this process with the Rust compiler. Originally, it was written in the OCaml language. But the current Rust compiler was rewritten in Rust, which makes it a proper self-hosting compiler.&lt;/p&gt;
&lt;p&gt;This is all good and well, but most of the time us simple developers don&apos;t really care about the language the compiler uses, we just need it to compile code. The reason I&apos;m telling you all this is that it gives us an interesting insight into the development of programming languages.&lt;/p&gt;
&lt;p&gt;The people who write compilers are usually the same people that design the language itself. And most of what they do all day long is writing compiler code. This might skew their incentives. They really enjoy using languages that are a good fit for writing compilers. Even more so, since in the early stages of a new language the compiler is probably the biggest piece of code written in it. That&apos;s the chance for the new language to really shine.&lt;/p&gt;
&lt;p&gt;We shouldn&apos;t be surprised then, to find that many new languages tend to accumulate features that are very convenient for compiler writers. Things like pattern matching, and implicit parameters.&lt;/p&gt;
&lt;p&gt;But seeing how compilers are not very representative of the code most of us write daily, this might not be the best way to spend the complexity budget when designing a new language. Lucky for us, some of those features are useful for plain-old, boring business code as well. Especially code written in the functional style.&lt;/p&gt;
&lt;p&gt;Let&apos;s wrap up this exploration of compilers with one more language.&lt;/p&gt;
&lt;h3 id=&quot;typescript&quot;&gt;TypeScript&lt;/h3&gt;
&lt;p&gt;The TypeScript compiler is special in that the target is not a low-level format like assembly and bytecode, but instead another high-level language, JavaScript. Although definitions are a bit fluid, but in this case we say that the TypeScript compiler is actually a &quot;transpiler&quot;. Even more curiously, since JavaScript is a strict subset of TypeScript, we can say that TypeScript is compiling into itself.&lt;/p&gt;
&lt;p&gt;There are two main reasons to want to do that: nicer syntax for various features of JavaScript (like classes and enums), and the type-system that TypeScript adds on top of JavaScript.&lt;/p&gt;
&lt;p&gt;Microsoft, who developed TypeScript, was motivated by the pain they experienced when scaling large JavaScript codebases. Adding a robust type-system to a language makes it much easier to tame large codebases over time (things like refactoring become safer). We can see the same trend with Dropbox investing in Mypy, an optional type checker for Python, to help scaling their Python codebase.&lt;/p&gt;
&lt;p&gt;Adding a type-system to an existing programming language is very different from designing a language with types from the ground up. With an existing language you must be able to add types to as much existing code as possible. And codebases in dynamic language sure do tend to be... Dynamic.&lt;/p&gt;
&lt;p&gt;To mitigate this, TypeScript uses gradual typing. With gradual typing you don&apos;t have to add types to the whole codebase all at once, like you would in, say, Java. Instead, you can do it gradually, and the compiler verifies that whatever is annotated is consistent with itself, and ignores the rest. This smoothes migration from an untyped code and mindset to a typed one.&lt;/p&gt;
&lt;p&gt;There is also the question of which type system is most appropriate for an ex-dynamic language. Did you know that type-systems come in different flavors? Most mainstream languages use &quot;nominal&quot; type-systems. This means that each type gets a unique name, and types that have different names are treated as distinct types by the compiler. In contrast, TypeScript uses a &quot;structural&quot; type-system. Two types are considered the same if they have the same structure, e.g., the same fields and methods. The name of the type doesn&apos;t matter.&lt;/p&gt;
&lt;p&gt;Structural typing seems to be better suited when gradually typing a dynamic language. &quot;If it walks like a duck and quacks like a duck, then it must be a duck&quot; is a common viewpoint in dynamic languages, and structural typing is the way to capture this mindset with types.&lt;/p&gt;
&lt;p&gt;In a curious turn of events, TypeScript and Mypy went the other way around with bootstrapping and self-hosting. The current TypeScript compiler is self-hosting, but a rewrite in Go is in the works. In Mypy, the compiler was rewritten from plain Python into a special almost-Python dialect that compiles directly to C. The motivation for both rewrites is performance. Turns out that it&apos;s difficult to get a sufficiently fast compiler that can quickly process large codebases in dynamic runtimes like JavaScript and Python.&lt;/p&gt;
&lt;p&gt;Like before, we can&apos;t help but notice that the type-system is the main interface between the developer and the compiler. In the case of TypeScript I would say that it&apos;s also the main reason to even want to use the language.&lt;/p&gt;
&lt;p&gt;And yet, so many developers hate the compiler. It seems that all it does is yell at us about trivialities like &quot;string is not int&quot; and such. Is it worth bothering with a compiled language just for that?&lt;/p&gt;
&lt;h2 id=&quot;part-ii-lying-to-the-compiler&quot;&gt;Part II: Lying to the Compiler&lt;/h2&gt;
&lt;p&gt;The compiler is always angry. It&apos;s always yelling at us for no good reason. It&apos;s only happy when we surrender to it and do what it tells us to do. Why do we agree to such an abusive relationship?&lt;/p&gt;
&lt;p&gt;And for someone who yells that much, it&apos;s not even that smart. Remember all the times when the compiler yelled at you something about a type mismatch, but turns out that you were right and the compiler was wrong? In the end you had to use a cast (or mark something as &lt;code&gt;any&lt;/code&gt;) just to shut the compiler up.&lt;/p&gt;
&lt;p&gt;We can solve these issues by quitting the relationship, and jumping ship to a dynamic language. I suspect that compiler abuse is why in the past Python grew in popularity compared to Java (that, and Java&apos;s verbosity). But we just saw that even in historically dynamic languages, like JavaScript and Python, there&apos;s a trend towards a more static, more compiler-centric way of writing code. These days Python has not just one, but &lt;em&gt;multiple&lt;/em&gt; competing tools for typechecking (mypy, pyright, pyrefly, ty). Why do people lean that way?&lt;/p&gt;
&lt;p&gt;Maybe it&apos;s performance. We talked before about how compiled languages tend to be faster than dynamic languages. But that doesn&apos;t seem quite right. In many contexts, where the language&apos;s performance is not the bottleneck, people will still use types. And TypeScript, for example, doesn&apos;t give us a runtime performance benefit, all the type information is discarded after compilation. Are people really just that masochistic?&lt;/p&gt;
&lt;p&gt;Maybe Rust is the answer. Rust&apos;s compiler brings significant improvements in code safety where memory management is involved. True, its compiler is even more difficult to satisfy than average, but it solves a very tangible problem that would be very difficult to safely solve otherwise. Still, most of us don&apos;t write low-level code, and we mostly use garbage-collected, memory-safe languages. Not many mainstream languages have a type-system that is as powerful as Rust&apos;s, so we don&apos;t usually get that much extra safety.&lt;/p&gt;
&lt;p&gt;We can imagine that in very large codebases, like the ones managed by Microsoft and Dropbox, there&apos;s some magical advantage to having types checked by the compiler (we mentioned safer refactoring before), despite the obvious pains. But most of us don&apos;t maintain code that large, why bother with types then?&lt;/p&gt;
&lt;p&gt;It seems that all the compiler can do for us is act as simple, and not very reliable, guard rails. Sure it would sometimes notice that this or that int is actually a string. But if that&apos;s all it can do for us, then we might as well write a couple of unit tests, and remove this nuisance from our lives.&lt;/p&gt;
&lt;h3 id=&quot;the-lies&quot;&gt;The Lies&lt;/h3&gt;
&lt;p&gt;I think that this is a false premise. The compiler is actually much more useful and powerful than it might appear. So useful in fact, that relying on it is beneficial in any codebase, large and small. The problem is that we are used to lie to the compiler all the time, in return all it can do for us is yell about strings not being ints and do little else.&lt;/p&gt;
&lt;p&gt;What do I mean by lying to the compiler?&lt;/p&gt;
&lt;p&gt;The compiler, or rather the type-system, is only aware of things that are known at compile-time. A &quot;lie&quot; would be to write code that communicates one thing at compile-time while doing something else at runtime. On the other hand, the more facts that we can truthfully state to the compiler at compile-time, the more useful the compiler can be. But first, let&apos;s see some concrete examples of lies.&lt;/p&gt;
&lt;h3 id=&quot;null&quot;&gt;Null&lt;/h3&gt;
&lt;p&gt;When we say that some variable &lt;code&gt;x&lt;/code&gt; is a string. What do we mean by that? It means that the runtime values that &lt;code&gt;x&lt;/code&gt; can take are things &quot;a&quot;, &quot;ab&quot;, &quot;abc&quot;, and many other reasonable strings. This also means that we can apply various methods to &lt;code&gt;x&lt;/code&gt; like &lt;code&gt;substring&lt;/code&gt; or &lt;code&gt;endsWith&lt;/code&gt;. Unfortunately, in most modern languages this also means that at runtime it can be &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Unlike the other non-null values, you cannot call &lt;code&gt;substring&lt;/code&gt; or any other method on &lt;code&gt;null&lt;/code&gt;. Calling a method on &lt;code&gt;null&lt;/code&gt; will trigger an &lt;code&gt;NullPointerException&lt;/code&gt;, possibly far away in code and time from where that &lt;code&gt;null&lt;/code&gt; was created. We could adopt a policy of &quot;defensive programming&quot; checking for &lt;code&gt;null&lt;/code&gt; every step of the way. But in practice nobody does that. Instead, in many places in our code we implicitly &lt;em&gt;assume&lt;/em&gt; that values are non-null unless stated otherwise (in a comment that nobody actually reads).&lt;/p&gt;
&lt;p&gt;This is one of the most common lies we tell the compiler. We state that something is a valid string, but at runtime it can just as well be &lt;code&gt;null&lt;/code&gt;. In most mainstream languages the compiler cannot distinguish between nullable and non-nullable values. And so it cannot help us out when our nullability assumptions turn out to be wrong. That&apos;s when things crash.&lt;/p&gt;
&lt;p&gt;How many times did you find yourself setting something to &lt;code&gt;null&lt;/code&gt; because you couldn&apos;t come up with something better, only to discover that some other place starts crashing?&lt;/p&gt;
&lt;h3 id=&quot;exceptions&quot;&gt;Exceptions&lt;/h3&gt;
&lt;p&gt;Speaking of crashing, another source of lies we tell the compiler are exceptions. More specifically, unchecked exceptions.&lt;/p&gt;
&lt;p&gt;If we write that some method returns string, what do we mean by this? Ideally, we mean that every time we call the method a string value will be returned. But in reality, there&apos;s another, sneaky way of returning from a method: we can throw an exception.&lt;/p&gt;
&lt;p&gt;Similar to &lt;code&gt;null&lt;/code&gt;, unchecked exceptions might be discovered away from the origin (although not as bad as &lt;code&gt;null&lt;/code&gt;, they have to walk up the stack, and are not stored as data). And similar to &lt;code&gt;null&lt;/code&gt; the compiler won&apos;t track them for us. If we don&apos;t go into a full defensive mode, we&apos;re assuming that the code is exception-free, and assumptions tend to break over time. But we lied to the compiler, so it won&apos;t be there to help when they finally break.&lt;/p&gt;
&lt;p&gt;How many times did you leave a comment on some branch of code stating &quot;this CANNOT happen&quot; and thrown an exception? Did you ever find yourself surprised when eventually it &lt;em&gt;did&lt;/em&gt; happen? I know I did, since then I at least add some logs even if I think I&apos;m sure that it really cannot happen.&lt;/p&gt;
&lt;h3 id=&quot;casts&quot;&gt;Casts&lt;/h3&gt;
&lt;p&gt;So far things happened by accident, due to implicit assumptions being broken. But sometimes we are smarter than the compiler. Sometimes we know better and we can force the compiler to obey. We can use type casting.&lt;/p&gt;
&lt;p&gt;Suppose that there&apos;s some interface, let&apos;s call it &lt;code&gt;Animal&lt;/code&gt;. And it has two different implementations: &lt;code&gt;Cat&lt;/code&gt; and &lt;code&gt;Dog&lt;/code&gt;. Sorry for the cliche example, it&apos;s difficult to be original without written code.&lt;/p&gt;
&lt;p&gt;Now we are working on some code that got an &lt;code&gt;Animal&lt;/code&gt; argument, call it &lt;code&gt;x&lt;/code&gt;. In this particular flow we know for certain that &lt;code&gt;x&lt;/code&gt; must be a &lt;code&gt;Dog&lt;/code&gt;. We can follow the code ourselves and show that &lt;code&gt;x&lt;/code&gt; is unquestionably a &lt;code&gt;Dog&lt;/code&gt;. So we want to use &lt;code&gt;Dog&lt;/code&gt;-specific methods. But the silly compiler won&apos;t let us. It only knows about &lt;code&gt;Animal&lt;/code&gt; methods.&lt;/p&gt;
&lt;p&gt;The compiler won&apos;t budge, and we won&apos;t budge. So we take out our hammer, and use a cast. Now &lt;code&gt;x&lt;/code&gt; is a &lt;code&gt;Dog&lt;/code&gt;, and the compiler doesn&apos;t argue anymore. All is well, until some new feature request that requires supporting &lt;code&gt;Cat&lt;/code&gt;s in our flow. When we make the change, we forget about that cast we did a while ago to make things work, and the code explodes.&lt;/p&gt;
&lt;p&gt;The compiler didn&apos;t help us because we silenced it by lying to it. There are two lies going on here, why wasn&apos;t the initial type precise enough? If we can see that it must be &lt;code&gt;Dog&lt;/code&gt;, why weren&apos;t we able to show that to the compiler? Then there&apos;s the cast itself, which blatantly tells one thing to the compiler (&lt;code&gt;x&lt;/code&gt; is a &lt;code&gt;Dog&lt;/code&gt;), when the reality might be something else (&lt;code&gt;x&lt;/code&gt; is a &lt;code&gt;Cat&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;How often do you think you know better than the compiler?&lt;/p&gt;
&lt;h3 id=&quot;side-effects&quot;&gt;Side-Effects&lt;/h3&gt;
&lt;p&gt;Up until now the examples are quite obvious lies. We all feel a bit dirty when doing a cast, or throwing a runtime exception with the comment &quot;this should NEVER happen&quot;. But here&apos;s a more subtle example.&lt;/p&gt;
&lt;p&gt;Let&apos;s say there&apos;s a function called &lt;code&gt;foo&lt;/code&gt;. When we see that &lt;code&gt;foo&lt;/code&gt; takes no arguments and returns &lt;code&gt;void&lt;/code&gt;, what can we say about what &lt;code&gt;foo&lt;/code&gt; does without looking at its implementation? Nothing really. It&apos;s clearly doing something, and that something is not reflected in its inputs or outputs. We can only conclude that the function is doing some kind of side-effect, like changing a variable or writing to a file.&lt;/p&gt;
&lt;p&gt;Just like us, the compiler doesn&apos;t know anything useful about &lt;code&gt;foo&lt;/code&gt; either. So we have a function that is clearly doing something important, otherwise it won&apos;t be there. But all we tell the compiler is that &quot;nope, move along, nothing interesting to see here&quot;. But that&apos;s a blatant lie, something interesting is going on here, but the type of &lt;code&gt;foo&lt;/code&gt; doesn&apos;t reflect that.&lt;/p&gt;
&lt;p&gt;The compiler gets so confused that even if we comment out a call to &lt;code&gt;foo&lt;/code&gt; it won&apos;t complain, it&apos;s as if it doesn&apos;t even know it exists.&lt;/p&gt;
&lt;p&gt;If you look at a typical codebase, many, many functions return void. Every time we do that we prevent the compiler from knowing something useful about the flow of the code.&lt;/p&gt;
&lt;p&gt;With all these lies all over the place, it&apos;s no wonder that the compiler is not particularly useful. What happens if we stop lying to it?&lt;/p&gt;
&lt;h2 id=&quot;part-iii-no-more-lies&quot;&gt;Part III: No More Lies&lt;/h2&gt;
&lt;p&gt;If we want develop a serious, constructive relationship with the compiler, we must first stop lying to it. Only then can we open channels for serious dialogue.&lt;/p&gt;
&lt;p&gt;We are so used to using &lt;code&gt;null&lt;/code&gt;, exceptions, casting, and side-effects, that at first sight it might seem impossible to give those up.&lt;/p&gt;
&lt;p&gt;But that&apos;s just an illusion.&lt;/p&gt;
&lt;h3 id=&quot;null-1&quot;&gt;Null&lt;/h3&gt;
&lt;p&gt;Some languages like Rust and Haskell have no &lt;code&gt;null&lt;/code&gt; at all. Can you imagine what it&apos;s like to work that way?&lt;/p&gt;
&lt;p&gt;Liberating, that&apos;s how it feels. You no longer have to keep worrying about an accidental &lt;code&gt;null&lt;/code&gt; slipping through, because there aren&apos;t any. But, you might ask, what if something &lt;em&gt;really is&lt;/em&gt; missing, how do I represent that without &lt;code&gt;null&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;In a world without &lt;code&gt;null&lt;/code&gt; there are a number of ways to represent a thing that might be missing. The most common one is to use something like an &lt;code&gt;Option&lt;/code&gt; type, that can be in one of two states, present with a value, or missing. When you want say that a &lt;code&gt;String&lt;/code&gt; might not exist you use the type &quot;option-of-&lt;code&gt;String&lt;/code&gt;&quot;. And that makes all the difference to the compiler.&lt;/p&gt;
&lt;p&gt;Now the compiler knows what&apos;s going on, since option-of-&lt;code&gt;String&lt;/code&gt; is a distinct type from just &lt;code&gt;String&lt;/code&gt;, it&apos;s no longer possible to call &lt;code&gt;String&lt;/code&gt; methods, like &lt;code&gt;substring&lt;/code&gt;, on it. Every time you deal with an option-of-&lt;code&gt;String&lt;/code&gt; value the compiler will helpfully remind you to deal with the possibility that the value is missing.&lt;/p&gt;
&lt;p&gt;On the flip side of that, now every time you have a &lt;code&gt;String&lt;/code&gt; value you no longer have to worry that it might be missing. You can just use it without any defensive programming involved. And if you mistakenly break the rules and try to assign an option value to a non-option value the compiler will point that out and force you to think whether that&apos;s what you really want and what the consequences across the codebase will be.&lt;/p&gt;
&lt;p&gt;Some languages, like Typescript and Kotlin have builtin support for nullable values. If you mark a &lt;code&gt;String&lt;/code&gt; as nullable, it will become a distinct type from a plain &lt;code&gt;String&lt;/code&gt;, giving us similar benefits to &lt;code&gt;Option&lt;/code&gt;. In Java we can emulate something similar with &lt;code&gt;Nullable&lt;/code&gt; and &lt;code&gt;Nonnull&lt;/code&gt; annotation. But that requires some additional tooling to make it work.&lt;/p&gt;
&lt;p&gt;What all those approaches have in common is that all of them involve the compiler. Instead of hiding our assumptions from the compiler we make them explicit in a way that the type-system can track.&lt;/p&gt;
&lt;p&gt;We can do the same with exceptions.&lt;/p&gt;
&lt;h3 id=&quot;exceptions-1&quot;&gt;Exceptions&lt;/h3&gt;
&lt;p&gt;I&apos;m personally not aware of languages that completely remove exceptions from the language. But working mostly without exceptions, at least as a convention, is definitely possible.&lt;/p&gt;
&lt;p&gt;Inspired by the &lt;code&gt;null&lt;/code&gt; case, we can take a similar approach with exceptions and make them explicit in our types.&lt;/p&gt;
&lt;p&gt;The most common way to do that is to use another container type like &lt;code&gt;Result&lt;/code&gt; or &lt;code&gt;Try&lt;/code&gt;. A return type like &quot;result-of-&lt;code&gt;String&lt;/code&gt;&quot; means that we can have one of two cases, either a success with a &lt;code&gt;String&lt;/code&gt; value, or a failure with some exception.&lt;/p&gt;
&lt;p&gt;This approach makes it explicit to both us and the compiler what&apos;s going in a function. Now the compiler can track possible errors for us, and force us to deal with them as appropriate. And if we change some piece of code from &lt;code&gt;String&lt;/code&gt; to &quot;result-of-&lt;code&gt;String&lt;/code&gt;&quot;, the compiler will helpfully shows where this needs to be handled.&lt;/p&gt;
&lt;p&gt;In Java we can take an alternative approach and use checked exceptions. But industry experience over the years seems to indicate that people don&apos;t enjoy using them. It&apos;s probably a matter of ergonomics. Using a wrapper type instead might be a more ergonomic alternative.&lt;/p&gt;
&lt;p&gt;While avoiding nulls and exceptions is fairly straightforward, casts can be trickier.&lt;/p&gt;
&lt;h3 id=&quot;casts-1&quot;&gt;Casts&lt;/h3&gt;
&lt;p&gt;Unless forced to by some external library, I think that the presence of casts in our code should be considered a design smell. My first (possibly controversial) suggestion would be to configure your tooling to just forbid them by default.&lt;/p&gt;
&lt;p&gt;When feeling the need to use a cast, we should consider whether the current design is doing a good job of reflecting our intentions. Recall that when doing a cast we can ask one of two questions, why was the type not precise enough to begin with? And why do we need to cast here?&lt;/p&gt;
&lt;p&gt;There&apos;s no generic answer to these questions, but finding a good answer to either of them will make the compiler&apos;s life easier.&lt;/p&gt;
&lt;p&gt;Some times the solution would be to add more methods on a parent interface (like &lt;code&gt;Animal&lt;/code&gt;). Some times you might discover that you could&apos;ve started out with the more specific type (&lt;code&gt;Dog&lt;/code&gt;, for example) from the very beginning.&lt;/p&gt;
&lt;p&gt;Another class of solutions is to remodel the problem from using open interfaces to using sealed/union types. Many modern languages support them. Using sealed types retains some of the flexibility of using open interfaces, but still allows the compiler to be in the loop. Instead of blindly assuming that some case is true, we can safely match on the sealed type and the compiler will gladly inform us about all the cases we must handle at this point. Some languages can even magically auto-complete all the relevant cases.&lt;/p&gt;
&lt;p&gt;As code evolves, the compiler will keep track of the cases over time. And if something changed in the sealed type, the compiler will tell us about all the locations where the new case should be handled. Giving us a chance to handle whatever new logic that was added, before it crashes at runtime. Something that it was powerless to do when you used a plain cast.&lt;/p&gt;
&lt;p&gt;Now for the thorniest issue of them all.&lt;/p&gt;
&lt;h3 id=&quot;side-effects-1&quot;&gt;Side-Effects&lt;/h3&gt;
&lt;p&gt;Software exists to make side-effects, otherwise we would just have overheating boxes without any useful output. So side-effects cannot be completely eliminated from code. But they can be better managed.&lt;/p&gt;
&lt;p&gt;This is even a deeper design issue than casting, and there isn&apos;t a one size fits all solution. My suggestion would be to separate as much pure computation from side-effects as possible.&lt;/p&gt;
&lt;p&gt;In practical terms that would mean that every time you see a void return, or something that performs some side-effects mixed with other useful computation, you try to extract a pure function, with well-defined inputs and outputs, that only does computation, and another function that does the side-effects (similar to the Command Query Separation principle).&lt;/p&gt;
&lt;p&gt;A common pattern would be to separate pure business logic from data fetching/writing. So instead of intertwining database calls with computation, you split into three separate phases: fetch, compute, store (a tiny ETL). First fetch all the data you need from a database, then you pass it to a (pure) function that produces some output, then pass the output of the pure function to a store procedure.&lt;/p&gt;
&lt;p&gt;Now instead of having one big flow that takes no inputs and produces no outputs, you have three building blocks, and one of them has well-defined inputs and outputs. Not only is having this separation makes it easier to focus on your business logic, especially when writing tests. But now that we have inputs and outputs the compiler can follow along as well and help out.&lt;/p&gt;
&lt;p&gt;Taking these ideas seriously leads us toward architectures that are known as &quot;functional core, imperative shell&quot;. Within the functional core the compiler is much more useful, because it has actual types for inputs and outputs that it can follow and enforce. More &quot;interesting stuff&quot; is happening within the knowledge of the compiler.&lt;/p&gt;
&lt;p&gt;Let&apos;s see what happens when we maximize that knowledge.&lt;/p&gt;
&lt;h2 id=&quot;part-iv-the-compiler-as-our-friend&quot;&gt;Part IV: The Compiler as Our Friend&lt;/h2&gt;
&lt;p&gt;Okay, so we stopped lying to the compiler as much as we can, and we are already seeing some gains in safety. No more null pointers, surprising exceptions, or fragile code. We can stop here and pat ourselves on our backs.&lt;/p&gt;
&lt;p&gt;But we can do even better. Not lying is just the first step in a productive relationship. The next step would be to start sharing knowledge. The simplest way to start doing that is to sprinkle our code with more types.&lt;/p&gt;
&lt;h3 id=&quot;typed-wrappers&quot;&gt;Typed wrappers&lt;/h3&gt;
&lt;p&gt;When we use a string or an int in our code, what do they actually mean? Rarely is it just an arbitrary number or some raw text. Usually they represent some concept like a user ID or a file name. The thing is that this special meaning of the int or string are not reflected anywhere outside our head, or maybe a comment.&lt;/p&gt;
&lt;p&gt;As time goes by we have more and more different meanings that an int can take in our system. Maybe it&apos;s an app ID, or a post ID, or a feed ID? Who knows. And so confusion arises, you start mixing up your ints, weird bugs creep up.&lt;/p&gt;
&lt;p&gt;While this is happening, the compiler remains silent, &quot;an int is an int, what can you do...&quot;. We didn&apos;t share our knowledge with the compiler, we have no one to blame.&lt;/p&gt;
&lt;p&gt;The fix is simple: use small, typed wrappers, sometimes called &quot;tiny types&quot;. Each concept you have in your system deserves to have a unique type, something for the compiler to hold on to. So you would have a &lt;code&gt;UserID&lt;/code&gt; type, an &lt;code&gt;AppID&lt;/code&gt; type, and so on. Underneath they might all be simple ints, but now both you and, more importantly, the compiler can distinguish the different concepts in code.&lt;/p&gt;
&lt;p&gt;The difference might seem small, but the consequences are far reaching. Extra safety, the compiler will no longer let us confuse the different ints, is just the beginning.&lt;/p&gt;
&lt;p&gt;Once the compiler gained knowledge of a concept you can use it for various tasks. For example, suppose you want to find out where are all the usages of user IDs in code. If we were still using ints, asking the compiler for all usages of the type int is meaningless, there are too many of those and they mean different things. Searching variable names is easy, but the results might not be complete. But if we have a dedicated type for &lt;code&gt;UserID&lt;/code&gt;, that&apos;s easy, just ask your editor to find usages of the type and you get the precise results you need. The compiler keeps track.&lt;/p&gt;
&lt;p&gt;For the same reasons, refactoring becomes easy. You want to change the representation of the &lt;code&gt;UserID&lt;/code&gt;? Add some extra salt for security? No need to search for all the relevant ints and pray for the best. Modify the type and follow the compilation errors. Done.&lt;/p&gt;
&lt;p&gt;Need some special validation for user IDs? Hide the constructor of &lt;code&gt;UserID&lt;/code&gt;, add an alternative &quot;smart&quot; constructor, follow compilation errors. Done. No need to chase different ints across the system.&lt;/p&gt;
&lt;p&gt;And for us humans, descriptive, domain-specific types make function signatures more readable, both for developers and for the occasional stakeholder. What&apos;s not to like about it?&lt;/p&gt;
&lt;p&gt;Simple typed wrappers are, well, simple. But sometimes we need more sophisticated types to express ourselves to the compiler.&lt;/p&gt;
&lt;h3 id=&quot;union-types&quot;&gt;Union Types&lt;/h3&gt;
&lt;p&gt;Did you ever stumble on a class &lt;code&gt;Foo&lt;/code&gt; that has too many fields with a long comment that explains something along the lines of &quot;if field &lt;code&gt;a&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt; then fields &lt;code&gt;b&lt;/code&gt; and &lt;code&gt;c&lt;/code&gt; must not be &lt;code&gt;null&lt;/code&gt;&quot;. And so on and so forth for a few more different conditions?&lt;/p&gt;
&lt;p&gt;Then when you actually start trusting &lt;code&gt;Foo&lt;/code&gt;&apos;s comment and check that &lt;code&gt;a&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;, you discover, with a production null pointer exception, that &lt;code&gt;b&lt;/code&gt; was actually &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To describe this more formally, &lt;code&gt;Foo&lt;/code&gt; had some invariants, and those invariants were violated. Not every combination of field values makes sense for our application.&lt;/p&gt;
&lt;p&gt;How much nicer it would if the compiler could tell us &quot;hey, look here, this invariant breaks if you use this set of field values&quot;? Unfortunately, most compilers are incapable of expressing invariants that depend in such a way on runtime values.&lt;/p&gt;
&lt;p&gt;The solution to many such problems is to remodel the type to better reflect the invariants that we want to encode, in such a way that the compiler will be able to follow along and only allow for legal combinations.&lt;/p&gt;
&lt;p&gt;One of the most versatile tools for this is to use union, or sealed types (enums in some languages). With a union type we create one variant per case. And each case can only have a legal combination of values. So the case where &lt;code&gt;a&lt;/code&gt; is true will become one variant with two non-nullable fields &lt;code&gt;b&lt;/code&gt; and &lt;code&gt;c&lt;/code&gt;. And that&apos;s it.&lt;/p&gt;
&lt;p&gt;The advantage of taking this approach is that the union type doesn&apos;t even allow us to write down an invalid combination. You can only choose from valid ones, anything else is a compilation error.&lt;/p&gt;
&lt;p&gt;The way you work with union types is that every time you need to access some data you need to match on the value. And then you act according to the case you&apos;re on. If you&apos;re in case &lt;code&gt;a&lt;/code&gt;, you&apos;re &lt;em&gt;guaranteed&lt;/em&gt; to have the &lt;code&gt;b&lt;/code&gt; and &lt;code&gt;c&lt;/code&gt; values. The compiler makes sure they are consistently there no matter how you got to this point of code.&lt;/p&gt;
&lt;p&gt;This can make union types more powerful than tests that check invariants. Unlike a unit test, which can only test for a specific code paths you currently know about, the compiler can track &lt;em&gt;all&lt;/em&gt; code paths, now and forever.&lt;/p&gt;
&lt;p&gt;As mentioned before, in some languages the compiler can auto-complete the different cases relevant for the point in code you&apos;re at. Sometimes it feels like the code is writing itself, you just have to fill in the blanks.&lt;/p&gt;
&lt;p&gt;When the code evolves over time and more cases are added, the compiler will notify you about all the relevant places where you need to update the logic to handle new cases. This is a great way to assess the consequences of changes you&apos;re about to make and help you with decisions at the design phase.&lt;/p&gt;
&lt;p&gt;Using union types this way is a great way to model alternative states in code, and it&apos;s part of a more general approach to programming where you strive to &quot;make illegal states unrepresentable&quot;. That is, encode your invariants as facts that the compiler can enforce and makes it impossible to even &lt;em&gt;write&lt;/em&gt; code that will break those invariants.&lt;/p&gt;
&lt;p&gt;This opens up a whole new way of approaching types.&lt;/p&gt;
&lt;h3 id=&quot;typed-guarantees&quot;&gt;Typed Guarantees&lt;/h3&gt;
&lt;p&gt;We are used to think about types as just a way to distinguish different values. This one&apos;s a string, that one an int. But as we rely more on the compiler to help us we can start thinking about types as guarantees that we want from the type-system. Or facts that we expect to hold, and that the compiler should verify &lt;em&gt;for&lt;/em&gt; us.&lt;/p&gt;
&lt;p&gt;A common scenario is that we want to make sure that the list we got as an argument contains at least one item. A classic example is when we need to compute some average, it&apos;s undefined for empty lists. Or choose a payment method in a payments system, you must have at least one to be able to proceed.&lt;/p&gt;
&lt;p&gt;What we could do is revert to defensive programming, check the list before proceeding, and throw an exception if it&apos;s empty. But being defensive all the time is tedious (and eventually you forget about it), and we are back to lying to the compiler. Instead, we can signal our intentions with a type: &lt;code&gt;NonEmptyList&lt;/code&gt;, a list type that cannot be even constructed without at least one element (some languages have it builtin, but it&apos;s also easy to implement in a library).&lt;/p&gt;
&lt;p&gt;Not only does this communicate our intent to our callers better than any comment can do, but it also forces the compiler to make sure that nobody can call us with an illegal value. Once we encoded a guarantee as a type, we don&apos;t have to worry about it anymore. No defensive programming, no need to write extra tests for it. No more fear that some future refactor will break our invariants.&lt;/p&gt;
&lt;p&gt;Once we start thinking about guarantees the compiler can make for us, the opportunities are endless. You can come up with a type for pretty much anything (within reason):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;PositiveNumber&lt;/code&gt;, how much code that we write would be invalid if it got a negative number?&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AgeOver18&lt;/code&gt;, maybe it&apos;s mission critical to limit certain flows based on user data&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SortedByPriority&lt;/code&gt;, because it might be expensive to re-sort stuff just to be defensive&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And the list goes on, for almost any kind of custom validation logic you might want. Some can be encoded with union types, some might require techniques like smart constructors. You don&apos;t have to get too fancy for this to become a valuable tool.&lt;/p&gt;
&lt;p&gt;Once you start sprinkling these types around, not only is the intent of the code becomes clearer, but the compiler can help verify that everything is consistent, and that any new code knows what it&apos;s required to do.&lt;/p&gt;
&lt;p&gt;With enough types in place you might get the feeling that &quot;if it compiles, ship it&quot;.&lt;/p&gt;
&lt;h3 id=&quot;dialogue&quot;&gt;Dialogue&lt;/h3&gt;
&lt;p&gt;In the prologue we saw what I think is a fairly common story, &lt;code&gt;null&lt;/code&gt; unexpectedly crashing a production system. But we can make this story very concrete. I&apos;m sure that many of us felt the Google Cloud outage in June 2025. So many services around the world were affected that it was very hard to miss.&lt;/p&gt;
&lt;p&gt;From the &lt;a href=&quot;https://status.cloud.google.com/incidents/ow5i3PPK96RduMcb1SsW&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;incident report&lt;/a&gt; we learn that the root cause of it all was a &lt;code&gt;null&lt;/code&gt; value where it was unexpected (along with some other operational compounding issues, like a lack of randomized backoff). And it&apos;s not really surprising. We saw that &lt;code&gt;null&lt;/code&gt;s are lies that we tell the compiler, values that it cannot track for us. In a large enough system, you are bound to lose track of them.&lt;/p&gt;
&lt;p&gt;With the lessons that we learned, can we imagine another outcome? An outcome that doesn&apos;t involve a big chunk of the internet crashing?&lt;/p&gt;
&lt;p&gt;So we already know that using &lt;code&gt;null&lt;/code&gt; is lying to the compiler. If we are motivated enough we will never use a &lt;code&gt;null&lt;/code&gt; in our code, opting instead for something that the compiler can track, an option-like type. The first step of not lying to the compiler might be the most difficult one, but once we take it a whole new world opens up for us.&lt;/p&gt;
&lt;p&gt;The incident report mentions some &quot;unintended blank fields&quot; in &lt;code&gt;Policy&lt;/code&gt; datatype that triggered the whole thing. Although I don&apos;t know the actual details, let&apos;s imagine that those fields used to be non-empty and now we want to open the possibility for making them empty. But we no longer have &lt;code&gt;null&lt;/code&gt; at our disposal, what do we do? We change the type of the fields to be &quot;option-of-X&quot;, and run the compiler. This is where the magic of dialogue starts to shine.&lt;/p&gt;
&lt;p&gt;In a large codebase and a central entity like &lt;code&gt;Policy&lt;/code&gt; you can imagine that you&apos;ll have many compilation errors the moment you set some fields to be optional. That sounds scary, but that&apos;s way better than &lt;code&gt;null&lt;/code&gt; silently propagating all over the code and not knowing the consequences. With the compilation errors you now have a full report of all the code sites you need to reconsider.&lt;/p&gt;
&lt;p&gt;You start going over them, one by one, fixing the errors. But the more errors you fix the more you feel that something&apos;s not right. Adapting the code feels like too much effort, too hacky, &quot;a maze of twisty little ifs, all alike&quot;. That&apos;s the compiler trying to tell you something:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Maybe you need to reconsider the design?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And that&apos;s what you do, with all those changes that you had to make it becomes more obvious that these are not just fields that you made optional, what you really did is uncover a new flow in the system, with new rules. In the old flow the fields really are mandatory, in the new flow they are no longer needed. So you combined the two flows and made the fields optional. But by just setting the fields to be options the new flow becomes implicit in a combination of empty values and flags, something that the compiler can&apos;t track, and is not apparent when reading the code.&lt;/p&gt;
&lt;p&gt;Instead of riding on the existing flow and tweaking it, you can make things explicit and turn &lt;code&gt;Policy&lt;/code&gt; into a union type. If it already was a union, you add another case to describe the new flow, and you compile again. There are still many compilation errors, matches that are incomplete, but now they are telling you something different:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you want to support this new flow, here are all the things you need to consider&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And that&apos;s what you do reviewing the compilation errors and figuring out what the new flow implies at each site. Maybe along the way you discover that the new &lt;code&gt;Policy&lt;/code&gt; type requires different validations that you didn&apos;t think of before. As a result in the new &lt;code&gt;Policy&lt;/code&gt; you start using a &lt;code&gt;NonEmptyList&lt;/code&gt; of &lt;code&gt;AvailabilityZone&lt;/code&gt;s which guarantees that you can&apos;t run the new flow without at least one applicable &lt;code&gt;AvailabilityZone&lt;/code&gt;. (Of course &lt;code&gt;AvailabilityZone&lt;/code&gt; is not some plain string, you have a dedicated type that tells you exactly what it means and gets validates accordingly.)&lt;/p&gt;
&lt;p&gt;You compile again and somewhere along the edges of the system where you process user input the compiler tells you:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Listen, I don&apos;t have any way to prove that you actually have at least on availability zone provided by the user&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And the compiler is right, if you were just using a regular list to represent the availability zones you would&apos;ve missed this. Input processing happens far away from where we actually require the availability zones to not be empty and your unit tests missed this. So you add the appropriate user input validation. And lo and behold, it compiles. Ship it!&lt;/p&gt;
&lt;p&gt;Yes, you struggled with the compiler, and yes it took some time. But guess who&apos;s getting good night&apos;s sleep tonight?&lt;/p&gt;</content:encoded><author>Daniel Beskin</author></item><item><title>What&apos;s the Point of Learning Functional Programming?</title><link>https://blog.daniel-beskin.com/2025-11-13-point-of-learning-fp</link><guid isPermaLink="true">https://blog.daniel-beskin.com/2025-11-13-point-of-learning-fp</guid><description>&quot;What&apos;s the point of learning functional programming?&quot; was a genuine question I got from a student on a functional programming course I was TAing on.</description><pubDate>Thu, 13 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://blog.daniel-beskin.com/social-cards/2025-11-13-point-of-learning-fp.png&quot; alt=&quot;Cover image for What&apos;s the Point of Learning Functional Programming?&quot; style=&quot;width: 100%; height: auto; margin-bottom: 2rem;&quot;&gt;&lt;p&gt;&quot;What&apos;s the point of learning functional programming?&quot; was a genuine question I got from a student on a functional programming course I was TAing on.&lt;/p&gt;
&lt;p&gt;But let&apos;s rewind for a bit of background first. As I found myself standing in front of a frightened looking class, reviewing some Haskell basics, I was starting to feel guilty&lt;sup&gt;&lt;a href=&quot;#user-content-fn-guilt&quot; id=&quot;user-content-fnref-guilt&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; for overwhelming them with all these foreign new concepts. Recursion, currying, function composition, algebraic data types, and the list goes on and on. So it felt natural to give them an escape hatch.&lt;/p&gt;
&lt;p&gt;I mean, if all you know in life are loops, how can you possibly make do with just recursion? So when it was time for a new homework assignment we gave them a hint along the following lines:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Try solving with Pythonish pseudocode first, and every time you have a loop, you can convert it to a tail recursive function as follows...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Proceeding to explain how to convert loops to tail recursion&lt;sup&gt;&lt;a href=&quot;#user-content-fn-opposite&quot; id=&quot;user-content-fnref-opposite&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. At that, some of the students seemed mildly relieved, and continued with their homework.&lt;/p&gt;
&lt;p&gt;Fast forward after the submissions. A student approached me after class with a question. I was surprised, till now I scarcely seen them outside their Haskell stupor. And here&apos;s what the student had to say. &quot;If all we did was write some Python in Haskell syntax, what&apos;s the point of learning functional programming?&quot;.&lt;/p&gt;
&lt;p&gt;Thinking about it, he was right. With the escape hatch we gave them they really could solve the homework by mechanically translating Python code into Haskell. Apart from some new syntax, you&apos;re not really learning much of anything new. And that&apos;s unfortunate, there&apos;s so much to learn from Haskell and functional programming in general. It would be a shame to miss the opportunity.&lt;/p&gt;
&lt;p&gt;Can we do better?&lt;/p&gt;
&lt;h2 id=&quot;the-homework&quot;&gt;The Homework&lt;/h2&gt;
&lt;p&gt;The homework problem was to solve the &lt;a href=&quot;https://en.wikipedia.org/wiki/Knight%27s_tour&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;knight&apos;s tour problem&lt;/a&gt;. That is, given a chess board of some arbitrary dimensions and a starting position, find a path that a knight can take that will go through all the cells of the board exactly once.&lt;/p&gt;
&lt;p&gt;Efficiency was not the point of this exercise, so the students could just do a brute-force search for the correct path using backtracking. Suppose you&apos;re a student and Python is your main weapon of choice. How would you solve this?&lt;/p&gt;
&lt;p&gt;Here&apos;s the core function of a very naive attempt at brute-forcing the solution&lt;sup&gt;&lt;a href=&quot;#user-content-fn-github&quot; id=&quot;user-content-fnref-github&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tour&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;n&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;visited&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;moves&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pos&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;len&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;visited&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; n &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; n&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; moves&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; knight_move &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; all_moves&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;row&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; col&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; pos&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dx&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; dy&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; knight_move&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;next_row &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; row &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; dx&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;next_col &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; col &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; dy&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;next_pos &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;next_row&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; next_col&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;is_valid&lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;n&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; visited&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; next_row&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; next_col&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;new_visited &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; visited &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;next_pos&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt; 6&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;new_moves &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; moves &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;knight_move&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt; 7&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;result &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tour&lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;n&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; new_visited&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; new_moves&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; next_pos&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt; 8&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; result &lt;/span&gt;&lt;span&gt;is&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;not&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;None&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;                &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; result&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt; 9&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;None&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is not very good code, for various reasons. But that&apos;s not really the point, it (slowly) solves the problem as stated, and that&apos;s good enough for illustration purposes. Let&apos;s review what this does:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;tour&lt;/code&gt; is a recursive function (1) that takes the current state as input:
&lt;ul&gt;
&lt;li&gt;The size of the board &lt;code&gt;n&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The list of already &lt;code&gt;visited&lt;/code&gt; coordinates&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;moves&lt;/code&gt; we constructed so far&lt;/li&gt;
&lt;li&gt;And a tuple of coordinates &lt;code&gt;pos&lt;/code&gt; for the current position&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;We have a stopping condition (2), if the path we visited covered the whole board, in which case we are done and we return the moves that were constructed&lt;/li&gt;
&lt;li&gt;If not, we iterate over all possible knight moves (3), which are defined as a separate enum&lt;/li&gt;
&lt;li&gt;For each such move, we construct the next cell that we are going to visit (4)&lt;/li&gt;
&lt;li&gt;If the new cell is valid (5), i.e., within the bounds of the board and wasn&apos;t visited before&lt;/li&gt;
&lt;li&gt;We add the new position to the list of visited cells (6), and the current move to the moves we are building&lt;sup&gt;&lt;a href=&quot;#user-content-fn-quadratic&quot; id=&quot;user-content-fnref-quadratic&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;We then proceed with the next step by recursively calling on &lt;code&gt;tour&lt;/code&gt; with the new state (7)&lt;/li&gt;
&lt;li&gt;If the result of the recursive call was successful we return that (8)&lt;/li&gt;
&lt;li&gt;Otherwise, we backtrack on this attempt and try the next knight move in the list&lt;/li&gt;
&lt;li&gt;When we exhausted all the possible knight moves, we give up and return &lt;code&gt;None&lt;/code&gt; (9)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is all good and well, but the actual homework is in Haskell. What good is it to have this Python solution instead?&lt;/p&gt;
&lt;h2 id=&quot;a-haskell-rewrite&quot;&gt;A Haskell Rewrite&lt;/h2&gt;
&lt;p&gt;It is true that having a concrete (and &lt;a href=&quot;https://blog.daniel-beskin.com/2025-06-10-first-make-it-correct&quot;&gt;correct&lt;/a&gt;) solution written out is very helpful in thinking about a problem. But with the tip that we gave the students about loops and recursion, it&apos;s even better than that. It&apos;s actually fairly mechanical to translate the Python code into Haskell. To wit&lt;sup&gt;&lt;a href=&quot;#user-content-fn-haskellsimple&quot; id=&quot;user-content-fnref-haskellsimple&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;tour n visited moves row col &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; length visited &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; n &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; n&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Just&lt;/span&gt;&lt;span&gt; moves &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; go allMoves &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;go &lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Nothing&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;go &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;knightMove &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; rest&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dx&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; dy&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; moveValue knightMove&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;nextRow &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; row &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; dx&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;nextCol &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; col &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; dy&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;nextPos &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;nextRow&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; nextCol&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;     &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; isValid n visited nextRow nextCol&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;            &lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; newVisited &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; visited &lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;nextPos&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;newMoves &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; moves &lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;knightMove&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;result &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; tour n newVisited newMoves nextRow nextCol&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;             &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; result &lt;/span&gt;&lt;span&gt;of&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;                  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Just&lt;/span&gt;&lt;span&gt; solution &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Just&lt;/span&gt;&lt;span&gt; solution&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;                  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Nothing&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; go rest &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; go rest &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; 6&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&apos;re familiar with Haskell syntax, this code should look basically the same as the original Python code. There are a few differences, but they are mostly cosmetic:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Since there&apos;s no &lt;code&gt;None&lt;/code&gt; (or &lt;code&gt;null&lt;/code&gt;s in general) in Haskell, we have to wrap things with &lt;code&gt;Maybe&lt;/code&gt; (1), it&apos;s a bit more syntax compared to Python, but not by much&lt;/li&gt;
&lt;li&gt;Haskell is expression-oriented, so there are no early returns&lt;/li&gt;
&lt;li&gt;As a consequence the stopping condition has an &lt;code&gt;else&lt;/code&gt; (2) that invokes the logic for the next step&lt;/li&gt;
&lt;li&gt;The main loop over knight moves was replaced by a tail recursive function called &lt;code&gt;go&lt;/code&gt; (3), this is our tip in action&lt;/li&gt;
&lt;li&gt;We need a bit more ceremony when working with &lt;code&gt;Maybe&lt;/code&gt;, so we pattern match on it (4)&lt;/li&gt;
&lt;li&gt;Since we are not running in a loop, we must call the next backtracking step explicitly by invoking &lt;code&gt;go&lt;/code&gt; (5, 6) with a new argument&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&apos;re a student who just created this Haskell solution from the Python sketch, what new things did you learn?&lt;/p&gt;
&lt;p&gt;You might&apos;ve learned that it&apos;s possible to work without nulls. But since this code is small, the consequences of not having &lt;code&gt;null&lt;/code&gt; are not very visible, and you mostly get just some added syntactic noise&lt;sup&gt;&lt;a href=&quot;#user-content-fn-map&quot; id=&quot;user-content-fnref-map&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;. And not having &lt;code&gt;null&lt;/code&gt; is not a uniquely functional thing, e.g., Rust doesn&apos;t use &lt;code&gt;null&lt;/code&gt; as well.&lt;/p&gt;
&lt;p&gt;There&apos;s a glimpse of being more expression-oriented here. That&apos;s a deep and very impactful principle. But since we just mechanically translated from Python, it&apos;s easy to miss its significance. This is even more obscured by the fact that it&apos;s difficult to appreciate the consequences of expression-orientedness &quot;in the small&quot;.&lt;/p&gt;
&lt;p&gt;Lastly, we learned that it&apos;s possible to replace iteration with simple recursion. Which is cool and all, but what&apos;s the point? &lt;code&gt;for&lt;/code&gt; loops work well enough, doing recursion just for that feels like a lousy (and overly general) syntax for something we already have figured out.&lt;/p&gt;
&lt;p&gt;If you submit this solution to the problem it won&apos;t be out of place to wonder &quot;what&apos;s the point?&quot;.&lt;/p&gt;
&lt;h2 id=&quot;going-functional&quot;&gt;Going Functional&lt;/h2&gt;
&lt;p&gt;Now you may be screaming at the screen that no, functional programming is worth learning, it&apos;s not just Python with uglier syntax. And you would be right, there is a lot to learn, even with a simple assignment such as the one we are working with now.&lt;/p&gt;
&lt;p&gt;But to get these benefits we must make an effort to break away from our &quot;mainstream&quot; roots, and stop translating the old way into a new syntax. We need to completely change the way we think about the problem and its solution.&lt;/p&gt;
&lt;p&gt;For problems like this knight&apos;s tour, one good approach is to use what is called &lt;a href=&quot;https://www.cis.upenn.edu/~cis1940/fall14/lectures/01-intro.html#:~:text=to%20the%20abstract.-,Wholemeal%20programming,-Another%20theme%20we&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&quot;wholemeal programming&quot;&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Functional languages excel at wholemeal programming, a term coined by Geraint Jones. Wholemeal programming means to think big: work with an entire list, rather than a sequence of elements; develop a solution space, rather than an individual solution; imagine a graph, rather than a single path. The wholemeal approach often offers new insights or provides new perspectives on a given problem. It is nicely complemented by the idea of projective programming: first solve a more general problem, then extract the interesting bits and pieces by transforming the general program into more specialised ones.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So instead of exploring the solution space step by step, we&apos;ll create a list of the whole solution space at once, and then use that list to solve the problem we are interested in. Then I&apos;m sure we&apos;ll find something new to learn about programming and problem-solving.&lt;/p&gt;
&lt;p&gt;The first issue to tackle is, how can we possibly represent the whole solution space? It might be huge. Lucky for us, Haskell is lazy by default, so we can just pretend that we have the full list, but actually compute it fully only on demand.&lt;/p&gt;
&lt;p&gt;Next, since we are going to deal with a state space, what are the actual states that we will be working with?&lt;/p&gt;
&lt;p&gt;We can encode a single state in the tour with this record&lt;sup&gt;&lt;a href=&quot;#user-content-fn-haskellwholemeal&quot; id=&quot;user-content-fnref-haskellwholemeal&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TourState&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TourState&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;board&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;visited&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;KnightPos&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;moves&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;KnightMove&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;current&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;KnightPos&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These are basically the arguments to the original &lt;code&gt;tour&lt;/code&gt; function wrapped up in a single record (with some more civilized wrapper types)&lt;sup&gt;&lt;a href=&quot;#user-content-fn-set&quot; id=&quot;user-content-fnref-set&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;8&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;To be able to explore the state space, we need a way to move between states. For that we can define:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;moveState&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TourState&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;KnightMove&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Maybe&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TourState&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Given a single state and a move, we compute the next state by calculating the new position and updating the list of moves. Since not every move is legal from a given state, we accommodate possible failure with a &lt;code&gt;Maybe&lt;/code&gt;. If we can&apos;t move to the next state the result will be &lt;code&gt;Nothing&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can see the implementation in the &lt;a href=&quot;https://github.com/ncreep/point-of-learning-fp-blog/blob/master/KnightsTourWholemeal.hs&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;repo&lt;/a&gt;, but it&apos;s basically the same logic that we had before, just adapted to using &lt;code&gt;TourState&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With &lt;code&gt;moveState&lt;/code&gt; we can apply one move to a state. Next we want to try out all the possible knight moves from a state. The signature will be:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;nextStates&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TourState&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;TourState&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From a given &lt;code&gt;TourState&lt;/code&gt; generate all possible (legal) states that can be reached by a single step. Here&apos;s the implementation:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;nextStates state &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; mapMaybe &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;moveState state&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; allKnightMoves&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We take all the possible knight moves and with the help of &lt;code&gt;mapMaybe&lt;/code&gt; apply &lt;code&gt;moveState&lt;/code&gt; from the current state to every element in the list of moves. &lt;code&gt;mapMaybe&lt;/code&gt; takes care of removing all the &lt;code&gt;Nothing&lt;/code&gt; entries, so that we end up with a flat list with just the legal states we can reach.&lt;/p&gt;
&lt;p&gt;This is actually our first example of using wholemeal programming. Notice how we didn&apos;t iterate step by step. Instead of thinking about moving between individual knight moves, we applied the moving logic to the whole list with &lt;code&gt;mapMaybe&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With these helpers we are now ready to create a (potentially) infinite list of all the states that we can be in while searching for a tour from the initial state. Concretely, we need to implement the following signature:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;allStatesFrom&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TourState&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;TourState&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Given a single state, we create a list of all the states that can be reached from it.&lt;/p&gt;
&lt;p&gt;Creating an infinite list of steps might sound intimidating, but we&apos;ll do it anyways:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;allStatesFrom state &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;next &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; nextStates state &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;allTheRest &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; concatMap allStatesFrom next &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;   &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;state &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; allTheRest &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We start from the initial &lt;code&gt;state&lt;/code&gt; (1), and we compute all the legal &lt;code&gt;next&lt;/code&gt; states using &lt;code&gt;nextStates&lt;/code&gt; (2). This is one step into the search. Next, we want to take the next steps from each of these new states. We do it by recursively&lt;sup&gt;&lt;a href=&quot;#user-content-fn-unfold&quot; id=&quot;user-content-fnref-unfold&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;9&lt;/a&gt;&lt;/sup&gt; calling &lt;code&gt;allStatesFrom&lt;/code&gt; on each of the &lt;code&gt;next&lt;/code&gt; states (3). &lt;code&gt;concatMap&lt;/code&gt; flattens all the resulting lists into one big list. We construct the full list of states by prepending the initial &lt;code&gt;state&lt;/code&gt; to &lt;code&gt;allTheRest&lt;/code&gt; with all the deeper steps (4).&lt;/p&gt;
&lt;p&gt;And just like that we have a list that represents all the possible states in the problem we are solving. This is a bit mind-bending, take a moment to let the tricky recursion sink in properly.&lt;/p&gt;
&lt;p&gt;Notice how &quot;wholesome&quot; this code is. We don&apos;t deal with edge cases or stopping conditions, we just generated the whole thing in one fell swoop. Nor do we worry about going off into infinity, laziness prevents this list from being computed prematurely&lt;sup&gt;&lt;a href=&quot;#user-content-fn-finite&quot; id=&quot;user-content-fnref-finite&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;10&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;With this list in hand, it&apos;s almost trivial to solve the knight&apos;s tour problem. We just need to find the first state from the list that covers the whole board.&lt;/p&gt;
&lt;p&gt;We can define the stopping condition like so:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;isComplete &lt;/span&gt;&lt;span&gt;TourState&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;board&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;visited&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;boardSize board &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; length visited&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function determines whether a given state is a solution by checking the size of the list of visited positions (which happens to be the same as the stopping condition in the original Python solution).&lt;/p&gt;
&lt;p&gt;With these two building blocks in hand, a list of states and a stopping condition, we can easily &lt;em&gt;compose&lt;/em&gt; them into a single solution:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;tour&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;KnightPos&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Maybe&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;KnightMove&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;tour board pos &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;init &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; initState board pos&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;finalState &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; find isComplete &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;allStatesFrom init&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;   &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fmap getMoves finalState &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Step by step:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The final &lt;code&gt;tour&lt;/code&gt; function takes in a board and an initial position (1)&lt;/li&gt;
&lt;li&gt;From this we build the initial state (2), which just initializes a fresh instance of &lt;code&gt;TourState&lt;/code&gt; with the current position and an empty list of moves&lt;/li&gt;
&lt;li&gt;Now for the highlight of this solution, we use &lt;code&gt;find&lt;/code&gt; (3) to locate the first &lt;code&gt;TourState&lt;/code&gt; that matches &lt;code&gt;isComplete&lt;/code&gt; in the full list of states that we generated from the initial state&lt;/li&gt;
&lt;li&gt;The final result (4) just extracts the moves from the state that we found (which might be missing, hence the &lt;code&gt;fmap&lt;/code&gt; call)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I find this approach to be breathtakingly elegant. Thanks to the classic &quot;Why Functional Programming Matters&quot; &lt;a href=&quot;https://www.cs.kent.ac.uk/people/staff/dat/miranda/whyfp90.pdf&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;paper&lt;/a&gt; for the inspiration. Not only does it work, but now we have an opportunity to learn something truly novel.&lt;/p&gt;
&lt;h2 id=&quot;what-did-we-learn&quot;&gt;What Did We Learn?&lt;/h2&gt;
&lt;p&gt;With such a strikingly different solution, we are bound to learn something new. Here are a few lessons that we can take away from this.&lt;/p&gt;
&lt;h3 id=&quot;laziness&quot;&gt;Laziness&lt;/h3&gt;
&lt;p&gt;To generate the list of states we used laziness in an essential, non-contrived way. Without laziness, the list could potentially overflow memory or take too much time to compute upfront, making it impractical to generate &quot;the whole state space&quot;.&lt;/p&gt;
&lt;p&gt;Haskell&apos;s built-in laziness makes this invisibly seamless (for better or for worse), but the lesson that we learn here is applicable almost anywhere. Once we recognize this need for laziness we can use the appropriate technique to achieve it in pretty much any modern language. Be it generators in Python, streams/iterators in Java, or any streaming library in any language.&lt;/p&gt;
&lt;p&gt;Laziness, in its conceptual form, is a powerful tool we can use anywhere. And developing an intuition for it from lazy Haskell lists makes the approach to it relatively gentle.&lt;/p&gt;
&lt;h3 id=&quot;explicit-state-space&quot;&gt;Explicit State Space&lt;/h3&gt;
&lt;p&gt;The way we went about exploring the state space forced us to actually think about that space. What are the possible states? How do we move between them?&lt;/p&gt;
&lt;p&gt;This was explicitly reified in the &lt;code&gt;TourState&lt;/code&gt; type and the &lt;code&gt;moveState&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;The state space exists independently of whether we bother to acknowledge it or not. But my feeling is that when thinking imperatively about code it&apos;s much easier to &quot;miss the forest for the trees&quot;. Focusing on each step we are making in some iterative loop tends to hide the significance of the state space as a whole from us.&lt;/p&gt;
&lt;p&gt;Whether or not you&apos;re applying functional programming to your code, keeping in mind the state space can only be beneficial. If only for something like &quot;&lt;a href=&quot;https://www.youtube.com/watch?v=PSh7JUfDstE&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;make illegal states unrepresentable&lt;/a&gt;&quot;.&lt;/p&gt;
&lt;h3 id=&quot;modularity&quot;&gt;Modularity&lt;/h3&gt;
&lt;p&gt;By separating the exploration of the state space from the stopping condition we made the code much more modular than what it was before. Previously we had to intertwine the stopping condition directly into iteration, but no more.&lt;/p&gt;
&lt;p&gt;This kind of modularity opens the door to many possible modifications that we can apply to one part of the code without disturbing the others.&lt;/p&gt;
&lt;p&gt;Some examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We can change the stopping condition without touching the state space exploration logic. Instead of using &lt;code&gt;find&lt;/code&gt; to find the first solution, we could search for all solutions. Or we could search only for &quot;closed tours&quot; (where the final cell is adjacent to the initial cell).&lt;/li&gt;
&lt;li&gt;In the solution code we are exploring the state space using a preorder depth-first traversal. We can easily adapt it to be any other depth-first traversal, or with some more effort we can make it into any traversal order we want.&lt;/li&gt;
&lt;li&gt;The current algorithm is a naive brute-force approach, we can make things faster by pruning the state space list with heuristics, like applying &lt;a href=&quot;https://en.wikipedia.org/wiki/Knight%27s_tour#Warnsdorf&amp;#x27;s_rule&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;Warnsdorf&apos;s rule&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The important thing is that we can make all these changes by modifying only the code that is responsible for it, without touching its surroundings. This also means that everything can be tested separately without having to test unrelated parts.&lt;/p&gt;
&lt;p&gt;Contrast this to the original code where there&apos;s no way to modify or test anything without touching everything else. The stopping condition sits smack in the middle of iteration.&lt;/p&gt;
&lt;p&gt;That&apos;s a level of modularity that takes some effort to achieve with procedural code, but flows out naturally from the wholemeal approach to solving problems.&lt;/p&gt;
&lt;h3 id=&quot;composition&quot;&gt;Composition&lt;/h3&gt;
&lt;p&gt;Complementing modularity we have composition&lt;sup&gt;&lt;a href=&quot;#user-content-fn-composition&quot; id=&quot;user-content-fnref-composition&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;11&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;It&apos;s one thing to split things into modules, but another thing is how easy it is to compose the modules back into a single piece of code that actually solves the full problem.&lt;/p&gt;
&lt;p&gt;In our case it was very easy, just a call to &lt;code&gt;find&lt;/code&gt; along with the state space list and the completion predicate. And the same applies to all the different variants described in the previous section. We can easily mix and match the different implementations to generate new working pieces of software.&lt;/p&gt;
&lt;p&gt;Using higher-order functions (&lt;code&gt;find&lt;/code&gt;) as the glue for simple, purely-functional (or side-effect-free) components is a great recipe to obtain code with good composition properties.&lt;/p&gt;
&lt;p&gt;Quoting from &lt;a href=&quot;https://web.archive.org/web/20221225045948/https://blog.tmorris.net/posts/why-functional-programming-matters-in-short-prose/index.html&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;Tony Morris&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Suppose any function that exists. I will suppose one that is made of parts A, B, C and D. I might denote this A ∘ B ∘ C ∘ D. Now suppose you desire a function that is made of parts A, B, C and E. Then the effort required for your function should be proportional to the effort required for E alone.&lt;/p&gt;
&lt;p&gt;The closer you are to achieving this objective for any function, the closer you are to the essence of (pure) functional programming. This exists independently of the programming language, though programming languages will vary significantly in the degree to which they accommodate this objective.&lt;/p&gt;
&lt;p&gt;Imperative programming – specifically the scope of a destructive update – is the anti-thesis of this objective.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;wholemeal-programming&quot;&gt;Wholemeal Programming&lt;/h3&gt;
&lt;p&gt;Tying all these concepts together is wholemeal programming, showing us how to tackle problems with a wholly different mindset.&lt;/p&gt;
&lt;p&gt;Not only does it force us to think differently about problem-solving, but it brings concrete, practical advantages to the code we produce.&lt;/p&gt;
&lt;p&gt;Although it&apos;s possible to see every one of these advantages in other paradigms, functional programming really shines in making those advantages stand out. Once you learn about them, you can apply them pretty much anywhere. You just need to stop mechanically translating your existing knowledge and break away from everything you think you know.&lt;/p&gt;
&lt;p&gt;So what&apos;s the point of learning functional programming? You tell me...&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-guilt&quot;&gt;
&lt;p&gt;I&apos;m sure the guilt fades away over time, but I was doing this for only one semester. &lt;a href=&quot;#user-content-fnref-guilt&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-opposite&quot;&gt;
&lt;p&gt;Funny how usually programmers find themselves going in the opposite direction. Let&apos;s ignore for now the fact that in Haskell tail recursion is &lt;a href=&quot;https://stackoverflow.com/questions/13042353/does-haskell-have-tail-recursive-optimization&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;not that important&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-opposite&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-github&quot;&gt;
&lt;p&gt;The full code for all the examples that follow can be found on &lt;a href=&quot;https://github.com/ncreep/point-of-learning-fp-blog&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;. The Python solution &lt;a href=&quot;https://github.com/ncreep/point-of-learning-fp-blog/blob/master/knights_tour.py&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-github&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-quadratic&quot;&gt;
&lt;p&gt;Appending to a list in a loop like this is quadratic, which might slow down the implementation. Can you think of a better way to do this while still using a simple list? &lt;a href=&quot;#user-content-fnref-quadratic&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-haskellsimple&quot;&gt;
&lt;p&gt;See the full code &lt;a href=&quot;https://github.com/ncreep/point-of-learning-fp-blog/blob/master/KnightsTourSimple.hs&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-haskellsimple&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-map&quot;&gt;
&lt;p&gt;Nor do we use any of the special &lt;code&gt;Maybe&lt;/code&gt; functions like &lt;code&gt;map&lt;/code&gt;. &lt;a href=&quot;#user-content-fnref-map&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 6&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-haskellwholemeal&quot;&gt;
&lt;p&gt;See the full code &lt;a href=&quot;https://github.com/ncreep/point-of-learning-fp-blog/blob/master/KnightsTourWholemeal.hs&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-haskellwholemeal&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 7&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-set&quot;&gt;
&lt;p&gt;We are still keeping a naive mindset towards performance so as to not distract from the main point. But playing around with the types of the record can yield some speedups (e.g., not using lists for sets). &lt;a href=&quot;#user-content-fnref-set&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 8&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-unfold&quot;&gt;
&lt;p&gt;Recursion is sometimes called &quot;the goto of functional programming&quot;. One might attempt to remove the explicit recursion in favor of something more structured like &lt;code&gt;unfold&lt;/code&gt;. &lt;a href=&quot;#user-content-fnref-unfold&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 9&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-finite&quot;&gt;
&lt;p&gt;The state space for this particular problem is actually finite. But that&apos;s beside the point, since it can be &lt;em&gt;very&lt;/em&gt; large for sufficiently large boards. For such boards actually computing the whole list would likely crash the program. &lt;a href=&quot;#user-content-fnref-finite&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 10&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-composition&quot;&gt;
&lt;p&gt;I&apos;m not using the term &quot;compositionality&quot; because it has a more specific &lt;a href=&quot;https://medium.com/statebox/modularity-vs-compositionality-a-history-of-misunderstandings-be0150033568&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;technical meaning&lt;/a&gt; which I&apos;m not trying to imply here. &lt;a href=&quot;#user-content-fnref-composition&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 11&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><author>Daniel Beskin</author></item><item><title>Pretty Symlinking with Home Manager</title><link>https://blog.daniel-beskin.com/2025-10-18-symlinking-home-manager</link><guid isPermaLink="true">https://blog.daniel-beskin.com/2025-10-18-symlinking-home-manager</guid><description>Use the power of the Nix language to make your Home Manager symlinks pretty.</description><pubDate>Sat, 18 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://blog.daniel-beskin.com/social-cards/2025-10-18-symlinking-home-manager.png&quot; alt=&quot;Cover image for Pretty Symlinking with Home Manager&quot; style=&quot;width: 100%; height: auto; margin-bottom: 2rem;&quot;&gt;&lt;p&gt;I have a confession to make. It might not be appropriate to admit in polite society, but I&apos;m not ashamed: I don&apos;t manage all my dot files with Nix.&lt;/p&gt;
&lt;p&gt;Unless I get some concrete benefit like templating or cross-library integration, I can&apos;t be bothered to rewrite arbitrary dot files into Nix. And beyond that, I just don&apos;t want to be forced to rebuild every time I make a tiny tweak to this or that setting&lt;sup&gt;&lt;a href=&quot;#user-content-fn-tweak&quot; id=&quot;user-content-fnref-tweak&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;On the other hand, I do like the idea that all my configs, Nix or otherwise, are in the same repo. And since I&apos;m already using a tremendously powerful file management tool, Home Manager, I don&apos;t want to bring in yet another tool like &lt;a href=&quot;https://brandon.invergo.net/news/2012-05-26-using-gnu-stow-to-manage-your-dotfiles.html&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;GNU Stow&lt;/a&gt; or &lt;a href=&quot;https://www.chezmoi.io/&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;chezmoi&lt;/a&gt;. Call me simple-minded, but I just want as few tools as reasonably possible&lt;sup&gt;&lt;a href=&quot;#user-content-fn-reasonable&quot; id=&quot;user-content-fnref-reasonable&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; to achieve my goals.&lt;/p&gt;
&lt;p&gt;Luckily, Home Manager got our back. What I just described fits nicely within the capabilities of &lt;a href=&quot;https://github.com/nix-community/home-manager/blob/4958aafe7b237dc1e857fb0c916efff72075048f/modules/files.nix#L84&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;mkOutOfStoreSymlink&lt;/a&gt;. And there are &lt;a href=&quot;https://gvolpe.github.io/blog/home-manager-dotfiles-management/&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;tutorials&lt;/a&gt; on how to make use of it for this exact purpose&lt;sup&gt;&lt;a href=&quot;#user-content-fn-flakes&quot; id=&quot;user-content-fnref-flakes&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;. But this post is not about the capabilities of Home Manager (or Nix in general), but rather how people use the Nix language itself.&lt;/p&gt;
&lt;p&gt;While setting up my system, I was looking for examples around the web, and a typical example of using &lt;code&gt;mkOutOfStoreSymlink&lt;/code&gt; would look something like this&lt;sup&gt;&lt;a href=&quot;#user-content-fn-github&quot; id=&quot;user-content-fnref-github&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;symlinkRoot&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;# 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;xdg&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;configFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;# 2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;# 3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;copyq/copyq.conf&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;lib&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;mkOutOfStoreSymlink&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;symlinkRoot&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/copyq/copyq.conf&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;shellcheckrc&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;lib&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;mkOutOfStoreSymlink&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;symlinkRoot&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/shellcheckrc&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;spaceship.zsh&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;lib&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;mkOutOfStoreSymlink&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;symlinkRoot&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/spaceship.zsh&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;# 4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;eww&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;lib&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;mkOutOfStoreSymlink&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;symlinkRoot&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/eww&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;recursive&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fish&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;lib&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;mkOutOfStoreSymlink&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;symlinkRoot&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/fish&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;recursive&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nushell&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;lib&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;mkOutOfStoreSymlink&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;symlinkRoot&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/nushell&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;recursive&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nvim&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;lib&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;mkOutOfStoreSymlink&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;symlinkRoot&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/nvim&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;recursive&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tmux&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;lib&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;mkOutOfStoreSymlink&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;symlinkRoot&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/tmux&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;recursive&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;waybar&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;lib&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;mkOutOfStoreSymlink&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;symlinkRoot&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/waybar&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;recursive&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is not particularly complicated Nix code. We get the root directory for the symlinks as an argument (1)&lt;sup&gt;&lt;a href=&quot;#user-content-fn-flakesagain&quot; id=&quot;user-content-fnref-flakesagain&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;. We then set up &lt;code&gt;xdg.configFile&lt;/code&gt; (2), which takes an attribute set and creates matching files in &lt;code&gt;XDG_CONFIG_HOME&lt;/code&gt;. Finally we set the actual standalone files (3), and directories (4) with &lt;code&gt;recursive = true&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Great, that works as well as it should, but, oh my eyes... I&apos;m a programmer, damn it. I have &lt;code&gt;DRY&lt;/code&gt; tattooed on my forehead. It hurts deep down in my soul to see that much repetition:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The repeated fully qualified &lt;code&gt;mkOutOfStoreSymlink&lt;/code&gt; calls&lt;/li&gt;
&lt;li&gt;The repeated &lt;code&gt;symlinkRoot&lt;/code&gt; prefix&lt;/li&gt;
&lt;li&gt;The doubly named files and folders (both keys and sources); typos, anyone?&lt;/li&gt;
&lt;li&gt;The repeated &lt;code&gt;source&lt;/code&gt; and &lt;code&gt;recursive&lt;/code&gt; keys&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And it&apos;s only going to get worse as we keep on adding more and more keys here.&lt;/p&gt;
&lt;p&gt;We&apos;re so used to using highly constrained and repetitive configuration formats like YAML&lt;sup&gt;&lt;a href=&quot;#user-content-fn-dhall&quot; id=&quot;user-content-fnref-dhall&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;, that this kind of never ending tedium is not perceived as something out of the ordinary. Just another heap of text to maintain along with a pile of Kubernetes YAMLs, nothing to see here... Maybe one day someone invents a templating engine for Nix code to relieve some of the noise.&lt;/p&gt;
&lt;p&gt;Hyperbole aside, I do suspect that many of the people that write Nix treat it as just another &quot;config syntax&quot;. While in reality Nix is a full-fledged programming language, capable of many things you might expect a programming language to do&lt;sup&gt;&lt;a href=&quot;#user-content-fn-turing&quot; id=&quot;user-content-fnref-turing&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;. DRYing code with Nix is very much possible, and I would claim that it&apos;s even desirable.&lt;/p&gt;
&lt;p&gt;One might argue that the repetition allows for some flexibility. What if some of the entries do not map the files one-to-one, suppose you want to rename a file on the fly? Maybe, but we don&apos;t actually use that flexibility most of the time. So from now on I&apos;ll consider the problem of simple, stow-like, symlinking without any special cases. Special cases can be added separately.&lt;/p&gt;
&lt;p&gt;Now, not everyone share my sensibilities and sensitivities when it comes to code that you have to maintain (even as a hobby). I&apos;m also, like many software engineers when presented with shiny new toys, prone to over-engineering. So if you&apos;re fine with this kind of repetition and it works for you, keep at it. Don&apos;t let me infect you with my DRY cough. For the rest of you, how do we clean this code up?&lt;/p&gt;
&lt;p&gt;I&apos;ll present a series of steps trying to reduce repetition and to (subjectively) aid readability. I don&apos;t claim that any of them are necessary, or that the resulting code is &quot;the Nix way&quot; of doing things. What I want to do is show that you can make configuration in Nix less repetitive and more readable, you just need to pick the right tools from whatever the Nix language offers. Hopefully this will be mostly beginner-friendly as well.&lt;/p&gt;
&lt;h2 id=&quot;imports&quot;&gt;Imports&lt;/h2&gt;
&lt;p&gt;Although Nix doesn&apos;t have separate import statements like many mainstream languages, you can simulate them easily with &lt;code&gt;let&lt;/code&gt; bindings to make thing more readable:&lt;/p&gt;
&lt;pre data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;symlinkRoot&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;# 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;lib&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;mkOutOfStoreSymlink&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;xdg&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;configFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;# 2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;copyq/copyq.conf&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;symlinkRoot&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/copyq/copyq.conf&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;shellcheckrc&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;symlinkRoot&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/shellcheckrc&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;spaceship.zsh&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;symlinkRoot&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/spaceship.zsh&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;eww&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;symlinkRoot&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/eww&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;recursive&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;# ... similarly for the rest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have the &quot;import&quot; on (1), we just assign the fully qualified function reference to another value (which we can name whatever we want). Then use that instead of the fully qualified name (2). Already much less noisy.&lt;/p&gt;
&lt;p&gt;This simulates a renaming import. We can also have a &quot;regular import&quot; with the following syntax:&lt;/p&gt;
&lt;pre data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;inherit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;lib&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mkOutOfStoreSymlink&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;in&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;# ... use mkOutOfStoreSymlink unqualified&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;inherit&lt;/code&gt; syntax binds any symbol we can reference to its unqualified name. But I like the shorter name for now. We&apos;ll use &lt;code&gt;inherit&lt;/code&gt; more later on.&lt;/p&gt;
&lt;h2 id=&quot;functions&quot;&gt;Functions&lt;/h2&gt;
&lt;p&gt;One of the most ancient ways to DRY up code is to use functions. Simple, small, everyday functions. Nix being a functional language has a very pleasant syntax for functions, so there&apos;s no reason not to use it.&lt;/p&gt;
&lt;p&gt;First step, let&apos;s stop repeating the &lt;code&gt;symlinkRoot&lt;/code&gt; prefix:&lt;/p&gt;
&lt;pre data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;symlinkRoot&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;lib&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;mkOutOfStoreSymlink&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;# 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;toSrcFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;symlinkRoot&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;xdg&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;configFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;# 2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;copyq/copyq.conf&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;toSrcFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;copyq/copyq.conf&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;shellcheckrc&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;toSrcFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;shellcheckrc&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;spaceship.zsh&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;toSrcFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;spaceship.zsh&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;eww&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;toSrcFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;eww&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;recursive&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;# ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of manually prepending &lt;code&gt;symlinkRoot&lt;/code&gt; for each entry, we defined a new function &lt;code&gt;toSrcFile&lt;/code&gt; (1). It takes the name of the file as an argument and prepends &lt;code&gt;symlinkRoot&lt;/code&gt; to it. Now we use &lt;code&gt;toSrcFile&lt;/code&gt; in each entry (2) without repeating the reference to &lt;code&gt;symlinkRoot&lt;/code&gt; every time.&lt;/p&gt;
&lt;p&gt;Looking at the new code though, it seems that we only ever use &lt;code&gt;link&lt;/code&gt; in combination with &lt;code&gt;symlinkRoot&lt;/code&gt;. Let&apos;s redefine &lt;code&gt;link&lt;/code&gt; to call &lt;code&gt;toSrcFile&lt;/code&gt; and stop repeating this combo:&lt;/p&gt;
&lt;pre data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;symlinkRoot&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;# 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;inherit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;lib&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mkOutOfStoreSymlink&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;toSrcFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;symlinkRoot&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;# 2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mkOutOfStoreSymlink&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;toSrcFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;xdg&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;configFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;# 3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;copyq/copyq.conf&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;copyq/copyq.conf&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;shellcheckrc&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;shellcheckrc&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;spaceship.zsh&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;spaceship.zsh&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;eww&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;eww&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;recursive&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;# ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;inherit&lt;/code&gt; is back is business now. We import &lt;code&gt;mkOutOfStoreSymlink&lt;/code&gt; (1), and now &lt;code&gt;link&lt;/code&gt; is redefined to call &lt;code&gt;mkOutOfStoreSymlink&lt;/code&gt; after applying &lt;code&gt;toSrcFile&lt;/code&gt; (2). And now the &lt;code&gt;link&lt;/code&gt; calls look very clean (3).&lt;/p&gt;
&lt;h2 id=&quot;programmatic-keys&quot;&gt;Programmatic Keys&lt;/h2&gt;
&lt;p&gt;Now the starkest bit of repetition are the file paths. Each name is repeated both as a key and as an argument to &lt;code&gt;link&lt;/code&gt;. If this was YAML we would be stuck, but in Nix we can create attribute sets with dynamically specified keys. They don&apos;t have to be string literals. Here&apos;s a function to link a single file:&lt;/p&gt;
&lt;pre data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;linkFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function creates an attribute with the provided key &lt;code&gt;name&lt;/code&gt; (and &lt;code&gt;source&lt;/code&gt; under it). The &lt;code&gt;${name}&lt;/code&gt; syntax lets us splice the dynamic key name into the set without using a string literal.&lt;/p&gt;
&lt;p&gt;We can do the same thing with directories&lt;sup&gt;&lt;a href=&quot;#user-content-fn-unify&quot; id=&quot;user-content-fnref-unify&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;8&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;linkDir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;recursive&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These functions can now be used to create all the file entries without repeating the path. But we have a wrinkle. Calling &lt;code&gt;linkFile&lt;/code&gt; creates a set with a single key. Calling it multiple times will produce multiple sets, each with one key. What we need though, is one set with multiple keys. Since I don&apos;t know how to create &quot;bare&quot; key/value pairs, we&apos;ll just have to merge all these redundant sets into one using the &lt;a href=&quot;https://nix.dev/manual/nix/2.26/language/operators#update&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;//&lt;/code&gt;&lt;/a&gt; operator. Like so:&lt;/p&gt;
&lt;pre data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;# ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;confFiles&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;# 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;linkFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;copyq/copyq.conf&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;linkFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;shellcheckrc&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;linkFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;spaceship.zsh&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;confDirs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;# 2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;linkDir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;eww&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;linkDir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fish&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;linkDir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nushell&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;linkDir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nvim&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;linkDir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tmux&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;linkDir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;waybar&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;# 3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;links&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;confFiles&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;confDirs&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;xdg&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;configFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;links&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No more filename repetition! We make multiple calls to &lt;code&gt;linkFile&lt;/code&gt; (1) and &lt;code&gt;linkDir&lt;/code&gt; (2) and combine the results into one big attribute set with &lt;code&gt;//&lt;/code&gt; (3), the resulting &lt;code&gt;links&lt;/code&gt; value is an attribute set that we can assign to &lt;code&gt;xdg.confFile&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;list-processing&quot;&gt;List Processing&lt;/h2&gt;
&lt;p&gt;Written out this way, this looks a lot like we have two lists (separated by &lt;code&gt;//&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-fold&quot; id=&quot;user-content-fnref-fold&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;9&lt;/a&gt;&lt;/sup&gt;), one for files and one for directories. Each entry is prefixed with either &lt;code&gt;linkFile&lt;/code&gt; or &lt;code&gt;linkDir&lt;/code&gt;, the last source of repetition in this code. Let&apos;s make the list processing explicit.&lt;/p&gt;
&lt;p&gt;Nix has a bunch list processing functions, and it&apos;s quite easy to set up a &quot;data processing pipeline&quot;. First we extract the lists we want to deal with:&lt;/p&gt;
&lt;pre data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;confFiles&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;copyq/copyq.conf&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;shellcheckrc&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;spaceship.zsh&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;confDirs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;eww&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fish&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nushell&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nvim&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tmux&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;waybar&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now to each entry of a list we need to apply the appropriate function (either &lt;code&gt;linkFile&lt;/code&gt; or &lt;code&gt;linkDir&lt;/code&gt;). The way to do it is to use &lt;a href=&quot;https://noogle.dev/f/lib/map&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;map&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;# ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;inherit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;lib&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;# 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;confFiles&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;linkFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;copyq/copyq.conf&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;shellcheckrc&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;spaceship.zsh&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;# 2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;confDirs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;linkDir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;eww&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fish&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nushell&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nvim&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tmux&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;waybar&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;# 3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;links&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;confFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;confDirs&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;# ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;map&lt;/code&gt; takes care of iterating over the list and applying the supplied function to each entry (1, 2). We are dealing with lists now, so we combine the two lists into &lt;code&gt;links&lt;/code&gt; using &lt;code&gt;++&lt;/code&gt; (3). We are almost back to the original &lt;code&gt;links&lt;/code&gt; value. But the types don&apos;t quite add up.&lt;/p&gt;
&lt;p&gt;Previously &lt;code&gt;links&lt;/code&gt; was an attribute set, now it&apos;s a list of attribute sets. No worries though, that&apos;s why we have &lt;a href=&quot;https://noogle.dev/f/lib/mergeAttrsList&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;mergeAttrsList&lt;/code&gt;&lt;/a&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-foldinstead&quot; id=&quot;user-content-fnref-foldinstead&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;10&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;inherit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;lib&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mergeAttrsList&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;confFiles&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;linkFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;copyq/copyq.conf&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;shellcheckrc&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;spaceship.zsh&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;confDirs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;linkDir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;eww&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fish&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nushell&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nvim&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tmux&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;waybar&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;links&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mergeAttrsList&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;confFiles&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;confDirs&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;xdg&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;configFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;links&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that&apos;s where I&apos;ll stop DRYing up the code. We removed all of the egregious repetitions, and I think that with the two explicit lists of files the essence of what&apos;s going on is much clearer (contingent on good naming for the custom functions).&lt;/p&gt;
&lt;h2 id=&quot;cosmetics&quot;&gt;Cosmetics&lt;/h2&gt;
&lt;p&gt;Lastly, let&apos;s apply some cosmetic changes to the code, to make it really shine. This is getting very subjective, but I personally like the &quot;DSL aesthetic&quot;, with small, well-named, custom functions to do my bidding. I&apos;m sure that many people prefer using built-in functions more explicitly, since it&apos;s easier to look up what they do. Even if you don&apos;t like the style below, I think the DSL/small library mentality can be useful at times.&lt;/p&gt;
&lt;p&gt;A minor tweak we can do to hide away the explicit list manipulation is to extract the &lt;code&gt;map&lt;/code&gt; calls into helper functions:&lt;/p&gt;
&lt;pre data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;linkConfFiles&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;linkFile&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;linkConfDirs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;linkDir&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# 2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;confFiles&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;linkConfFiles&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;copyq/copyq.conf&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;shellcheckrc&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;spaceship.zsh&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# 3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;confDirs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;linkConfDirs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;eww&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fish&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nushell&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nvim&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tmux&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;waybar&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With &lt;code&gt;map link...&lt;/code&gt; assigned to new functions (1), we can apply the new &lt;code&gt;linkConfFiles&lt;/code&gt; and &lt;code&gt;linkConfDirs&lt;/code&gt; directly to our file lists (2, 3). The main code now flows better without mentioning the plumbing with lists and &lt;code&gt;map&lt;/code&gt;. Nix&apos;s curried functions make creating these small helpers a breeze. Note how we didn&apos;t need to explicitly name the argument to &lt;code&gt;linkConfFiles&lt;/code&gt;, we just partially applied &lt;code&gt;map&lt;/code&gt; (a.k.a, point-free style).&lt;/p&gt;
&lt;p&gt;Next, this pattern of combining attribute set lists with &lt;code&gt;++&lt;/code&gt; and then merging seems to be pretty useful. Let&apos;s make it work more generically. Instead of working with just two lists, we can make it work with a list of lists:&lt;/p&gt;
&lt;pre data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;inherit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;lib&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;flatten&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mergeAttrsList&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;flatMerge&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sets&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mergeAttrsList&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;flatten&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sets&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;flatMerge&lt;/code&gt; is now doing the same thing, but relies on the built-in &lt;code&gt;flatten&lt;/code&gt; to concatenate a list of lists, rather than just two lists. We use it like so:&lt;/p&gt;
&lt;pre data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;links&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;flatMerge&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;confFiles&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;confDirs&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, when you work enough with functional-styled code some patterns tend emerge. One of them is applying a chain of functions one after the other. In the code so far we had two examples of it:&lt;/p&gt;
&lt;pre data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mkOutOfStoreSymlink&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;toSrcFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;flatMerge&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sets&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mergeAttrsList&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;flatten&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sets&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We first apply one function, and then another to the output of the first one. This is &lt;a href=&quot;https://en.wikipedia.org/wiki/Function_composition&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;function composition&lt;/a&gt;, the same one you learned about in school algebra. It tends to pop out a lot. This pattern can be easily abstracted a way, without us manually writing new functions every time we need to compose a couple of functions.&lt;/p&gt;
&lt;p&gt;For some reason, Nix doesn&apos;t have a built-in function composition operator. But it does have the &lt;a href=&quot;https://noogle.dev/f/lib/pipe&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;pipe&lt;/code&gt;&lt;/a&gt; function, which takes a value and a list of functions, it then composes them (in reverse) and applies the resulting function to the value.&lt;/p&gt;
&lt;p&gt;Convenient though &lt;code&gt;pipe&lt;/code&gt; is, its signature is not quite what we want. It would be more reusable for us to first receive a list of functions, and then the value. This is easily remedied with the &lt;a href=&quot;https://noogle.dev/f/lib/flip&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;flip&lt;/code&gt;&lt;/a&gt; function:&lt;/p&gt;
&lt;pre data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;inherit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;lib&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;flatten&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;flip&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mergeAttrsList&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;pipe&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;flip&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lib&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;pipe&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# 2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;flatMerge&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pipe&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;flatten&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mergeAttrsList&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;# 3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pipe&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;toSrcFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mkOutOfStoreSymlink&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In (1) we override the built-in &lt;code&gt;pipe&lt;/code&gt; with the flipped version. With the &lt;code&gt;pipe&lt;/code&gt; in hand&lt;sup&gt;&lt;a href=&quot;#user-content-fn-everythingpipe&quot; id=&quot;user-content-fnref-everythingpipe&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;11&lt;/a&gt;&lt;/sup&gt; we can turn &lt;code&gt;flatMerge&lt;/code&gt; (2) and &lt;code&gt;link&lt;/code&gt; (3) into explicit pipelines of composed functions, without manually spelling out the function composition. As a bonus, this easily generalizes to any number of (compatible) functions.&lt;/p&gt;
&lt;p&gt;And that&apos;s all the cosmetics we&apos;ll apply for today. The full code now looks like this:&lt;/p&gt;
&lt;pre data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;lib&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;symlinkRoot&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;inherit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;lib&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mkOutOfStoreSymlink&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;inherit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;lib&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;flatten&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;flip&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mergeAttrsList&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pipe&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;flip&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lib&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;pipe&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;flatMerge&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pipe&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;flatten&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mergeAttrsList&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;toSrcFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;symlinkRoot&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pipe&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;toSrcFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mkOutOfStoreSymlink&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;linkFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;linkDir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;recursive&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;linkConfFiles&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;linkFile&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;linkConfDirs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;linkDir&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;confFiles&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;linkConfFiles&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;copyq/copyq.conf&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;shellcheckrc&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;spaceship.zsh&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;confDirs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;linkConfDirs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;eww&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fish&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nushell&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nvim&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tmux&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;waybar&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;links&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;flatMerge&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;confFiles&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;confDirs&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;xdg&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;configFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;links&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can find all the different revisions in an executable format in the &lt;a href=&quot;https://github.com/ncreep/nixos-examples-blog/tree/master/symlinks&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;repo&lt;/a&gt;. If you want homework, you can try to apply the same approach to files linked directly to &lt;code&gt;home.file&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;My eyes can rest now. But if we were really serious about Nix programming, we would probably turn this into a real, configurable, Nix module, and hide away all the utility functions from our hypothetical users.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-tweak&quot;&gt;
&lt;p&gt;Although usually I don&apos;t tweak configs much after a short period of initial set up, some apps get tweaked continuously. For example, Neovim, whose configuration (gasp) I don&apos;t manage through Nix. &lt;a href=&quot;#user-content-fnref-tweak&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-reasonable&quot;&gt;
&lt;p&gt;Reasonable people may disagree what to consider &quot;reasonable&quot;. But I&apos;m fine with using myself as the golden standard for being reasonable. &lt;a href=&quot;#user-content-fnref-reasonable&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-flakes&quot;&gt;
&lt;p&gt;If you&apos;re using flakes, keep in mind how flakes &lt;a href=&quot;https://github.com/nix-community/home-manager/issues/2085&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;treat relative paths&lt;/a&gt; and be careful to use absolute paths when passing arguments to &lt;code&gt;mkOutOfStoreSymlink&lt;/code&gt;. &lt;a href=&quot;#user-content-fnref-flakes&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-github&quot;&gt;
&lt;p&gt;You can find the code for these examples in the &lt;a href=&quot;https://github.com/ncreep/nixos-examples-blog/tree/master/symlinks&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;repo&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-github&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-flakesagain&quot;&gt;
&lt;p&gt;Don&apos;t forget that this must be an absolute path if you&apos;re using flakes. &lt;a href=&quot;#user-content-fnref-flakesagain&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-dhall&quot;&gt;
&lt;p&gt;Instead of using a more capable solution like &lt;a href=&quot;https://dhall-lang.org/&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;Dhall&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-dhall&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 6&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-turing&quot;&gt;
&lt;p&gt;Although one might argue that when used for configuring NixOS it might be beneficial to have a non-Turing complete language, something in the vein of what &lt;a href=&quot;https://dhall-lang.org/&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;Dhall&lt;/a&gt; does. &lt;a href=&quot;#user-content-fnref-turing&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 7&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-unify&quot;&gt;
&lt;p&gt;I leave it as an exercise for the reader to unify these two functions and to reduce that tiny repetition with the &lt;code&gt;source&lt;/code&gt; key. &lt;a href=&quot;#user-content-fnref-unify&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 8&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-fold&quot;&gt;
&lt;p&gt;This sure looks a lot like an application of some &lt;code&gt;fold&lt;/code&gt; variant. &lt;a href=&quot;https://en.wikipedia.org/wiki/Free_monoid#Map_and_fold&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;Coincidence&lt;/a&gt;? &lt;a href=&quot;#user-content-fnref-fold&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 9&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-foldinstead&quot;&gt;
&lt;p&gt;We could&apos;ve use a fold instead of mapping and merging. But that would&apos;ve been clunkier to write, and possibly less performant, since &lt;code&gt;mergeAttrsList&lt;/code&gt; seems more optimized with some kind of &lt;a href=&quot;https://noogle.dev/f/lib/mergeAttrsList#implementation&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;binary merging&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-foldinstead&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 10&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-everythingpipe&quot;&gt;
&lt;p&gt;When all you have is a pipe, everything looks like a functional pipeline... &lt;a href=&quot;#user-content-fnref-everythingpipe&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 11&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><author>Daniel Beskin</author></item><item><title>Validating Custom Keyboard Layouts on NixOS</title><link>https://blog.daniel-beskin.com/2025-10-04-validating-custom-keyboard-layouts-on-nixos</link><guid isPermaLink="true">https://blog.daniel-beskin.com/2025-10-04-validating-custom-keyboard-layouts-on-nixos</guid><description>If you&apos;re in the rare intersection of people that use NixOS and custom keyboard layouts, this post is for you. And for those of you who don&apos;t use custom layouts, you might learn something useful…</description><pubDate>Sat, 04 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://blog.daniel-beskin.com/social-cards/2025-10-04-validating-custom-keyboard-layouts-on-nixos.png&quot; alt=&quot;Cover image for Validating Custom Keyboard Layouts on NixOS&quot; style=&quot;width: 100%; height: auto; margin-bottom: 2rem;&quot;&gt;&lt;p&gt;If you&apos;re in the rare intersection of people that use NixOS and custom keyboard layouts, this post is for you. And for those of you who don&apos;t use custom layouts, you might learn something useful about build-time assertions and laziness.&lt;/p&gt;
&lt;p&gt;All three of you still reading, here we go.&lt;/p&gt;
&lt;p&gt;So, one day I decided that I wanted to use a custom keyboard layout on my NixOS machine. Should be simple enough. If you find the &lt;a href=&quot;https://nixos.org/manual/nixos/stable/#custom-xkb-layouts&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;right bit&lt;/a&gt; of documentation, you&apos;ll find out that you can easily and declaratively add custom XKB layouts to NixOS using the &lt;code&gt;services.xserver.xkb.extraLayouts&lt;/code&gt; key. This presumes that you understand how to write your own XKB symbol files, a nontrivial assumption given how arcane that format is&lt;sup&gt;&lt;a href=&quot;#user-content-fn-xkbtutorial&quot; id=&quot;user-content-fnref-xkbtutorial&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Suppose you have the following simple XKB symbol file&lt;sup&gt;&lt;a href=&quot;#user-content-fn-github&quot; id=&quot;user-content-fnref-github&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;xkb_symbols &quot;test&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;include &quot;us(basic)&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;key &amp;#x3C;CAPS&gt; {[ Escape ]};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This file defines a new layout based on the regular QWERTY layout, mapping the capslock key to escape.&lt;/p&gt;
&lt;p&gt;Adding this to your NixOS setup is easy enough:&lt;/p&gt;
&lt;pre data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;services&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;xserver&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;xkb&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;extraLayouts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Test&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;languages&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;eng&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;symbolsFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;./test.xkb&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each entry under &lt;code&gt;extraLayouts&lt;/code&gt; points to one XKB symbols file along with some metadata. If you add this to your NixOS config, this will build and successfully add the new layout to the system.&lt;/p&gt;
&lt;p&gt;But then I kept on reading the documentation and saw the following warning:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;... a broken XKB file can lead to the X session crashing at login. Therefore, you&apos;re strongly advised to &lt;strong&gt;test your layout before applying it&lt;/strong&gt;...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And then they show you some cryptic bash commands to actually perform the testing. This decidedly doesn&apos;t feel like the Nix way. What&apos;s the point of having this amazing build machinery if I have to run some manual commands every time I decide to update my keyboard layouts&lt;sup&gt;&lt;a href=&quot;#user-content-fn-frequency&quot; id=&quot;user-content-fnref-frequency&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;?&lt;/p&gt;
&lt;p&gt;No, what I want is to validate my layouts during the Nix build, and stop the build if the XKB file is invalid.&lt;/p&gt;
&lt;p&gt;After some digging around, I found this somewhat dated documentation on the &lt;a href=&quot;https://nixos.wiki/wiki/Keyboard_Layout_Customization#Advanced&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;Wiki&lt;/a&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;# 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;compiledLayout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pkgs&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;runCommand&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;keyboard-layout&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;pkgs&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;xorg&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;xkbcomp&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/bin/xkbcomp &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;./path/to/layout.xkb&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; $out&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;in&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;# 2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;services&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;xserver&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;displayManager&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;sessionCommands&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;pkgs&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;xorg&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;xkbcomp&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/bin/xkbcomp &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;compiledLayout&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; $DISPLAY&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This uses &lt;code&gt;xkbcomp&lt;/code&gt; to compile the layout file (1), which should fail if the file is invalid. It then passes on the result of compilation as part of the &lt;code&gt;sessionCommands&lt;/code&gt; for XServer (2). But using &lt;code&gt;sessionCommands&lt;/code&gt; is outdated, we want to use the newer &lt;code&gt;extraLayouts&lt;/code&gt; way of doing things, which feels more high-level.&lt;/p&gt;
&lt;p&gt;So let&apos;s combine the two approaches: compile only to validate the layout, and if compilation succeeded, use the &lt;code&gt;extraLayouts&lt;/code&gt; setting like we did before.&lt;/p&gt;
&lt;p&gt;To make sure this actually work for us, let&apos;s check the relevant commands in the terminal:&lt;/p&gt;
&lt;pre data-language=&quot;console&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;xkbcomp ./test.xkb&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This won&apos;t have any output if the file is valid. But suppose you make some error in the file, then you&apos;ll get something like this:&lt;/p&gt;
&lt;pre data-language=&quot;console&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;➜&lt;/span&gt;&lt;span&gt; xkbcomp ./test.xkb&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;syntax error: line 5 of ./test.xkb&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;last scanned symbol is: CAPS&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Errors encountered in ./test.xkb; not compiled.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is exactly what we need, a check we can run during the build. The only wrinkle with &lt;code&gt;xkbcomp&lt;/code&gt; is that it makes what I find to be a strange usage of exit codes. Whether or not compilation succeeds, the exit code is always &lt;code&gt;1&lt;/code&gt;. No worries though, it&apos;s easy enough to accommodate.&lt;/p&gt;
&lt;p&gt;With this tool in hand, here&apos;s my naive first attempt to write a Nix function that compiles the layout before using it:&lt;/p&gt;
&lt;pre data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;buildLayout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;# 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;lang&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;symbols&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;# 2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;xkbcomp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pkgs&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;lib&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getExe&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pkgs&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;xorg&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;xkbcomp&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;# 3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;compilationOutputFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pkgs&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;runCommand&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;-compiled-keyboard-layout&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;xkbcomp&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;symbols&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; 2&gt; $out) || true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;# 4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;compilationOutput&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;builtins&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;readFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;compilationOutputFile&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;# 5&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;compilationSuccess&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;compilationOutput&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;abort&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Failure compiling layout [&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;]: &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;compilationOutput&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;# 6&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;languages&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;lang&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;symbolsFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;symbols&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a bit long, so step by step:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In (1) we define a new function &lt;code&gt;buildLayout&lt;/code&gt; that takes all the data that we need to construct a new layout, &lt;code&gt;symbols&lt;/code&gt; refers to the XKB symbols file&lt;/li&gt;
&lt;li&gt;We find the executable for &lt;code&gt;xkbcomp&lt;/code&gt; (2)&lt;/li&gt;
&lt;li&gt;And use it to validate the symbols file (3)
&lt;ul&gt;
&lt;li&gt;We want to run the command and get its output to be available within the build&lt;/li&gt;
&lt;li&gt;For that we use &lt;code&gt;runCommand&lt;/code&gt; which will run the given command and let us access its output&lt;/li&gt;
&lt;li&gt;This is done by running &lt;code&gt;xkbcomp&lt;/code&gt; and redirecting the error output to &lt;code&gt;$out&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Due to the quirk with the exit code of &lt;code&gt;xkbcomp&lt;/code&gt; described above, we ignore it completely and always succeed with &lt;code&gt;|| true&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;After running this, &lt;code&gt;compilationOutputFile&lt;/code&gt; points to a file that should either be empty if the layout is valid, or contain the error message that &lt;code&gt;xkbcomp&lt;/code&gt; produced&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;We then read the file back into the variable &lt;code&gt;compilationOutput&lt;/code&gt; (4)&lt;/li&gt;
&lt;li&gt;And finally the check (5), if &lt;code&gt;compilationOutput&lt;/code&gt; is empty nothing happens, but otherwise we &lt;code&gt;abort&lt;/code&gt; with a friendly message that contains the output from &lt;code&gt;xkbcomp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;After all that, we can construct the new layout definition (6), safe in the knowledge that if we got this far, the layout is definitely valid&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With &lt;code&gt;buildLayout&lt;/code&gt; in hand, we can set our now verified layout:&lt;/p&gt;
&lt;pre data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;services&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;xserver&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;xkb&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;extraLayouts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;buildLayout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Test&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;lang&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;eng&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;symbols&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;./test.xkb&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Done, the layout building and verification is fully automated. We can try building this new module with the following command&lt;sup&gt;&lt;a href=&quot;#user-content-fn-fullfile&quot; id=&quot;user-content-fnref-fullfile&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;console&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;nix build --impure --expr &apos;let pkgs = import &amp;#x3C;nixpkgs&gt; {}; in (import ./with-validation1.nix { inherit pkgs; }).services.xserver.xkb.extraLayouts&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unfortunately, this never fails. Whether or not the XKB file is valid or not doesn&apos;t change a thing. The build always passes. Can you see the issue?&lt;/p&gt;
&lt;p&gt;It took me a bit to spot it, but Nix is a lazy language. So unless an expression is explicitly used for anything, it&apos;s never evaluated&lt;sup&gt;&lt;a href=&quot;#user-content-fn-shame&quot; id=&quot;user-content-fnref-shame&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;In the new flow I haphazardly mixed and matched the validation logic from one place with layout setting from another. In the outdated documentation the result of compilation was actually referenced in an expression that is used by NixOS, transitively forcing the evaluation of the compilation command&lt;sup&gt;&lt;a href=&quot;#user-content-fn-error&quot; id=&quot;user-content-fnref-error&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Notice that in the command I&apos;m running above, I explicitly reference &lt;code&gt;extraLayouts&lt;/code&gt;, this will force the evaluation of the &lt;code&gt;buildLayout&lt;/code&gt; call. Within &lt;code&gt;buildLayout&lt;/code&gt; itself, the &lt;code&gt;compilationSuccess&lt;/code&gt; value is defined in a &lt;code&gt;let&lt;/code&gt; but it&apos;s never actually used in the output of the &lt;code&gt;let&lt;/code&gt; expression. That is to say, it is not referenced in the final output that we produce. As a result, under lazy evaluation, we don&apos;t have to evaluate &lt;code&gt;compilationSuccess&lt;/code&gt; to compute the output of &lt;code&gt;buildLayout&lt;/code&gt;. In effect, we never actually run the check we so lovingly crafted.&lt;/p&gt;
&lt;p&gt;I have no qualms with laziness in the Nix language in general, can&apos;t really imagine the flexibility of nixpkgs without it, but I really do want that check to be executed. So we need to chain it to the final output.&lt;/p&gt;
&lt;p&gt;Like so:&lt;/p&gt;
&lt;pre data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;# ... same as before&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;compilationSuccess&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;compilationOutput&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;# 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;compilationSuccess&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;# 2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;languages&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;lang&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;symbolsFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;symbols&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;# 3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;abort&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Failure compiling layout [&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;]: &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;compilationOutput&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Most of the code remains as is, but we now modify &lt;code&gt;compilationSuccess&lt;/code&gt; to only check the condition without aborting (1). We then branch on the result of &lt;code&gt;compilationSuccess&lt;/code&gt; (2), and abort if it&apos;s &lt;code&gt;false&lt;/code&gt; (3). As a result &lt;code&gt;compilationSuccess&lt;/code&gt; and transitively the &lt;code&gt;xkbcomp&lt;/code&gt; call, is chained to the final output.&lt;/p&gt;
&lt;p&gt;And this works as expected, if we build it with a broken layout:&lt;/p&gt;
&lt;pre data-language=&quot;console&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;➜&lt;/span&gt;&lt;span&gt; nix build --impure --expr &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;let pkgs = import &amp;#x3C;nixpkgs&gt; {}; in (import ./with-validation2.nix { inherit pkgs; }).services.xserver.xkb.extraLayouts&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;error:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;… while evaluating the attribute &apos;test&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;at with-validation2.nix:22:5:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;           &lt;/span&gt;&lt;/span&gt;&lt;span&gt;21|   in {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;           &lt;/span&gt;&lt;/span&gt;&lt;span&gt;22|     ${name} =&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;|     ^&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;           &lt;/span&gt;&lt;/span&gt;&lt;span&gt;23|       if compilationSuccess&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;… while calling the &apos;abort&apos; builtin&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;at with-validation2.nix:29:12:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;           &lt;/span&gt;&lt;/span&gt;&lt;span&gt;28|       }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;           &lt;/span&gt;&lt;/span&gt;&lt;span&gt;29|       else abort &quot;Failure compiling layout [${name}]: ${compilationOutput}&quot;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;|            ^&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;           &lt;/span&gt;&lt;/span&gt;&lt;span&gt;30|   };&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;error: evaluation aborted with the following error message: &apos;Failure compiling layout [test]: syntax error: line 5 of /nix/store/ilwgm7nihyzy30avldk7nr2wim9vwj9d-test.xkb&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;last scanned symbol is: CAPS&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Errors encountered in /nix/store/ilwgm7nihyzy30avldk7nr2wim9vwj9d-test.xkb; not compiled.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A bit verbose, but the build fails as required, and the compilation error from &lt;code&gt;xkbcomp&lt;/code&gt; is printed along the way.&lt;/p&gt;
&lt;p&gt;Since doing an &lt;code&gt;if/else&lt;/code&gt; and &lt;code&gt;abort&lt;/code&gt; every time I want to validate something feels a bit noisy, we can use utility functions to prettify it. There are a few ways to do this, like the builtin &lt;code&gt;assert&lt;/code&gt; special form, maybe paired with the slightly more friendly &lt;a href=&quot;https://noogle.dev/f/lib/assertMsg&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;assertMsg&lt;/code&gt;&lt;/a&gt;. But I think that the syntax for &lt;a href=&quot;https://noogle.dev/f/lib/throwIfNot&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;throwIfNot&lt;/code&gt;&lt;/a&gt; is a bit cleaner. Here&apos;s the final version&lt;sup&gt;&lt;a href=&quot;#user-content-fn-final&quot; id=&quot;user-content-fnref-final&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;# ... same as before&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;compilationSuccess&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;compilationOutput&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;# 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;errorMessage&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Failure compiling layout [&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;]: &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;compilationOutput&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;# 2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ifCompilationSuccess&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lib&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;throwIfNot&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;compilationSuccess&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;errorMessage&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;# 3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ifCompilationSuccess&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;languages&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;lang&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;symbolsFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;symbols&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To prettify things we extracted the error message into a variable (1), now laziness is on our side, it won&apos;t be evaluated unless actually used. We then create a new function called &lt;code&gt;ifCompilationSuccess&lt;/code&gt; (2). It uses &lt;code&gt;throwIfNot&lt;/code&gt; to check the &lt;code&gt;compilationSuccess&lt;/code&gt; value and will throw the &lt;code&gt;errorMessage&lt;/code&gt; if it&apos;s false. &lt;code&gt;throwIfNot&lt;/code&gt; returns the identity function if the condition is &lt;code&gt;true&lt;/code&gt;, otherwise it throws an error. Lastly, we apply &lt;code&gt;ifCompilationSuccess&lt;/code&gt; to the result of &lt;code&gt;buildLayout&lt;/code&gt; (3), and by this we chained the compilation check into the flow, and kept the noise to a minimum.&lt;/p&gt;
&lt;p&gt;This works as before, although the error message looks a bit different due to the difference in formatting between &lt;code&gt;abort&lt;/code&gt; and &lt;code&gt;throw&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We are now done. We can easily create and modify XKB layouts without worrying about breaking our system. Any errors will be caught at build time.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-xkbtutorial&quot;&gt;
&lt;p&gt;If you&apos;re feeling particularly masochistic, you can read an in depth &lt;a href=&quot;https://medium.com/@damko/a-simple-humble-but-comprehensive-guide-to-xkb-for-linux-6f1ad5e13450&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;tutorial&lt;/a&gt; on the topic. &lt;a href=&quot;#user-content-fnref-xkbtutorial&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-github&quot;&gt;
&lt;p&gt;The example code can be found on &lt;a href=&quot;https://github.com/ncreep/nixos-examples-blog/tree/master/custom-layouts&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-github&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-frequency&quot;&gt;
&lt;p&gt;I&apos;ll silently ignore the question of how often I &lt;em&gt;actually&lt;/em&gt; modify keyboard layouts. &lt;a href=&quot;#user-content-fnref-frequency&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-fullfile&quot;&gt;
&lt;p&gt;The full file can be found &lt;a href=&quot;https://github.com/ncreep/nixos-examples-blog/blob/master/custom-layouts/with-validation1.nix&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-fullfile&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-shame&quot;&gt;
&lt;p&gt;Shame on me, having spent a semester TAing on a Haskell course, I shouldn&apos;t be surprised by laziness. &lt;a href=&quot;#user-content-fnref-shame&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-error&quot;&gt;
&lt;p&gt;Actually, in the old documentation it&apos;s not clear to me how it could ever work in the first place. Due to the aforementioned exit code quirk of &lt;code&gt;xkbcomp&lt;/code&gt; any build that calls it without error capturing should fail. Maybe it&apos;s more recent behavior. &lt;a href=&quot;#user-content-fnref-error&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 6&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-final&quot;&gt;
&lt;p&gt;The full &lt;a href=&quot;https://github.com/ncreep/nixos-examples-blog/blob/master/custom-layouts/with-validation3.nix&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;file&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-final&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 7&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><author>Daniel Beskin</author></item><item><title>Random Scala Tip #534: Adopt an Error Handling Convention for `Future`</title><link>https://blog.daniel-beskin.com/2025-09-08-random-scala-tip-534-future-error-handling</link><guid isPermaLink="true">https://blog.daniel-beskin.com/2025-09-08-random-scala-tip-534-future-error-handling</guid><description>When working with `Future` be explicit about your error handling convention, and make sure your whole team and codebase are aligned to it.</description><pubDate>Mon, 08 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://blog.daniel-beskin.com/social-cards/2025-09-08-random-scala-tip-534-future-error-handling.png&quot; alt=&quot;Cover image for Random Scala Tip #534: Adopt an Error Handling Convention for `Future`&quot; style=&quot;width: 100%; height: auto; margin-bottom: 2rem;&quot;&gt;&lt;p&gt;We seem to be living in the future. Surrounded by an ever increasing number of advanced effect-systems, capability trackers, and even salads&lt;sup&gt;&lt;a href=&quot;#user-content-fn-caprese&quot; id=&quot;user-content-fnref-caprese&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. Yet I&apos;m sure that many of us, in an effort to bring food on the table, are still stuck in the past, using &lt;code&gt;Future&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There are quite a few things to say about Scala&apos;s default &lt;code&gt;Future&lt;/code&gt; implementation, some of them good, some not so much, but I&apos;ll focus on one thing: error handling. For better or for worse&lt;sup&gt;&lt;a href=&quot;#user-content-fn-worse&quot; id=&quot;user-content-fnref-worse&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, errors in &lt;code&gt;Future&lt;/code&gt; are not tracked by the compiler. That is to say, it&apos;s impossible to statically distinguish between a potentially failing &lt;code&gt;Future&lt;/code&gt; instance, and another instance that cannot fail&lt;sup&gt;&lt;a href=&quot;#user-content-fn-failure&quot; id=&quot;user-content-fnref-failure&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;. More concretely in code:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;doStuff&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Stuff&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;defaultStuff&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Stuff&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;foo&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Future&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;doStuff&lt;/span&gt;&lt;span&gt;())&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bar&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Future&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;doStuff&lt;/span&gt;&lt;span&gt;())&lt;/span&gt;&lt;span&gt;.recover&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NonFatal&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ex&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; defaultStuff&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; foo&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;y&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; bar&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The types of &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; are both &lt;code&gt;Future[Stuff]&lt;/code&gt;, despite the fact that &lt;code&gt;bar&lt;/code&gt; handles exceptions, and &quot;cannot fail&quot;. There is no way to reflect that difference between &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; with types. This leads to some unfortunate consequences.&lt;/p&gt;
&lt;p&gt;With this in mind, and without any help from the compiler, we must live in constant fear of exceptions. Can an exception lurk in this &lt;code&gt;Future&lt;/code&gt; or that&lt;sup&gt;&lt;a href=&quot;#user-content-fn-runtimeex&quot; id=&quot;user-content-fnref-runtimeex&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;?.. What should we do about it? Should we do anything? Every developer touching the codebase might have different answers to these questions which can lead to a host of different surprises.&lt;/p&gt;
&lt;p&gt;This is no way to maintain an existence, so in lieu of the type system&apos;s support, here is today&apos;s tip:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When working with &lt;code&gt;Future&lt;/code&gt; be explicit about your error handling convention, and make sure your whole team and codebase are aligned to it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you adopt this tip, some of the anxiety from what I wrote above might dissipate.&lt;/p&gt;
&lt;p&gt;Lest this stays completely vague, let&apos;s discuss some concrete error handling conventions we can use.&lt;/p&gt;
&lt;h2 id=&quot;yolo&quot;&gt;YOLO&lt;/h2&gt;
&lt;p&gt;As the name of this convention implies, here we don&apos;t worry much about anything, and about &lt;code&gt;Future&lt;/code&gt; errors in particular. You use exceptions in &lt;code&gt;Future&lt;/code&gt; as you see fit, and you never bother handling them in any specific place.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;: There&apos;s only one answer to all of the questions above, and it requires zero effort or discipline.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;: Let me know if you find any.&lt;/p&gt;
&lt;h2 id=&quot;full-on-defensive&quot;&gt;Full On Defensive&lt;/h2&gt;
&lt;p&gt;In this convention we move to the other end of the worrying scale: every instance of &lt;code&gt;Future&lt;/code&gt; is suspect, we trust no one but ourselves, and we call &lt;code&gt;recover&lt;/code&gt; (and maybe add some logging for good measure) every time we stumble on &lt;code&gt;Future&lt;/code&gt; in the code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;: Yet again, we have only one answer to all the questions above, it&apos;s always &lt;code&gt;recover&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;: This gets old fast, requires discipline, the code gets cluttered with error handling, and letting loose your paranoia is not likely to get you anywhere productive anyways.&lt;/p&gt;
&lt;h2 id=&quot;only-defects-in-error-channel&quot;&gt;Only Defects in Error Channel&lt;/h2&gt;
&lt;p&gt;This convention is more subtle&lt;sup&gt;&lt;a href=&quot;#user-content-fn-zio&quot; id=&quot;user-content-fnref-zio&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;: we decree that we only use &lt;code&gt;Future&lt;/code&gt;&apos;s error channel for defects in code. I.e., errors that we didn&apos;t predict during development. As a rule, we don&apos;t handle defects till the &quot;top-level&quot; of the app, where we might recover from the defect with some generic logging&lt;sup&gt;&lt;a href=&quot;#user-content-fn-interface&quot; id=&quot;user-content-fnref-interface&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;. Defects are not the norm, and when we discover one, we try to eliminate it from our code.&lt;/p&gt;
&lt;p&gt;Now what I mean by the &quot;top-level&quot; is context dependent. It might be the actual top-level of the application, somewhere in the &quot;main&quot; of the code. But more typically that would be &quot;per request&quot;. So, for example, if you&apos;re writing a web server, you will handle errors at the top-level of the request. We wouldn&apos;t want to crash the whole server just for one failing request. Similarly, if you&apos;re writing some kind of message handler (like with streams, or actors), you would handle errors once per message.&lt;/p&gt;
&lt;p&gt;If the &lt;code&gt;Future&lt;/code&gt;&apos;s error channel is only used for defects, where do we put all other errors? Somewhere where the compiler can track them, of course. So every time we intentionally want to raise and handle an error we will reflect it with a dedicated type like &lt;code&gt;Either&lt;/code&gt; or one of a panoply of &lt;code&gt;Validation&lt;/code&gt; types that are available in the Scala ecosystem&lt;sup&gt;&lt;a href=&quot;#user-content-fn-try&quot; id=&quot;user-content-fnref-try&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;. As a result, when working with code that can fail and be meaningfully recovered, we will have to deal with something like &lt;code&gt;Future[Either[MyError, Stuff]]&lt;/code&gt;. A bit verbose though it may be, this makes it very explicit when we have an error to deal with, versus when all errors have already been dealt with (when we end up with &lt;code&gt;Future[Stuff]&lt;/code&gt; again). And we are letting the compiler guide us in the right direction in case we forget anything.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;: Most of the time&lt;sup&gt;&lt;a href=&quot;#user-content-fn-interface&quot; id=&quot;user-content-fnref-interface-2&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt; we know how to answer all of the questions about error-handling, and the compiler is there to help us, so that we don&apos;t have to rely on discipline too much.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;: Ergonomics, working with nested types can be a bit cumbersome. And if performance is at stake, the extra layer of indirection might be a price that we don&apos;t want to pay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Possible mitigation&lt;/strong&gt;: If we settle for some specific type for error representation (like &lt;code&gt;Either&lt;/code&gt;), we can add some utilities and convenience syntax for the nested &lt;code&gt;Future[Either[_, _]]&lt;/code&gt; type, like support for mapping/flat-mapping the inner value (or use an existing library solution like &lt;a href=&quot;https://typelevel.org/cats/datatypes/eithert.html&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;EitherT&lt;/code&gt;&lt;/a&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-reddit&quot; id=&quot;user-content-fnref-reddit&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;8&lt;/a&gt;&lt;/sup&gt;).&lt;/p&gt;
&lt;h2 id=&quot;others&quot;&gt;Others&lt;/h2&gt;
&lt;p&gt;I&apos;m sure there are many other viable error handling conventions&lt;sup&gt;&lt;a href=&quot;#user-content-fn-submarine&quot; id=&quot;user-content-fnref-submarine&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;9&lt;/a&gt;&lt;/sup&gt;. The two main criteria I would judge them by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How much discipline they require? The less, the better.&lt;/li&gt;
&lt;li&gt;How well can you answer questions about error handling at compile-time? The less thought it requires, the better.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you have other &lt;code&gt;Future&lt;/code&gt; error handling conventions that you find useful, I&apos;ll be glad to hear about them in the comments.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-caprese&quot;&gt;
&lt;p&gt;An &lt;a href=&quot;https://www.slideshare.net/slideshow/capabilities-for-resources-and-effects-252161040/252161040&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;Italian one&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-caprese&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-worse&quot;&gt;
&lt;p&gt;Worse. &lt;a href=&quot;#user-content-fnref-worse&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-failure&quot;&gt;
&lt;p&gt;For the purposes of this discussion I&apos;ll be ignoring any fatal errors, like out-of-memory, and only consider &quot;user space&quot; errors, like IO exceptions and the like. &lt;a href=&quot;#user-content-fnref-failure&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-runtimeex&quot;&gt;
&lt;p&gt;I&apos;ll ignore the fact that Scala has regular runtime exceptions as well. I think it&apos;s pretty conventional for Scala developers to eschew the explicit use of &lt;code&gt;throw&lt;/code&gt;. As a result most of the time I don&apos;t feel much of a need to get all defensive about it. On the other hand, since &lt;code&gt;Future&lt;/code&gt; is a datatype, people seem to feel more comfortable to use &lt;code&gt;Future&lt;/code&gt;&apos;s error channel, just as you would with &lt;code&gt;Try&lt;/code&gt;. I don&apos;t harp about &lt;code&gt;Try&lt;/code&gt; because it&apos;s not as contagious as &lt;code&gt;Future&lt;/code&gt;, and its foremost purpose is error handling, so I expect people to be sufficiently careful around it. &lt;code&gt;Future&lt;/code&gt;&apos;s main purpose in not about errors, so it&apos;s easier to forget about them. &lt;a href=&quot;#user-content-fnref-runtimeex&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-zio&quot;&gt;
&lt;p&gt;The terminology is inspired by the error channels in the &lt;a href=&quot;https://zio.dev/reference/error-management/types/&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;ZIO library&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-zio&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-interface&quot;&gt;
&lt;p&gt;We might have to break the convention in various rare cases. Like when interfacing with &lt;code&gt;Future&lt;/code&gt;-based code that we don&apos;t control. In such cases we will hit &lt;code&gt;recover&lt;/code&gt; as soon as possible, and then continue pretending that our convention was never broken (kind of like you would with &lt;code&gt;null&lt;/code&gt; handling when interfacing with Java). &lt;a href=&quot;#user-content-fnref-interface&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 6&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-interface-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 6-2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-try&quot;&gt;
&lt;p&gt;Or &lt;code&gt;Try&lt;/code&gt;, but it feels kind of strange to work with &lt;code&gt;Future[Try[?]]&lt;/code&gt;, since &lt;code&gt;Future&lt;/code&gt; already conceptually contains a &lt;code&gt;Try&lt;/code&gt;, and you cannot define custom errors for it that are reflected in the type. &lt;a href=&quot;#user-content-fnref-try&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 7&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-reddit&quot;&gt;
&lt;p&gt;Thanks u/pizardwenis96 from &lt;a href=&quot;https://www.reddit.com/r/scala/comments/1ndtkwh/comment/ndliof9/&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;Reddit&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-reddit&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 8&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-submarine&quot;&gt;
&lt;p&gt;Like adapting something like the capability-tracked &lt;a href=&quot;https://typelevel.org/blog/2025/09/02/custom-error-types.html&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&quot;submarine&quot; exception&lt;/a&gt; from Cats Effect. It&apos;s probably just as applicable to &lt;code&gt;Future&lt;/code&gt; as it is to Cats Effect. Especially once capture-checking and co. gain some more mainstream adoption. Also, see &lt;a href=&quot;https://www.reddit.com/r/scala/comments/1ndtkwh/comment/ndnqxf6/&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;this&lt;/a&gt; discussion on Reddit. &lt;a href=&quot;#user-content-fnref-submarine&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 9&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><author>Daniel Beskin</author></item><item><title>Make Illegal AI Edits Unrepresentable</title><link>https://blog.daniel-beskin.com/2025-08-24-illegal-ai-edits</link><guid isPermaLink="true">https://blog.daniel-beskin.com/2025-08-24-illegal-ai-edits</guid><description>These are strange times we&apos;re living in. We have unleashed an army of junior developers on our codebases while changing little else, yet we expect everything to just be okay.</description><pubDate>Sun, 24 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://blog.daniel-beskin.com/social-cards/2025-08-24-illegal-ai-edits.png&quot; alt=&quot;Cover image for Make Illegal AI Edits Unrepresentable&quot; style=&quot;width: 100%; height: auto; margin-bottom: 2rem;&quot;&gt;&lt;p&gt;This post is also available in video form:&lt;/p&gt;
&lt;iframe width=&quot;100%&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/sPjHsMGKJSI?si=om5W_bCB2XbGrq8q&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;hr&gt;
&lt;p&gt;These are strange times we&apos;re living in. We have unleashed an army of junior developers on our codebases while changing little else, yet we expect everything to just be okay.&lt;/p&gt;
&lt;p&gt;Granted, they aren&apos;t exactly junior developers; they are both smarter and dumber, all at once. But it doesn&apos;t change the reality that the cumulative effect of running all sorts of AIs in our codebases, with little to no supervision, can, and likely will, lead to ever increasing complexity and maintainability woes.&lt;/p&gt;
&lt;p&gt;&quot;No, no, I&apos;m doing stringent code review on every line of code the AI generates&quot;, sure you do, buddy. You&apos;re running five agents, all at once, and then expect that you&apos;ll be able to thoroughly review every line of an ever growing volume of generated code?&lt;/p&gt;
&lt;p&gt;Imagine a well-functioning team of five senior developers, working on a large codebase. The complexity is there, slowly growing, but they manage to wrangle it, somehow. Then they bring on some young blood, a brand new junior developer, fresh off the boat from college. The codebase is large and complex, unsurprisingly, the junior makes mistakes. But it&apos;s okay, the seniors are there, at least one of them is always available to guide, review, and clean up any mess that comes up. After a while, the junior is no longer a junior, but a young senior. Soon we&apos;ll have a team of six seniors, and we can bring in some more fresh blood. All is as it should be.&lt;/p&gt;
&lt;p&gt;Next, imagine the same scenario, but now we bring in 25 juniors in one batch. If you think that it&apos;s going to work out fine, you&apos;re most likely delusional. The seniors will have no way to keep up with the juniors, and the code will end up in shambles.&lt;/p&gt;
&lt;p&gt;More generally, when many people work on the same system all at once, be it seniors, juniors, or anything in-between, code quality tends to quickly plummet. It appears that humans, juniors and seniors alike, are pretty bad at taming complexity. They have trouble keeping track of enough context to see the consequences of small changes rippling across code and time.&lt;/p&gt;
&lt;p&gt;Unfortunately, current generation AIs suffer from the same problem. The only difference is that the number of AIs working on code seems to explode these days, overwhelming whatever benefit that experienced developers can contribute to the process.&lt;/p&gt;
&lt;p&gt;In the long run, if we want to be productive using AIs to develop code, we must use better ways to handle complexity.&lt;/p&gt;
&lt;p&gt;Even before the advent of widespread AI tools, I spent most of my professional career thinking about the problem of keeping code complexity at bay in the presence of humans. And now I do the same in the presence of an army of AIs.&lt;/p&gt;
&lt;p&gt;One of the most useful tools for the purpose of complexity management that I know of is the design principle coined by &lt;a href=&quot;https://x.com/yminsky&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;Yaron Minsky&lt;/a&gt; that says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Make illegal states unrepresentable&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And that&apos;s what I want to talk about now. To put this into today&apos;s context, we&apos;ll paraphrase a little bit. Our goal will be to:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Make illegal AI edits unrepresentable&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;the-pitch&quot;&gt;The Pitch&lt;/h2&gt;
&lt;p&gt;I won&apos;t delve into explaining what &quot;illegal states&quot; or &quot;representability&quot; are, hopefully most of you have some intuition about them&lt;sup&gt;&lt;a href=&quot;#user-content-fn-talk&quot; id=&quot;user-content-fnref-talk&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. What I want to highlight though, is that &quot;making illegal states unrepresentable&quot; is an excellent complement to the weaknesses of both humans and AIs that I described above.&lt;/p&gt;
&lt;p&gt;The way one would typically enforce the unrepresentability of illegal states is at compile-time, using the type system. Unlike your average AI, the compiler is enforcing type constraints by taking your whole program as context. It doesn&apos;t &quot;forget&quot; things just because they are too far away in code or time. And while the wet and imprecise brain of an AI can miss things, the cold and calculating brain of a compiler never forgets anything you ever taught it.&lt;/p&gt;
&lt;p&gt;The biggest takeaway from this is that the more rules and invariants we encode into the type system, the more helpful the compiler will be in enforcing those invariants. Deterministically and without fail. So if some illegal state needs to be avoided globally and forever, make it unrepresentable at compile-time, and whether you or your AI remember about it while editing code won&apos;t matter in the least.&lt;/p&gt;
&lt;p&gt;Let&apos;s illustrate this with a concrete example.&lt;/p&gt;
&lt;h2 id=&quot;the-illegal-states&quot;&gt;The Illegal States&lt;/h2&gt;
&lt;p&gt;Let&apos;s pretend we are programming a coffee-making robot&lt;sup&gt;&lt;a href=&quot;#user-content-fn-based&quot; id=&quot;user-content-fnref-based&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. Something like this:
&lt;a href=&quot;https://en.orionstar.com/&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;1050&quot; height=&quot;700&quot; src=&quot;https://blog.daniel-beskin.com/_astro/robot.D4sZEmrE_ZxxiWk.webp&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There are many subsystems involved in operating such a thing. We&apos;ll focus on the coffee-ordering part. Since we are just a small startup, we start with a small proof of concept. For now, we support only one type of drink.&lt;/p&gt;
&lt;p&gt;Apparently, cappuccino is very popular these days, so that&apos;s a good starting point for us. To order cappuccino the user needs to tell us what kind of milk should be used. We&apos;ll model the order with a record class&lt;sup&gt;&lt;a href=&quot;#user-content-fn-github&quot; id=&quot;user-content-fnref-github&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;record&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrder&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nonnull&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Milk&lt;/span&gt;&lt;span&gt; milk&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It appears that choosing milk nowadays is a non-trivial task, so &lt;code&gt;Milk&lt;/code&gt; is actually an enum with multiple choices. Notice the &lt;code&gt;Nonnull&lt;/code&gt; annotation on the &lt;code&gt;Milk&lt;/code&gt; field. I hear that &lt;code&gt;null&lt;/code&gt;s can be &lt;a href=&quot;https://www.reddit.com/r/programming/comments/1lb3jld/root_cause_of_the_june_12_2025_google_cloud_outage/&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;dangerous&lt;/a&gt;, so we are very explicit about how we handle them. Every cappuccino must have &lt;code&gt;Milk&lt;/code&gt; specified.&lt;/p&gt;
&lt;p&gt;After successfully making a cappuccino, we want to expand our repertoire. So might as well make a plain espresso, we already have all the ingredients to make it. Espresso is just cappuccino minus the milk:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;record&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrder&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nullable&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Milk&lt;/span&gt;&lt;span&gt; milk&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now &lt;code&gt;milk&lt;/code&gt; is a nullable field. If the value is present we make a cappuccino, if not, we make an espresso.&lt;/p&gt;
&lt;p&gt;We got the ball rolling, so let&apos;s make another drink: latte. It&apos;s just like cappuccino, but with more milk. So both latte and cappuccino must have &lt;code&gt;Milk&lt;/code&gt; specified. The presence or absence of the &lt;code&gt;milk&lt;/code&gt; field is no longer enough to indicate which type of drink we need. We&apos;ll have to disambiguate the drinks somehow:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;enum&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DrinkType&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CAPPUCCINO&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ESPRESSO&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;LATTE&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;DrinkType&lt;/code&gt; enum will indicate which drink the user ordered. It can be used like so:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;record&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrder&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nonnull&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DrinkType&lt;/span&gt;&lt;span&gt; drinkType&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nullable&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Milk&lt;/span&gt;&lt;span&gt; milk&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can tell which type of drink to prepare, even when the &lt;code&gt;milk&lt;/code&gt; field is present. We are still careful with &lt;code&gt;null&lt;/code&gt;s: &lt;code&gt;drinkType&lt;/code&gt; is mandatory. Otherwise, we won&apos;t be able to tell which drink the user actually ordered. This is a nice way to future-proof ourselves so that we can handle other drinks as well.&lt;/p&gt;
&lt;p&gt;With support for some basic drinks in place, we want to show how fancy our robot is. Let&apos;s add a couple more special drinks: affogato and Irish Coffee.&lt;/p&gt;
&lt;p&gt;The new drinks require more ingredients, so we add them to the &lt;code&gt;CoffeeOrder&lt;/code&gt; record:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;record&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrder&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nonnull&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DrinkType&lt;/span&gt;&lt;span&gt; drinkType&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nullable&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Milk&lt;/span&gt;&lt;span&gt; milk&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nullable&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Gelato&lt;/span&gt;&lt;span&gt; gelato&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nullable&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cream&lt;/span&gt;&lt;span&gt; cream&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nullable&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Whiskey&lt;/span&gt;&lt;span&gt; whiskey&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we have gelato for the affogato, and cream and whiskey for the Irish coffee. The new fields are all nullable, as they are only relevant for these specific drinks.&lt;/p&gt;
&lt;p&gt;We also update &lt;code&gt;DrinkType&lt;/code&gt; to support the new drinks:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;enum&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DrinkType&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CAPPUCCINO&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ESPRESSO&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;LATTE&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;AFFOGATO&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;IRISH_COFFEE&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we take a step back and look at what we have here, we&apos;ll see that although we are only supporting five drinks, this is already quite the mess. The &lt;code&gt;CoffeeOrder&lt;/code&gt; record is definitely capable of representing all the drinks we want it to. But along with that it&apos;s capable of representing nonsensical drinks. Some of them, like espresso with gelato are just redundant. But others make no sense at all. Like Irish coffee without whiskey, which is an illegal state if I ever seen one. And so, we made illegal states representable in our system.&lt;/p&gt;
&lt;p&gt;This example might look a bit contrived, who ever just adds many fields like this to a class all at once? But this is based on actual experience, where a team of developers keeps adding fields over time, without immediately noticing the implications. And before you know it, you end up with a monster full of potential illegal states.&lt;/p&gt;
&lt;p&gt;All is not lost though, we are conscientious developers. We did our best with the nullability annotations. We can also add our knowledge about the different invariants to the code as well. Here&apos;s the final version of &lt;code&gt;CoffeeOrder&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* Invariants:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* - For cappuccino: milk must not be null, all other nullables should be null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* - For espresso: all nullable field should be null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* - For latte: milk must not be null, all other nullables should be null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* - For affogato: gelato must not be null, all other nullables should be null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* - For Irish coffee: cream and whiskey must not be null, all other nullables should be null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;record&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrder&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nonnull&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DrinkType&lt;/span&gt;&lt;span&gt; drinkType&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nullable&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Milk&lt;/span&gt;&lt;span&gt; milk&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nullable&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Gelato&lt;/span&gt;&lt;span&gt; gelato&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nullable&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cream&lt;/span&gt;&lt;span&gt; cream&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nullable&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Whiskey&lt;/span&gt;&lt;span&gt; whiskey&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This comment is very thorough, now anyone who forgets about the invariants has a complete reference. Is this good enough?&lt;/p&gt;
&lt;h2 id=&quot;getting-in-the-flow&quot;&gt;Getting in the Flow&lt;/h2&gt;
&lt;p&gt;How do we actually use &lt;code&gt;CoffeeOrder&lt;/code&gt;? We are in the business of taking orders and operating robots. So here&apos;s the pipeline that we&apos;ll be implementing:
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;1330&quot; height=&quot;315&quot; src=&quot;https://blog.daniel-beskin.com/_astro/flow.lR7fl6yt_ncxI4.webp&quot;&gt;&lt;/p&gt;
&lt;p&gt;In this flow we take in a new &lt;code&gt;CoffeeOrderDTO&lt;/code&gt;, which represents the raw data we got from the user. It&apos;s defined like so:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;record&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrderDTO&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nullable&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DrinkType&lt;/span&gt;&lt;span&gt; drinkType&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nullable&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Milk&lt;/span&gt;&lt;span&gt; milk&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nullable&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Gelato&lt;/span&gt;&lt;span&gt; gelato&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nullable&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cream&lt;/span&gt;&lt;span&gt; cream&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nullable&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Whiskey&lt;/span&gt;&lt;span&gt; whiskey&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Basically the same thing as &lt;code&gt;CoffeeOrder&lt;/code&gt;, but now everything is nullable and we don&apos;t assume any invariants about it. We have to validate &lt;code&gt;CoffeeOrderDTO&lt;/code&gt; before we can convert it into a proper &lt;code&gt;CoffeeOrder&lt;/code&gt;. This is where &lt;code&gt;OrderProcessor&lt;/code&gt; comes into play. The &lt;code&gt;process&lt;/code&gt; method takes in a &lt;code&gt;CoffeeOrderDTO&lt;/code&gt; and converts it into a &lt;code&gt;CoffeeOrder&lt;/code&gt;. Here we&apos;ll be careful to maintain all the &lt;code&gt;CoffeeOrder&lt;/code&gt; invariants that we care about. With a &lt;code&gt;CoffeeOrder&lt;/code&gt; ready we send it to &lt;code&gt;CoffeeMachine&lt;/code&gt;, which will produce the instructions to the robot according to the &lt;code&gt;CoffeeOrder&lt;/code&gt; we have.&lt;/p&gt;
&lt;p&gt;To keep things contained, we&apos;ll only focus on a single drink. And so we&apos;ll just look at the code that is responsible for Irish coffee.&lt;/p&gt;
&lt;p&gt;Let&apos;s start with &lt;code&gt;OrderProcessor&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;OrderProcessor&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;process&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Nonnull&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrderDTO&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;drinkType&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;drinkType&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;DrinkType&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;CAPPUCCINO&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;drinkType&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;switch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;drinkType&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; IRISH_COFFEE &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;processIrishCoffee&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we take the input &lt;code&gt;CoffeeOrderDTO&lt;/code&gt; (1). As a first step we are trying to determine the &lt;code&gt;DrinkType&lt;/code&gt;. Since it cannot be missing, we provide a default value (cappuccino) in case it&apos;s &lt;code&gt;null&lt;/code&gt; (2). We then switch on the &lt;code&gt;DrinkType&lt;/code&gt; (3). In case it&apos;s &lt;code&gt;IRISH_COFFEE&lt;/code&gt; we process the order using the &lt;code&gt;processIrishCoffee&lt;/code&gt; method (4). Which is defined as follows:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;processIrishCoffee&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;CoffeeOrderDTO&lt;/span&gt;&lt;span&gt; order&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrder&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;DrinkType&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;IRISH_COFFEE&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;cream&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cream&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;HALF_AND_HALF&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;cream&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Whiskey&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;JAMESON&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We finally create a &lt;code&gt;CoffeeOrder&lt;/code&gt; (1) of type &lt;code&gt;IRISH_COFFEE&lt;/code&gt; (2). At this point we have to populate its fields according to the invariants that we defined for Irish coffee. So we extract the &lt;code&gt;Cream&lt;/code&gt; value, and provide a fallback in case it&apos;s missing (3). And do the same for the &lt;code&gt;Whiskey&lt;/code&gt; value (4). The rest is not relevant for Irish coffee, so we set it to &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With this code in place, we kept all the invariants that are dictated by &lt;code&gt;CoffeeOrder&lt;/code&gt;. Illegal states are nowhere in sight. We could even write a unit test to make sure this is so.&lt;/p&gt;
&lt;p&gt;The next step is to program the &lt;code&gt;CoffeeMachine&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Suppose we have a method that tells the robot to prepare the Irish coffee. Given the user&apos;s choice of cream and whiskey it instructs the robot to prepare Irish coffee:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prepareIrishCoffee&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nonnull&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cream&lt;/span&gt;&lt;span&gt; cream&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nonnull&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Whiskey&lt;/span&gt;&lt;span&gt; whiskey&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because Irish coffee makes no sense without cream and whiskey, the method requires the &lt;code&gt;cream&lt;/code&gt; and &lt;code&gt;whiskey&lt;/code&gt; arguments to be non-null. This reflects the rationale for the invariants in &lt;code&gt;CoffeeOrder&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We&apos;ll use the &lt;code&gt;prepareIrishCoffee&lt;/code&gt; method in &lt;code&gt;CoffeeMachine.prepare&lt;/code&gt; when converting a &lt;code&gt;CoffeeOrder&lt;/code&gt; into instructions for the robot:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeMachine&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prepare&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;CoffeeOrder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;switch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;drinkType&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; IRISH_COFFEE &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;prepareIrishCoffee&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;cream&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt;&lt;span&gt;())&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We switch on the order&apos;s &lt;code&gt;DrinkType&lt;/code&gt; (1) and in the case where the drink type is &lt;code&gt;IRISH_COFFEE&lt;/code&gt; (2) we extract the &lt;code&gt;cream&lt;/code&gt; and &lt;code&gt;whiskey&lt;/code&gt; values and pass them on (3). That&apos;s okay, right? We have all those invariants in place (here&apos;s to good documentation) because it makes no sense for those values to be empty at this stage. &lt;code&gt;OrderProcessor&lt;/code&gt; made sure of this.&lt;/p&gt;
&lt;p&gt;Unfortunately, this doesn&apos;t compile:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;CoffeeMachine.java:23: error: [NullAway] passing @Nullable parameter &apos;order.cream()&apos; where @NonNull is required&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order.cream(),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;                       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;^&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;CoffeeMachine.java:24: error: [NullAway] passing @Nullable parameter &apos;order.whiskey()&apos; where @NonNull is required&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order.whiskey());&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;                         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;^&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;2 errors&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ah, we just specified that &lt;code&gt;prepareIrishCoffee&lt;/code&gt; takes non-null values, and despite our invariants, nothing guarantees to the compiler that the values are &lt;em&gt;actually&lt;/em&gt; not null. But &lt;em&gt;we know&lt;/em&gt; that everything is fine here. Let&apos;s insist on that:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeMachine&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prepare&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;CoffeeOrder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;switch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;drinkType&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; IRISH_COFFEE &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; This is SAFE // 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; Irish coffee ALWAYS has cream and whiskey&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;cream&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;IllegalStateException&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;No cream&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;IllegalStateException&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;No whiskey&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;prepareIrishCoffee&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;cream&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt;&lt;span&gt;())&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We added a defensive &lt;code&gt;null&lt;/code&gt; check to satisfy the compiler (2), and we throw an exception if the invariant is broken. But &lt;em&gt;we know&lt;/em&gt; it&apos;s safe, and just to be extra careful we leave an emphatic comment for future reference about just how impossible it is for the values to be missing here (1). With the &lt;code&gt;null&lt;/code&gt; check in place this code now compiles.&lt;/p&gt;
&lt;p&gt;Our pipeline is now complete, all invariants are kept. We can even run it to make sure it works:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;processor&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;OrderProcessor&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;coffeeMaker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeMachine&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dto1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrderDTO&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;DrinkType&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;IRISH_COFFEE&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cream&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;HEAVY&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Whiskey&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;TULLAMORE&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dto2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrderDTO&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;DrinkType&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;IRISH_COFFEE&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cream&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;HEAVY&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;order1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;processor&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;process&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dto1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;order2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;processor&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;process&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dto2&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;out&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;println&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;order1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;out&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;println&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;order2&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 6&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;coffeeMaker&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;prepare&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;order1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;coffeeMaker&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;prepare&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;order2&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this simple script we initialize the different components of the flow (1). We then create a couple of &lt;code&gt;CoffeeOrderDTO&lt;/code&gt;s. The first is for Irish coffee, with all the relevant data present (2). The second is missing a whiskey value (3). Next we process both orders and generate &lt;code&gt;CoffeeOrder&lt;/code&gt; instances (4). We print those values so that we can see they are okay (5). Lastly, we prepare the orders (6).&lt;/p&gt;
&lt;p&gt;Running this produces the following output:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;CoffeeOrder[drinkType=IRISH_COFFEE, milk=null, gelato=null, cream=HEAVY, whiskey=TULLAMORE]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;CoffeeOrder[drinkType=IRISH_COFFEE, milk=null, gelato=null, cream=HEAVY, whiskey=JAMESON] // 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Making Irish coffee with: HEAVY cream and TULLAMORE whiskey&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Making Irish coffee with: HEAVY cream and JAMESON whiskey // 2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can see that the missing whiskey value in the second order was filled in with the default value (1), and the robot happily fulfilled the order (2).&lt;/p&gt;
&lt;p&gt;Everything is fine, no invariants were broken, no illegal states encountered.&lt;/p&gt;
&lt;p&gt;This is our baseline, now let&apos;s see how things break over time...&lt;/p&gt;
&lt;h2 id=&quot;ai-vs-illegal-state&quot;&gt;AI vs. Illegal State&lt;/h2&gt;
&lt;p&gt;Time passes by, everything works correctly, and you forget all about this code. But then, a new requirement comes in. Recall how we used a hardcoded &lt;code&gt;Whiskey.JAMESON&lt;/code&gt; value when the whiskey value is missing from the order. Well, we noticed that the whiskey stock in the coffee shop keeps fluctuating, and it&apos;s possible that Jameson is out of stock today. We no longer want to hardcode this value, instead we&apos;ll query a dedicated service to obtain the default value for today.&lt;/p&gt;
&lt;p&gt;For this we&apos;ll have a new service definition:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;WhiskeyService&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nullable&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Whiskey&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fetchTodayDefaultWhiskey&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&apos;s just a simple fetcher. Since things can go wrong with fetching, the output is nullable, let our clients deal with that. We want to use an instance of this service to fetch the value instead of the hardcoded value we currently have.&lt;/p&gt;
&lt;p&gt;Additionally, there&apos;s an implementation of &lt;code&gt;WhiskeyService&lt;/code&gt; called &lt;code&gt;WhiskeyServiceDefault&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;WhiskeyServiceDefault&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;WhiskeyService&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nullable&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Override&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Whiskey&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fetchTodayDefaultWhiskey&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rnd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Random&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;whiskies&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Whiskey&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;values&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; whiskies&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;rnd&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;nextInt&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;whiskies&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; whiskey&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For purposes of demonstration, it just returns a random value each time it&apos;s called. In (1) we obtain an array of all available whiskies. Then we choose a random whiskey from all the possible whiskey values (2), and return it (3).&lt;/p&gt;
&lt;p&gt;Since we&apos;re living in the future, we won&apos;t be implementing the new feature ourselves, manually, like some plebs. Instead, we&apos;ll use our handy AI agent&lt;sup&gt;&lt;a href=&quot;#user-content-fn-aiversion&quot; id=&quot;user-content-fnref-aiversion&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Here&apos;s the prompt for this task:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt; In @OrderProcessor.java we no longer want to hardcode the&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;default `Whiskey` value since we don&apos;t know whether it&apos;s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;available today or not. Use @WhiskeyService.java to inject&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;the value dynamically instead. In @Main.java use&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;@WhiskeyServiceDefault.java. Use `gradlew build` to verify&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;that everything compiles.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nothing fancy here. The AI performs the relevant edits, without breaking a sweat. Here&apos;s the summary of what it&apos;s done:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;● Done! I&apos;ve successfully:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;1. Modified OrderProcessor.java:25 to use&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;whiskeyService.fetchTodayDefaultWhiskey() instead of&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;hardcoded Whiskey.JAMESON&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;2. Added WhiskeyService dependency injection to&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;OrderProcessor constructor&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;3. Updated Main.java:5-6 to create WhiskeyServiceDefault&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;instance and inject it into OrderProcessor&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;4. Verified compilation with gradlew build&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The new code
&lt;sup&gt;&lt;a href=&quot;#user-content-fn-aibranch&quot; id=&quot;user-content-fnref-aibranch&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;
looks very reasonable, with all the modern bells and whistles of dependency injection, and whatnot. Here&apos;s the new code in &lt;code&gt;processIrishCoffee&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;processIrishCoffee&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;CoffeeOrderDTO&lt;/span&gt;&lt;span&gt; order&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrder&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;DrinkType&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;IRISH_COFFEE&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;cream&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cream&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;HALF_AND_HALF&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;cream&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;whiskeyService&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fetchTodayDefaultWhiskey&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As we can see in (1) the default whiskey is no longer hardcoded, we delegate to &lt;code&gt;WhiskeyService&lt;/code&gt; instead. Cool, this fulfills the requirements as we stated them.&lt;/p&gt;
&lt;p&gt;We can run the example code from before:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;CoffeeOrder[drinkType=IRISH_COFFEE, milk=null, gelato=null, cream=HEAVY, whiskey=TULLAMORE]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;CoffeeOrder[drinkType=IRISH_COFFEE, milk=null, gelato=null, cream=HEAVY, whiskey=BUSHMILLS] // 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Making Irish coffee with: HEAVY cream and TULLAMORE whiskey&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Making Irish coffee with: HEAVY cream and BUSHMILLS whiskey&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since the default whiskey is no longer hardcoded, every time we run the code we&apos;ll get a different whiskey value. This time it was &lt;code&gt;BUSHMILLS&lt;/code&gt; (1).&lt;/p&gt;
&lt;p&gt;... cue in ominous music...&lt;/p&gt;
&lt;p&gt;The AI fell for the trap I set for it. Can you see it?&lt;/p&gt;
&lt;p&gt;As it currently stands the code doesn&apos;t exhibit any bugs. But after this simple edit a bug is latent in the codebase. Neither the AI, nor the human, or even the test suite caught anything.&lt;/p&gt;
&lt;p&gt;We move on with our life, busying ourselves with other parts of the codebase. Until one day we get another feature request.&lt;/p&gt;
&lt;p&gt;It appears the stock of whiskeys fluctuates so rapidly that we still sometimes try to serve whiskey that&apos;s out of stock, even without hardcoding the value. To fix this in the &lt;code&gt;WhiskeyService&lt;/code&gt; implementation we are asked to check the current stock and avoid choosing a whiskey that is out of stock.&lt;/p&gt;
&lt;p&gt;To do this we have another service we can query:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;WhiskeyStockService&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; invariant: all Whiskey types are present in the resulting Map // 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nonnull&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Map&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Whiskey&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Integer&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetchTodayStock&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;WhiskeyStockService.fetchTodayStock&lt;/code&gt; gives us a snapshot of the current stock (2). There&apos;s also a helpful comment about an invariant here, which we&apos;ll trust blindly. Now we can use &lt;code&gt;WhiskeyStockService&lt;/code&gt; to implement the new requirement (for demonstration purposes &lt;code&gt;WhiskeyStockServiceDefault&lt;/code&gt; will return random values for the stock).&lt;/p&gt;
&lt;p&gt;Let&apos;s prompt the AI:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt; In @WhiskeyServiceDefault.java it&apos;s possible that we used a&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;whiskey that&apos;s not in stock today. Use&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;@WhiskeyStockService.java to only return the chosen whiskey&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;if it&apos;s in stock at the moment. In @Main.java use&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;@WhiskeyStockServiceDefault.java. Use `gradlew build` to&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;verify that everything compiles.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The AI chugs along. It stumbles for a moment because the compiler was not impressed by the invariant that was stated in the comment in &lt;code&gt;WhiskeyStockService&lt;/code&gt;. But finally we get back the results:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;● Done! I&apos;ve successfully updated the code to check whiskey&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;stock availability:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;1. WhiskeyServiceDefault.java now uses WhiskeyStockService&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;to validate stock before returning a whiskey&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;2. Main.java now creates and uses WhiskeyStockServiceDefault&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;3. The build compiles successfully with gradlew build&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;The implementation now only returns whiskeys that are in&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;stock (stock level &gt; 0), and returns `null` if no whiskeys&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;are available.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The new &lt;code&gt;WhiskeyServiceDefault&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nullable&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Override&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Whiskey&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fetchTodayDefaultWhiskey&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rnd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Random&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;whiskies&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Whiskey&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;values&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stock&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stockService&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fetchTodayStock&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;attempts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; attempts &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;whiskies&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; attempts&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; whiskies&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;rnd&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;nextInt&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;whiskies&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stockLevel&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stock&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;stockLevel &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; stockLevel &lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; whiskey&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&apos;s ignore the question of whether this fulfills the requirements; vague human language sure tends to produce vague results. What&apos;s important is that this code now exhibits a bug. Despite the &lt;code&gt;@Nullable&lt;/code&gt; annotation, the original implementation of &lt;code&gt;fetchTodayDefaultWhiskey&lt;/code&gt; would never return a &lt;code&gt;null&lt;/code&gt; value in practice. With the new implementation this is no longer the case. The code can, and depending on today&apos;s stock, will eventually return &lt;code&gt;null&lt;/code&gt; (1).&lt;/p&gt;
&lt;p&gt;Why is this a problem? Recall that after our first round of edits we had this code:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrder&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;DrinkType&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;IRISH_COFFEE&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;cream&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cream&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;HALF_AND_HALF&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;cream&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;whiskeyService&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fetchTodayDefaultWhiskey&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We blindly take whatever &lt;code&gt;fetchTodayDefaultWhiskey&lt;/code&gt; returns and pass it along. The &lt;code&gt;null&lt;/code&gt; checking that the compiler is doing for us doesn&apos;t help us, since &lt;code&gt;whiskey&lt;/code&gt; is a nullable field. It shouldn&apos;t be &lt;code&gt;null&lt;/code&gt; only in specific circumstances (&lt;code&gt;DrinkType == IRISH_COFFEE&lt;/code&gt;) which the compiler is incapable of checking.&lt;/p&gt;
&lt;p&gt;For bonus points, this bug in intermittent, it only happens in specific circumstances, good luck debugging it when it hits production.&lt;/p&gt;
&lt;p&gt;If we run our test script, most of the times everything will be fine:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;CoffeeOrder[drinkType=IRISH_COFFEE, milk=null, gelato=null, cream=HEAVY, whiskey=TULLAMORE]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;CoffeeOrder[drinkType=IRISH_COFFEE, milk=null, gelato=null, cream=HEAVY, whiskey=BUSHMILLS]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Making Irish coffee with: HEAVY cream and TULLAMORE whiskey&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Making Irish coffee with: HEAVY cream and BUSHMILLS whiskey&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But some times, when we hit the right conditions (all chosen whiskeys are out of stock), we get an exception:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;CoffeeOrder[drinkType=IRISH_COFFEE, milk=null, gelato=null, cream=HEAVY, whiskey=TULLAMORE]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;CoffeeOrder[drinkType=IRISH_COFFEE, milk=null, gelato=null, cream=HEAVY, whiskey=null] // 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Making Irish coffee with: HEAVY cream and TULLAMORE whiskey&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Exception in thread &quot;main&quot; java.lang.IllegalStateException: No whiskey&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;at CoffeeMachine.prepare(CoffeeMachine.java:20)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;at Main.main(Main.java:23)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can see in (1) that we managed to get &lt;code&gt;null&lt;/code&gt; for the &lt;code&gt;whiskey&lt;/code&gt; field, and so the &lt;code&gt;null&lt;/code&gt; checks we were forced to place in &lt;code&gt;CoffeeMachine&lt;/code&gt; throw an exception.&lt;/p&gt;
&lt;p&gt;Remember that emphatic comment we had above?&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; This is SAFE&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; Irish coffee ALWAYS has cream and whiskey&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So much for the efficacy of comments...&lt;/p&gt;
&lt;h2 id=&quot;the-root-cause&quot;&gt;The Root Cause&lt;/h2&gt;
&lt;p&gt;What went wrong? The root cause of this is that we had an important invariant that was not enforced at compile-time. It was only encoded as textual knowledge (comments), not anything that is mechanically enforceable. We made an illegal state representable in our system, and given enough code and time, we eventually managed to hit it.&lt;/p&gt;
&lt;p&gt;It&apos;s no wonder that simple humans fall for such things all the time. The problem is that current AIs are prone to falling for this just the same. Both humans and AIs use a &lt;em&gt;local&lt;/em&gt; context of some random size when writing code. And whether or not they will notice this or that &lt;em&gt;global&lt;/em&gt; invariant being violated is given up to chance&lt;sup&gt;&lt;a href=&quot;#user-content-fn-randomedits&quot; id=&quot;user-content-fnref-randomedits&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;&quot;But&quot;, you might object, &quot;I can cover the invariant with tests, and those tests will keep the AI in check&quot;. Alas, that&apos;s not good enough either. You can definitely cover all &lt;em&gt;known&lt;/em&gt; relevant code paths with tests. But what about code paths that do not yet exist? How do you know what future code path you will create and how it interacts with the invariants you&apos;ve set?&lt;/p&gt;
&lt;p&gt;And to head off the obvious objection that &lt;em&gt;your&lt;/em&gt; AI can handle this trap and not fall for it. Sure, it&apos;s a tiny codebase, with only a few files. I was even nice enough to spell out the invariant very explicitly in a comment&lt;sup&gt;&lt;a href=&quot;#user-content-fn-rare&quot; id=&quot;user-content-fnref-rare&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;, in a way that an AI can definitely pick up. But this will only happen if the AI &quot;sees&quot; that comment. Given a sufficiently large codebase, with a sufficiently widespread invariant, &lt;em&gt;your&lt;/em&gt; AI is bound to miss it as well.&lt;/p&gt;
&lt;p&gt;The same applies for people that are more skilled at prompting than I am. Maybe you can prompt better, but nothing will save your prompt if you&apos;re not even aware of the danger you are trying to circumvent.&lt;/p&gt;
&lt;p&gt;Whatever your objections are to the details of this conceptual example, you cannot change the fact that in general you will not be able to consistently enforce global invariants with tools that only apply to a limited local scope. Humans, current AIs, and unit tests, all fall under the category of &quot;local&quot; tools.&lt;/p&gt;
&lt;p&gt;If an illegal state is representable in your system, you and your AI will eventually hit it. And those odds multiply as you run more and more agents generating ever increasing amounts of code.&lt;/p&gt;
&lt;p&gt;The best way to not fall for it is to make the illegal state unrepresentable, and by so making the illegal AI edits we just seen unrepresentable as well.&lt;/p&gt;
&lt;h2 id=&quot;the-unrepresentable&quot;&gt;The Unrepresentable&lt;/h2&gt;
&lt;p&gt;In a parallel universe, where developers know what&apos;s good for them and their sanity, every time they recognize an illegal state of the system, they make it unrepresentable. This is not always easy, and depending on what&apos;s at stake might not even be worth it. But for our example, the fix is fairly straightforward.&lt;/p&gt;
&lt;p&gt;The problem starts with &lt;code&gt;CoffeeOrder&lt;/code&gt; which is capable of representing illegal values. If we remodel &lt;code&gt;CoffeeOrder&lt;/code&gt; to only allow legal combinations, the rest will follow. One of the best techniques to do that is to use a sum type. In Java this means using a sealed interface, where each admitted class represents one (legal) combination of values.&lt;/p&gt;
&lt;p&gt;Let&apos;s rewrite &lt;code&gt;CoffeeOrder&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-illegalbranch&quot; id=&quot;user-content-fnref-illegalbranch&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;8&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sealed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;record&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cappuccino&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Nonnull&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Milk&lt;/span&gt;&lt;span&gt; milk&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;record&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Espresso&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;record&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Latte&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Nonnull&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Milk&lt;/span&gt;&lt;span&gt; milk&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;record&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Affogato&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Nonnull&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Gelato&lt;/span&gt;&lt;span&gt; gelato&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;record&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;IrishCoffee&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 6&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nonnull&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cream&lt;/span&gt;&lt;span&gt; cream&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Nonnull&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Whiskey&lt;/span&gt;&lt;span&gt; whiskey&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;CoffeeOrder&lt;/code&gt; is now a &lt;code&gt;sealed interface&lt;/code&gt; (1), with all the admitted classes nested in it. Each implementation of &lt;code&gt;CoffeeOrder&lt;/code&gt; is one possible drink that we currently allow. Accordingly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Cappuccino&lt;/code&gt; (2) has one &lt;code&gt;Milk&lt;/code&gt; field, which is now enforced to be &lt;code&gt;@Nonnull&lt;/code&gt;, as per the invariants we described before&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Espresso&lt;/code&gt; (3) has no fields, so we no longer need to set anything to be nullable just for it&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Latte&lt;/code&gt; (4) has a &lt;code&gt;@Nonnull&lt;/code&gt; &lt;code&gt;Milk&lt;/code&gt; as well, and we don&apos;t have any ambiguity with &lt;code&gt;Cappuccino&lt;/code&gt; since it&apos;s a separate subtype&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Affogato&lt;/code&gt; (5) has one mandatory &lt;code&gt;Gelato&lt;/code&gt; field, which by virtue of being a separate class doesn&apos;t affect anything else&lt;/li&gt;
&lt;li&gt;Lastly, &lt;code&gt;IrishCoffee&lt;/code&gt;, our nemesis, explicitly declares its requirements with a pair of &lt;code&gt;Cream&lt;/code&gt; and &lt;code&gt;Whiskey&lt;/code&gt; mandatory fields&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;CoffeeOrder&lt;/code&gt; datatype perfectly captures all the requirements that we stated before. Better yet, it does that at the type-level, where the compiler can enforce it. There&apos;s no longer a need for a comment nobody&apos;s going to read. &lt;code&gt;CoffeeOrder&lt;/code&gt; &lt;em&gt;is&lt;/em&gt; a precise specification of what is allowed in the system.&lt;/p&gt;
&lt;p&gt;Whereas the original &lt;code&gt;CoffeeOrder&lt;/code&gt; was &quot;too big&quot;, allowing for a mismatch between its &lt;code&gt;DrinkType&lt;/code&gt; and the nullable fields. The new &lt;code&gt;CoffeeOrder&lt;/code&gt; subsumes &lt;code&gt;DrinkType&lt;/code&gt; into its subclasses, perfectly aligning the required data with the drink being ordered.&lt;/p&gt;
&lt;p&gt;Now we have to adapt the code to the new datatype, but this time we no longer have to worry about invariants, the compiler will nudge us in the right direction at every step.&lt;/p&gt;
&lt;p&gt;We&apos;ll start with &lt;code&gt;OrderProcessor&lt;/code&gt;, we only need to change &lt;code&gt;processIrishCoffee&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;processIrishCoffee&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;CoffeeOrderDTO&lt;/span&gt;&lt;span&gt; order&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CoffeeOrder&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;IrishCoffee&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;cream&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cream&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;HALF_AND_HALF&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;cream&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Whiskey&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;JAMESON&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt;&lt;span&gt;())&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code looks pretty much the same, but now we initialize the specific subtype of &lt;code&gt;CoffeeOrder&lt;/code&gt; that we need here, &lt;code&gt;IrishCoffee&lt;/code&gt; (1). The &lt;code&gt;cream&lt;/code&gt; and &lt;code&gt;whiskey&lt;/code&gt; fields are initialized the same as in our baseline implementation (2). Unlike before we no longer have to provide the dummy &lt;code&gt;null&lt;/code&gt; values for the fields that are irrelevant in this case. Because there aren&apos;t any, &lt;code&gt;IrishCoffee&lt;/code&gt; is very precise.&lt;/p&gt;
&lt;p&gt;What&apos;s invisible in this code is the fact that the arguments to &lt;code&gt;IrishCoffee&lt;/code&gt; are not allowed to be &lt;code&gt;null&lt;/code&gt; like they were before. We&apos;ll later see how this saves our code from bugs.&lt;/p&gt;
&lt;p&gt;Next we&apos;ll adapt &lt;code&gt;CoffeeMachine&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prepare&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;CoffeeOrder&lt;/span&gt;&lt;span&gt; order&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;switch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrder&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;IrishCoffee&lt;/span&gt;&lt;span&gt; irishCoffee &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;prepareIrishCoffee&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;irishCoffee&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;cream&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;irishCoffee&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code is the same as our very first attempt to write it, but this time the compiler won&apos;t complain. The fields of &lt;code&gt;IrishCoffee&lt;/code&gt; cannot be null, and we can safely pass them to &lt;code&gt;prepareIrishCoffee&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With these few small changes we managed to encode the invariants we care about globally, in a way that can be enforced mechanically and deterministically by the compiler. Will this code withstand the pressure of humans and AIs trying to break it?&lt;/p&gt;
&lt;h2 id=&quot;ai-vs-the-unrepresentable&quot;&gt;AI vs. The Unrepresentable&lt;/h2&gt;
&lt;p&gt;Let&apos;s repeat the same prompt that we tried before and see how the AI manages now. Recall the prompt:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt; In @OrderProcessor.java we no longer want to hardcode the&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;default `Whiskey` value since we don&apos;t know whether it&apos;s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;available today or not. Use @WhiskeyService.java to inject&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;the value dynamically instead. In Main.java use&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;@WhiskeyServiceDefault.java. Use `gradlew build` to verify&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;that everything compiles.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The AI happily chugs along trying to make the same modification it did before:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;processIrishCoffee&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;CoffeeOrderDTO&lt;/span&gt;&lt;span&gt; order&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CoffeeOrder&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;IrishCoffee&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;cream&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cream&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;HALF_AND_HALF&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;cream&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Whiskey&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;JAMESON&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt;&lt;span&gt;())&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;whiskeyService&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fetchTodayDefaultWhiskey&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But this fails to compile. The arguments to &lt;code&gt;IrishCoffee&lt;/code&gt; cannot be null, but &lt;code&gt;fetchTodayDefaultWhiskey&lt;/code&gt; is marked &lt;code&gt;Nullable&lt;/code&gt;. The AI reacts with:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;● I need to handle the null case from the WhiskeyService.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;The issue is that fetchTodayDefaultWhiskey() can return&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;null, but the CoffeeOrder.IrishCoffee constructor expects a&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;non-null whiskey.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which is great, and exactly the sort of process that making illegal states unrepresentable should trigger. The AI then suggests the following modification:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;processIrishCoffee&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;CoffeeOrderDTO&lt;/span&gt;&lt;span&gt; order&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;defaultWhiskey&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;whiskeyService&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fetchTodayDefaultWhiskey&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;defaultWhiskey &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; defaultWhiskey &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Whiskey&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;JAMESON&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CoffeeOrder&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;IrishCoffee&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;cream&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cream&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;HALF_AND_HALF&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;cream&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; defaultWhiskey &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt;&lt;span&gt;())&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;whiskey)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In (1) there is now a fallback in case &lt;code&gt;WhiskeyService&lt;/code&gt; produced a &lt;code&gt;null&lt;/code&gt;, which keeps the original hardcoded value.&lt;/p&gt;
&lt;p&gt;Here the timeline splits. If we aren&apos;t carefully reviewing everything the AI is doing, which is getting more and more likely as people offload more code to agents, we might accept this solution. Despite the fact that it doesn&apos;t actually fulfill our requirements for not hardcoding values. Such is life. But, crucially, we didn&apos;t break any invariants.&lt;/p&gt;
&lt;p&gt;On another timeline, we might&apos;ve noticed that the requirements weren&apos;t fulfilled (or the prompt was more insistent about it), in which case we reject this edit. Likely or not, I&apos;ll continue on this timeline, just for the sake of seeing where this might lead:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt; Never use a hardcoded value&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which produces the following code:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CoffeeOrder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;processIrishCoffee&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;CoffeeOrderDTO&lt;/span&gt;&lt;span&gt; order&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;whiskey &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;whiskey &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;whiskeyService&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fetchTodayDefaultWhiskey&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;whiskey &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;IllegalStateException&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;No whiskey available today&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CoffeeOrder&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;IrishCoffee&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;cream&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cream&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;HALF_AND_HALF&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;cream&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;whiskey&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This solution throws an exception when the fetched whiskey is &lt;code&gt;null&lt;/code&gt; (1). Seeing how we went in this direction due to a runtime exception being thrown where we didn&apos;t expect it, we&apos;ll reject this solution as well&lt;sup&gt;&lt;a href=&quot;#user-content-fn-checkedexception&quot; id=&quot;user-content-fnref-checkedexception&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;9&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt; No exceptions&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To which we get this response:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;● I understand. Let me think about this differently. Since&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;the WhiskeyService interface is @Nullable but we need a&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;non-null value, and you don&apos;t want hardcoded values or&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;exceptions, perhaps the solution is to modify the&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;WhiskeyServiceDefault to never return null, or change the&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;interface contract.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Looking at the current WhiskeyServiceDefault implementation,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;it actually never returns null - it always returns a random&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;whiskey. Should I remove the @Nullable annotation from the&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;interface to reflect this reality?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nope:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt; It&apos;s impossible to guarantee that `fetcTodayDefaultWhiskey`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;is never `null`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the AI goes on a tangent, trying to swap the order for a hardcoded cappuccino if the whiskey is missing. This might be acceptable to some, but not in this house:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt; Changing the drink silently is not user friendly&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;● You&apos;re right. If whiskey isn&apos;t available, we shouldn&apos;t&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;silently change the drink type. Looking at this problem, I&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;think we need to modify the CoffeeOrder.IrishCoffee to&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;accept a @Nullable whiskey parameter, since that&apos;s the&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;reality of the business logic - sometimes whiskey might not&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;be available.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But no:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt; It makes no sense to have Irish coffee without whiskey&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;● You&apos;re absolutely right. An Irish coffee without whiskey&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;isn&apos;t an Irish coffee. Let me look at this from a different&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;angle - perhaps we need to handle this at the order&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;processing level by not allowing Irish coffee orders when no&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;whiskey is available.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Looking at the current flow, when someone orders an Irish&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;coffee but no whiskey is specified in the order AND the&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;service returns null (no whiskey available today), we have&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;an impossible state to represent. // 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Maybe the solution is to return an Optional&amp;#x3C;CoffeeOrder&gt; or&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;similar from the process method, or have a different&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;approach to handle unavailable ingredients. What would you&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;prefer as the approach to handle this case where Irish&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;coffee is ordered but no whiskey is available?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;</content:encoded><author>Daniel Beskin</author></item><item><title>First Make It Correct</title><link>https://blog.daniel-beskin.com/2025-06-10-first-make-it-correct</link><guid isPermaLink="true">https://blog.daniel-beskin.com/2025-06-10-first-make-it-correct</guid><description>Way back in December, in the spirit of the times (it seems that everyone was either solving Advent of Code or looking for a job), like an elephant in a china shop, I found myself solving a…</description><pubDate>Tue, 10 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://blog.daniel-beskin.com/social-cards/2025-06-10-first-make-it-correct.png&quot; alt=&quot;Cover image for First Make It Correct&quot; style=&quot;width: 100%; height: auto; margin-bottom: 2rem;&quot;&gt;&lt;p&gt;Way back in December, in the spirit of the times (it seems that everyone was either solving Advent of Code or looking for a job), like an elephant in a china shop, I found myself solving a leetcode-style problem.&lt;/p&gt;
&lt;p&gt;Before I say anything else&lt;sup&gt;&lt;a href=&quot;#user-content-fn-attorney&quot; id=&quot;user-content-fnref-attorney&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, I would like to preface that I hate leetcode problems with a passion, and not only because I suck at them. The software industry needs to rise as one and banish them from any interviewing process in existence, and leave them be for the masochists that actually enjoy solving them.&lt;/p&gt;
&lt;p&gt;Having said that, for the right level of difficulty&lt;sup&gt;&lt;a href=&quot;#user-content-fn-nottoodifficult&quot; id=&quot;user-content-fnref-nottoodifficult&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; I do get easily &lt;a href=&quot;https://xkcd.com/356/&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;nerd sniped&lt;/a&gt; by leetcode problems. And that&apos;s how I found myself spending way too much time solving a simple problem. Lest this time be completely wasted, I tried to conjure up a lesson for &quot;real code&quot; that can be learned from this sordid ordeal.&lt;/p&gt;
&lt;p&gt;And so, paraphrasing a &lt;a href=&quot;https://wiki.c2.com/?MakeItWorkMakeItRightMakeItFast&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;famous saying&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;First make it correct, then make it anything you want.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let me illustrate this point with a leetcode problem. This is going to be a wild ride from recursive solutions to procedural loops, all with a touch of property-based testing. Grab yourselves a cup of liquid and settle in for a (very) long read.&lt;/p&gt;
&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;/h2&gt;
&lt;p&gt;Here&apos;s the problem I was trying to solve:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Given a two dimensional board compute a list of coordinates that form a clockwise spiral from the top-left corner to the center.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For example, given a board of dimensions 4x3, the spiral&apos;s path looks like this&lt;sup&gt;&lt;a href=&quot;#user-content-fn-toomuchtime&quot; id=&quot;user-content-fnref-toomuchtime&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| -[1]→  | -[2]→  | -[3]→  |  [4]↓  |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| -[10]→ | -[11]→ |  [12]  |  [5]↓  |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| ↑[9]   | ←[8]-  | ←[7]-  | ←[6]-  |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the list of coordinates is (using 0-based indexing from left-to-right, top-to-bottom):&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before reading on, spend some time and think about how you would approach solving this&lt;sup&gt;&lt;a href=&quot;#user-content-fn-ordont&quot; id=&quot;user-content-fnref-ordont&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h2 id=&quot;obviously-correct-solution&quot;&gt;Obviously Correct Solution&lt;/h2&gt;
&lt;p&gt;As stated above, the first step when approaching any problem is to write a correct solution. I&apos;d even go further, ideally, I would start with an &quot;obviously correct&quot; solution. A solution that so closely follows the structure of the problem that you can just see it works from reading it. No nasty edge cases or convoluted logic. It&apos;s difficult to precisely define what &quot;obviously correct&quot; means, this is yet another one of those cases of &quot;you know it when you see it&quot;.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;500&quot; height=&quot;642&quot; src=&quot;https://blog.daniel-beskin.com/_astro/i-dont-always.B8sSwBFG_Z1LyGhE.webp&quot;&gt;
&lt;p&gt;Having scratched my head some, I got myself a working solution. But it wasn&apos;t an &quot;obviously correct&quot; solution. It had inconvenient edge cases and just wasn&apos;t obvious. Luckily, the friend who nerd sniped me&lt;sup&gt;&lt;a href=&quot;#user-content-fn-stillfriend&quot; id=&quot;user-content-fnref-stillfriend&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; gave me a better insight into a possible solution to the problem.&lt;/p&gt;
&lt;p&gt;Generating the correct coordinates is like peeling an orange with a knife. You shave off a row, rotate, and repeat until you&apos;re done.&lt;/p&gt;
&lt;p&gt;Let&apos;s see this with a concrete example. Suppose you fill the board with the coordinates of each cell:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,0) | (1,0) | (2,0) | (3,0) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,1) | (1,1) | (2,1) | (3,1) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,2) | (1,2) | (2,2) | (3,2) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can peel the top row and add it to the solution:&lt;/p&gt;
&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;(0,0), (1,0), (2,0), (3,0)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are then left with a smaller board:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,1) | (1,1) | (2,1) | (3,1) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,2) | (1,2) | (2,2) | (3,2) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which we rotate counter-clockwise:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (3,1) | (3,2) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (2,1) | (2,2) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (1,1) | (1,2) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,1) | (0,2) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Peel the top row again, and add it to the solution:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;(3,1), (3,2)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the remaining board:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (2,1) | (2,2) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (1,1) | (1,2) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,1) | (0,2) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which we can rotate again, and repeat the process until the board reaches size 0. This solves the problem neatly, nicely following the symmetry inherent to the problem, and leading us towards what might turn out to be an obviously correct solution. All that&apos;s left is to code it up.&lt;/p&gt;
&lt;p&gt;The solution just described is inherently recursive. We repeat the same procedure over and over again on an input that shrinks on every step, until we reach a base case. Since functional languages are very well adapted to recursion, we&apos;ll be using a functional language for this post. Specifically the Scala programming language, as it lets us do the whole functional thing with ease, but will also let us go procedural later on.&lt;/p&gt;
&lt;p&gt;First let&apos;s lay down some basic helpers. Since we don&apos;t want to get confused by the different pieces of data we&apos;ll need. Let&apos;s define some simple types:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;w&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;h&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;y&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Dims&lt;/code&gt; represents the dimensions of a board, with a width &lt;code&gt;w&lt;/code&gt; and a height &lt;code&gt;h&lt;/code&gt;. &lt;code&gt;Point&lt;/code&gt; represents a two-dimensional point located at the &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; coordinates.&lt;/p&gt;
&lt;p&gt;With this out of the way and without further ado, here&apos;s the orange peeling solution in code&lt;sup&gt;&lt;a href=&quot;#user-content-fn-github&quot; id=&quot;user-content-fnref-github&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;traceSpiral&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;peel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;buildCoordinates&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;peel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;board&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;]])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;board &lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Nil&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Nil&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; topRow &lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt; otherRows &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 6&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;topRow &lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;&lt;span&gt; peel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;rotateCCW&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;otherRows&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 7&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&apos;s go through it step by step:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The entry point is &lt;code&gt;traceSpiral&lt;/code&gt; (1), it takes in the dimensions of the board and produces a list of points&lt;/li&gt;
&lt;li&gt;We then call a utility function &lt;code&gt;buildCoordinates&lt;/code&gt; (2) that populates a list of lists with the coordinates of each board cell&lt;/li&gt;
&lt;li&gt;We pass that along to the recursive &lt;code&gt;peel&lt;/code&gt; function&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;peel&lt;/code&gt; function (3) operates on the board points producing a single list of points&lt;/li&gt;
&lt;li&gt;We do this by pattern matching on the current board (4)&lt;/li&gt;
&lt;li&gt;If the board has size 0 (i.e., the empty list) we reached the base case and return an empty list (5)&lt;/li&gt;
&lt;li&gt;If the board is non-empty, we extract its top row and the rest of the rows (6)&lt;/li&gt;
&lt;li&gt;We return the results by prepending the top row we just extracted to the result of calling &lt;code&gt;peel&lt;/code&gt; again with the counter-clockwise rotated leftover board (7)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This closely follows the algorithm we just described, and has very little extraneous detail. Pattern matching makes this safe and edge case free. You can check out the definitions of &lt;code&gt;buildCoordinates&lt;/code&gt; and &lt;code&gt;rotateCCW&lt;/code&gt; in the &lt;a href=&quot;https://github.com/ncreep/first-make-it-correct-blog/blob/master/ListUtil.scala&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;repo&lt;/a&gt;. But since they rely on high-level list functions like &lt;code&gt;tabulate&lt;/code&gt; and &lt;code&gt;transpose&lt;/code&gt; they too are edge case free, and are very easy to verify with a simple unit test&lt;sup&gt;&lt;a href=&quot;#user-content-fn-firstwrong&quot; id=&quot;user-content-fnref-firstwrong&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;And so, this is as close to an obviously correct solution as I&apos;ll ever get&lt;sup&gt;&lt;a href=&quot;#user-content-fn-othersolutions&quot; id=&quot;user-content-fnref-othersolutions&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;8&lt;/a&gt;&lt;/sup&gt;. But is it actually correct?&lt;/p&gt;
&lt;p&gt;I mean, I can handwave obvious correctness as much as I want, but without some kind of more concrete proof, that&apos;s all it is, handwaving. I won&apos;t try to make an actual proof in the mathematical sense, but I can at least provide some tests.&lt;/p&gt;
&lt;p&gt;Here&apos;s an easy one:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Traces the correct spiral&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;w &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;, h &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;points&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; traceSpiral&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;expected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;assertEquals&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;points, expected&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This test codifies the example we used above. And if we run it our solution passes with flying colors.&lt;/p&gt;
&lt;p&gt;&quot;But what about edge cases&quot;, you may wonder. And I shall answer that life&apos;s too short to look for edge cases manually. Instead we can employ &lt;a href=&quot;https://en.wikipedia.org/wiki/QuickCheck&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;property-based&lt;/a&gt; testing to search for edge cases for us.&lt;/p&gt;
&lt;p&gt;For that we need to specify some properties that should hold for any valid solution of the spiral problem. Having spent a few minutes thinking about it, here are some properties I came up with that can be used as sanity checks. For all possible board dimensions it should hold that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The points of a solution must be within the bounds of the board&lt;/li&gt;
&lt;li&gt;The number of points in a solution should be the same as the size of the area of the board&lt;/li&gt;
&lt;li&gt;There should be no duplicate points in a solution&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&apos;s make this specification executable using property-based testing&lt;sup&gt;&lt;a href=&quot;#user-content-fn-veryequal&quot; id=&quot;user-content-fnref-veryequal&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;9&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;property&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Sanity: all points in bounds&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;forAll&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dimsGen&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; dims &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;points&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; traceSpiral&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;points.forall&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims.inside&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;====&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This corresponds to property 1:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In (1) we define a new property&lt;/li&gt;
&lt;li&gt;We use &lt;code&gt;dimsGen&lt;/code&gt;, which is a generator of &lt;code&gt;Dims&lt;/code&gt; instances (2), that will randomly generate different dimensions for us&lt;sup&gt;&lt;a href=&quot;#user-content-fn-bounds&quot; id=&quot;user-content-fnref-bounds&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;10&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;The property should hold for any board dimensions that we throw at it&lt;/li&gt;
&lt;li&gt;With the randomly generated dimensions in hand we invoke the &lt;code&gt;traceSpiral&lt;/code&gt; function (3)&lt;/li&gt;
&lt;li&gt;We then assert that all the points in the generated solutions are within the bounds of the dimensions we are working with (4)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&apos;s property number 2:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;property&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Sanity: dimensions match number of points&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;forAll&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dimsGen&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; dims &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;points&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; traceSpiral&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;points.size &lt;/span&gt;&lt;span&gt;====&lt;/span&gt;&lt;span&gt; dims.area &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works similarly to the first property, we run for any instance of &lt;code&gt;Dims&lt;/code&gt; and assert that the length of solution matches the area of the dimensions (1).&lt;/p&gt;
&lt;p&gt;And for property number 3:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;property&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Sanity: no duplicate points&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;forAll&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dimsGen&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; dims &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;points&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; traceSpiral&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;points.distinct &lt;/span&gt;&lt;span&gt;====&lt;/span&gt;&lt;span&gt; points &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we make sure that any solution that we produce does not contain duplicate points (1). Again, holding for any board size.&lt;/p&gt;
&lt;p&gt;In aggregate, these three properties verify that any valid solution is a permutation of all the points on the board.&lt;/p&gt;
&lt;p&gt;Actually, we could&apos;ve written that as a single property, but splitting this up has the advantages that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It&apos;s marginally easier to write these properties than writing a function that correctly generates all the points on the board. Thus reducing the chances of the property itself being broken.&lt;/li&gt;
&lt;li&gt;Having separate properties makes it easier to debug if something in the solution breaks. As we can see which aspect of &quot;being a valid permutation&quot; fails to hold.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And now for the big benefit defining these tests as properties: the property-based machinery is going to run our code on many different dimensions of boards, thus very likely flushing out any edge cases lurking around in our code. This happens without us needing to actually come up with those edge cases ourselves (is a board of size 0 an edge case? 1x1? 2x1? who knows?..).&lt;/p&gt;
&lt;p&gt;We can run the full suite of tests now:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;+ Traces the correct spiral 0.0s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;+ Sanity: all points in bounds 0.006s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;+ Sanity: dimensions match number of points 0.007s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;+ Sanity: no duplicate points 0.008s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We passed all the tests (denoted by &lt;code&gt;+&lt;/code&gt;). Our solution looks correct indeed, and we can be fairly certain that we didn&apos;t miss any edge cases.&lt;/p&gt;
&lt;p&gt;Of course these are just sanity checks that I came up with quickly, they are not specific to spirals, and they don&apos;t actually validate that the solution we got is correct. They only attempt to reject obviously invalid solutions. My claim is that in conjunction with the single unit test above, these properties are enough to give me high confidence that the &quot;obviously correct&quot; solution is actually correct&lt;sup&gt;&lt;a href=&quot;#user-content-fn-initconditions&quot; id=&quot;user-content-fnref-initconditions&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;11&lt;/a&gt;&lt;/sup&gt;.
If you don&apos;t believe me, I challenge you to come up with a non-contrived solution that passes the unit test along with the sanity properties and isn&apos;t a valid solution to the spiral problem. Please let me know if you do.&lt;/p&gt;
&lt;p&gt;In case you&apos;re not satisfied with these properties, it is clear that such a nicely symmetric and recursive problem can have other properties that actually capture its rich structure. If you&apos;re so inclined, you can see an additional property that I defined &lt;a href=&quot;https://github.com/ncreep/first-make-it-correct-blog/blob/master/test/ComplexPropertySuite.scala&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; that tries to capture some of that structure. Since defining this property was more difficult than actually solving the problem, I won&apos;t be showing it here. There&apos;s only so much verification I&apos;m willing to do for a problem I&apos;m not being paid to solve...&lt;/p&gt;
&lt;p&gt;Anyways, I now feel comfortable stating that the obviously correct solution is indeed correct, and we can move on.&lt;/p&gt;
&lt;h2 id=&quot;the-reference-solution&quot;&gt;The Reference Solution&lt;/h2&gt;
&lt;p&gt;Before we actually do move on. Let&apos;s reap another benefit of using property-based testing.&lt;/p&gt;
&lt;p&gt;We can now treat the solution that we just created as a reference solution, so that we can use it to check any other implementation that we come up with. We can define one more property: for all board dimensions, every solution to the problem must match the reference solution.&lt;/p&gt;
&lt;p&gt;In code:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;property&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Matches reference&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;forAll&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dimsGen&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; dims &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;expected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Reference&lt;/span&gt;&lt;span&gt;&lt;span&gt;.traceSpiral&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;actual&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; traceSpiralCandidate&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;actual &lt;/span&gt;&lt;span&gt;====&lt;/span&gt;&lt;span&gt; expected &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are defining this property (for all board dimensions) as part of a suite that will be used by all future solution candidates. First generate the reference solution (1). Then generate the candidate solution (2). Lastly, compare the two to make sure they are equal (3).&lt;/p&gt;
&lt;p&gt;Since we trust the reference solution we can mechanically verify that every other solution matches it. This is a very powerful test. Assuming that the reference is indeed correct, such a test can act as a strong guarantee for any new code that we introduce&lt;sup&gt;&lt;a href=&quot;#user-content-fn-keepsanity&quot; id=&quot;user-content-fnref-keepsanity&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;12&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Finally, with a solid reference in hand, we are ready for the second part of the statement at the beginning of this blog:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;... then make it anything you want.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;recursing-the-right-way&quot;&gt;Recursing the &quot;Right&quot; Way&lt;/h2&gt;
&lt;p&gt;Astute readers may notice that our reference solution, recursively beautiful as it is, is actually flawed in several ways. The most glaring ones:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It does recursion &quot;the wrong way&quot;. That is, it uses recursion in a non-tail position. In a strict language like Scala this easily leads to stack overflows.&lt;/li&gt;
&lt;li&gt;It allocates &lt;code&gt;O(w * h)&lt;/code&gt; memory upfront. This is actually quite bad, so much so, that I wasn&apos;t able to trigger a stack overflow even with a heap of more than 8 GB. Allocating a large enough board, such that it is worthy of a stack overflow is so heavy that I got an out of memory exception first&lt;sup&gt;&lt;a href=&quot;#user-content-fn-jvmparams&quot; id=&quot;user-content-fnref-jvmparams&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;13&lt;/a&gt;&lt;/sup&gt; &lt;sup&gt;&lt;a href=&quot;#user-content-fn-testflaws&quot; id=&quot;user-content-fnref-testflaws&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;14&lt;/a&gt;&lt;/sup&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The fact that I consider these to be flaws is of course subjective, and depends on the use case being considered. More importantly though, these are only non-functional flaws, logically our solution is doing the right thing. And so we can still use it as a reference while we fix these flaws.&lt;/p&gt;
&lt;p&gt;We&apos;ll start by fixing the recursion issue, as this one is easier to tackle.&lt;/p&gt;
&lt;p&gt;Lucky for us, transforming (simple) non-tail-recursive code into tail recursive code is a fairly mechanical process. Given our reference code we can mechanically turn it into a tail recursive version:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Make your return value into an argument to the recursive function&lt;/li&gt;
&lt;li&gt;Instead of returning a result and recursing, update the new argument with the result of the current step and then recurse&lt;/li&gt;
&lt;li&gt;When reaching the base case, return the accumulated result argument&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Applied to our reference solution, the result is &lt;a href=&quot;https://github.com/ncreep/first-make-it-correct-blog/blob/master/test/2.TailRecursiveSuite.scala&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;this&lt;/a&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;traceSpiral&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;peel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;buildCoordinates&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, result &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Nil&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;tailrec &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;peel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;board&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;board &lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Nil&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; result &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; topRow &lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt; otherRows &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;peel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;rotateCCW&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;otherRows&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, topRow &lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;&lt;span&gt; result&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code is very similar to the code we started with, but it&apos;s now tail recursive. In detail:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;peel&lt;/code&gt; function now takes an extra argument &lt;code&gt;result&lt;/code&gt; (3), we&apos;ll use that to accumulate the final result&lt;/li&gt;
&lt;li&gt;When making the recursive call, we prepend the current result &lt;code&gt;topRow&lt;/code&gt; to the &lt;code&gt;result&lt;/code&gt; argument we got (5)&lt;/li&gt;
&lt;li&gt;In the base case (4), we return the &lt;code&gt;result&lt;/code&gt; argument&lt;/li&gt;
&lt;li&gt;&lt;code&gt;peel&lt;/code&gt; is now tail-recursive, so we annotate it with the &lt;code&gt;tailRec&lt;/code&gt; annotation (2), this forces the compiler to validate that the code is indeed tail recursive and can be optimized accordingly&lt;/li&gt;
&lt;li&gt;When making the initial call to &lt;code&gt;peel&lt;/code&gt; we initialize &lt;code&gt;result&lt;/code&gt; with the original base case value &lt;code&gt;Nil&lt;/code&gt; (1)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&apos;s it. The code is now tail recursive. But is it correct? I mean this transformation is not completely trivial, how do we know we got it right?&lt;/p&gt;
&lt;p&gt;Easy, we have our reference solution, we can just run our property-based tests and validate that we are still returning logically correct results:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;TailRecursiveSuite&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;==&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;X&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TailRecursiveSuite&lt;/span&gt;&lt;span&gt;. &lt;/span&gt;&lt;span&gt;Traces&lt;/span&gt;&lt;span&gt; the correct spiral &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; &amp;#x3C;--- 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;31&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;32&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt;    assertEquals&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;points, expected&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;33&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;values are not the same&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Obtained&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;x &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;y &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; ... more output&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Diff&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; obtained, &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&lt;span&gt; expected&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;    x &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;    y &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; ... more output&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Sanity&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; all points in bounds &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;.069s &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; &amp;#x3C;--- 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Sanity&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; dimensions &lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt; number of points &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;.015s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Sanity&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; no duplicate points &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;.019s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;==&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;X&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TailRecursiveSuite&lt;/span&gt;&lt;span&gt;. &lt;/span&gt;&lt;span&gt;Matches&lt;/span&gt;&lt;span&gt; reference&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;12&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;13&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;      actual &lt;/span&gt;&lt;span&gt;====&lt;/span&gt;&lt;span&gt; expected&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TailRecursiveSuite&lt;/span&gt;&lt;span&gt;. &lt;/span&gt;&lt;span&gt;Matches&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;reference&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Falsified&lt;/span&gt;&lt;span&gt; after &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt; passed tests&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Not&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Equal&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;span&gt; lhs &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;span&gt; rhs &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;... And we failed. On two tests no less. I clipped some of the output as it&apos;s a bit noisy. Let&apos;s see what happened.&lt;/p&gt;
&lt;p&gt;We failed the unit test, that&apos;s the &lt;code&gt;X TailRecursiveSuite. Traces the correct spiral&lt;/code&gt; (1). The output contains the diff between what we got and what was expected (clipped). It&apos;s a somewhat difficult to decipher as the hardcoded value in the unit test is a bit large. It&apos;s good to know that we are protected by the unit test, but it&apos;s not that useful for debugging.&lt;/p&gt;
&lt;p&gt;Next we have the sanity tests (2). We did pass them, which is unfortunate, as they don&apos;t help us to narrow down the problem in this case. Oh, well, maybe next time.&lt;/p&gt;
&lt;p&gt;Lastly, we have the reference solution test. And this one fails, and not only that, the failing values are very small. This is no coincidence, property-based testing libraries usually come with the ability to shrink their values. That is, if a test fails on some input, the library tries to shrink that input to the smallest possible value that is still failing&lt;sup&gt;&lt;a href=&quot;#user-content-fn-shrinking&quot; id=&quot;user-content-fnref-shrinking&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;15&lt;/a&gt;&lt;/sup&gt;. In our case this means that if we fail on some random &lt;code&gt;Dims&lt;/code&gt; instance, the library will try to reduce the size of the dimensions as close as possible to &lt;code&gt;Dims(0, 0)&lt;/code&gt;. Here we failed on the dimensions &lt;code&gt;Dims(1, 2)&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Not&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Equal&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;span&gt; lhs &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;span&gt; rhs &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;rhs&lt;/code&gt; is the expected (reference) value, while &lt;code&gt;lhs&lt;/code&gt; is the value we got from our tail recursive solution. We see that our new solution is reversed compared to the reference solution.&lt;/p&gt;
&lt;p&gt;Now, I&apos;m not much into thinking, I got my test to do that for me. Since the result looks reversed, let&apos;s reverse it back:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;traceSpiral&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;peel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;buildCoordinates&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, result &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Nil&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.reverse &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;tailrec&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;peel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;board&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]]&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;board &lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Nil&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; result&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; topRow &lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt; otherRows &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;peel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;rotateCCW&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;otherRows&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, topRow &lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;&lt;span&gt; result&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This solution is the same as the previous one, but the final result is reversed (1). Let&apos;s run the tests again:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;==&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;X&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TailRecursiveSuite&lt;/span&gt;&lt;span&gt;. &lt;/span&gt;&lt;span&gt;Matches&lt;/span&gt;&lt;span&gt; reference&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;12&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;13&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;      actual &lt;/span&gt;&lt;span&gt;====&lt;/span&gt;&lt;span&gt; expected&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TailRecursiveSuite&lt;/span&gt;&lt;span&gt;. &lt;/span&gt;&lt;span&gt;Matches&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;reference&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Falsified&lt;/span&gt;&lt;span&gt; after &lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt; passed tests&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Not&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Equal&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;span&gt; lhs &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;span&gt; rhs &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&apos;re still failing on the same test. The failure looks the same, the result is again reversed compared to the reference. Except that this time the dimensions are transposed, &lt;code&gt;Dims(2, 1)&lt;/code&gt; vs. &lt;code&gt;Dims(1, 2)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I guess it&apos;s not really surprising that a simple reverse wasn&apos;t good enough. The failing unit test with the bigger board wasn&apos;t failing on a simple reversal. Something trickier is going on here.&lt;/p&gt;
&lt;p&gt;The problem is that the counterexample is so minimized that it&apos;s hard to see what the underlying issue is. We need a bigger counterexample. If we weren&apos;t using property-based testing we&apos;d have to manually search for more problematic cases, like some plebs. Luckily, we&apos;ll be spared this gloomy fate. Instead we&apos;ll define a generator that limits the dimensions to be at least &lt;code&gt;2&lt;/code&gt;. And tweak our reference test to use that (temporarily, for &quot;debugging&quot;):&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;property&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Matches reference&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;forAll&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dimsAboveGen&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; dims &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;expected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Reference&lt;/span&gt;&lt;span&gt;&lt;span&gt;.traceSpiral&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;actual&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; traceSpiralCandidate&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.toList&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;actual &lt;/span&gt;&lt;span&gt;====&lt;/span&gt;&lt;span&gt; expected&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In (1) we&apos;re using the new generator. After removing the unproductive &lt;code&gt;reverse&lt;/code&gt; call, and we get a new, larger, counter example:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;span&gt; lhs &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;span&gt; rhs &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next counterexample was right around the corner, who could&apos;ve thunk...&lt;/p&gt;
&lt;p&gt;With this larger, but still not overwhelming, example we see a glimmer of a pattern. The new result is not reversed, but rather the halves are reversed. If we split the list in two and swap the halves around, we&apos;ll match the reference. Let&apos;s confirm this pattern with dimensions above 2:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Not&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Equal&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;span&gt; lhs &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;span&gt; rhs &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Although we probably skipped over &lt;code&gt;2x3&lt;/code&gt; cases, this is good enough. Since the number of points in the result is not even, we obviously won&apos;t be splitting in two halves. But nor can we split in thirds. It seems that the pattern is more involved. Looking for segments that we can reverse, we see the following segment lengths: 2, 2, 2, 3. Weird... But recall our orange peeling algorithm. The first segment we peel off is 3. We are then left with a &lt;code&gt;3x2&lt;/code&gt; board, which we rotate, peeling off a segment of length 2. Rotate again, peel off 2, and then another 2. Our segment lengths are just the orange peeling segments in reverse!&lt;/p&gt;
&lt;p&gt;We&apos;re building up the result the wrong way:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;peel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;board&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]]&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;board &lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Nil&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; result&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; topRow &lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt; otherRows &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;peel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;rotateCCW&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;otherRows&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, topRow &lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;&lt;span&gt; result&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; &amp;#x3C;--&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The arrow points at the problematic place where we build up the result. This was perfectly fine with the original, non-tail-recursive solution. But what I missed in my translation algorithm is that result building with tail recursion goes &quot;the other way around&quot; &lt;sup&gt;&lt;a href=&quot;#user-content-fn-fold&quot; id=&quot;user-content-fnref-fold&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;16&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;The fix is straightforward, just reverse the arguments:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;peel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;board&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]]&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;board &lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Nil&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; result&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; topRow &lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt; otherRows &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;peel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;rotateCCW&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;otherRows&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, result &lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;&lt;span&gt; topRow&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; &amp;#x3C;--&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the fix in place, we finally pass all the tests:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;TailRecursiveSuite:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+ Traces the correct spiral 0.04s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+ Sanity: all points in bounds 0.113s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+ Sanity: dimensions match number of points 0.04s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+ Sanity: no duplicate points 0.046s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+ Matches reference 0.058s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we have the confidence that we didn&apos;t break the correctness of our original solution.&lt;/p&gt;
&lt;p&gt;If you&apos;re really into asymptotics though, you might notice that our solution just turned to be quadratic. Using &lt;code&gt;++&lt;/code&gt; on a &lt;code&gt;List&lt;/code&gt; to append to the ever growing &lt;code&gt;result&lt;/code&gt; value is not good for performance.&lt;/p&gt;
&lt;p&gt;The fix is yet again mechanical, but leads us even further away from the reference solution. Since prepending to a &lt;code&gt;List&lt;/code&gt; is faster than appending to it, we can do that and sprinkle in some &lt;code&gt;reverse&lt;/code&gt; calls. I&apos;ll spare you the gory details of me trying to debug the implementation with tests. The upshot is this:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;traceSpiral&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;peel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;buildCoordinates&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, result &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Nil&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.reverse &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;tailrec&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;peel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;board&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;board &lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Nil&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; result&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; topRow &lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt; otherRows &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;peel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;rotateCCW&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;otherRows&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, topRow.reverse &lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;&lt;span&gt; result&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we reverse the way we build up the result (2). We first reverse the current row, and the prepend it to the accumulator. Finally, we have to reverse the whole result (1).
Rest assured that I messed up the different &lt;code&gt;reverse&lt;/code&gt; calls, but the tests had my back.&lt;/p&gt;
&lt;p&gt;So guided by the correct reference solution we managed to go quite far away to improve performance, while not losing sight of correctness.&lt;/p&gt;
&lt;p&gt;But why stop here?&lt;/p&gt;
&lt;h2 id=&quot;recursion-i-hardly-know-her&quot;&gt;Recursion, I Hardly Know Her&lt;/h2&gt;
&lt;p&gt;Lately, I was flabbergasted to hear that there exist languages that don&apos;t support any form of tail call optimization&lt;sup&gt;&lt;a href=&quot;#user-content-fn-java&quot; id=&quot;user-content-fnref-java&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;17&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Try as we might, our tail-recursive solution doesn&apos;t help us if we&apos;re stuck using such a language. Using any form of recursion is liable to get our stack blown. How can we help our fellow programmers in need?&lt;/p&gt;
&lt;p&gt;Easy, just as there&apos;s a mechanical way to translate simple recursion into tail recursion, so can we translate tail recursive code into an iterative loop.&lt;/p&gt;
&lt;p&gt;The process is as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Replace the recursive function with a while loop&lt;/li&gt;
&lt;li&gt;Replace each argument to the recursive function with a &lt;code&gt;var&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Instead of making a recursive call with new values, update the variables with those new values&lt;/li&gt;
&lt;li&gt;Run the loop until reaching the base case, then return the variable that stands for the accumulator&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Concretely, in &lt;a href=&quot;https://github.com/ncreep/first-make-it-correct-blog/blob/master/3.Iterative.scala&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;code&lt;/a&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;traceSpiral&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;&lt;span&gt;.empty&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;board&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; buildCoordinates&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;while&lt;/span&gt;&lt;span&gt; board &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Nil&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;topRow&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; board.head &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;otherRows&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; board.tail &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;result &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; topRow.reverse &lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt; result &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 6&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;board &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; rotateCCW&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;otherRows&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 7&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;end while&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;result.reverse &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 8&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;We no longer have a recursive function, just one big loop&lt;/li&gt;
&lt;li&gt;What were previously function arguments are now the (mutable) variables &lt;code&gt;result&lt;/code&gt; and &lt;code&gt;board&lt;/code&gt; (1, 2)&lt;/li&gt;
&lt;li&gt;The loop will run until we hit the base case, the empty board (3)&lt;/li&gt;
&lt;li&gt;While the board is not empty, we peel of the current row (4), and the rest of the rows (5)&lt;/li&gt;
&lt;li&gt;We then update &lt;code&gt;result&lt;/code&gt; variable in the same way we did in the last recursive solution (6)&lt;/li&gt;
&lt;li&gt;And the same for the &lt;code&gt;board&lt;/code&gt; variable (7)&lt;/li&gt;
&lt;li&gt;Once the loop completes, &lt;code&gt;result&lt;/code&gt; contains the full result in reverse, which we return after reversing (8)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Look how far away from our initial, elegant, recursive solution we managed to get. But also notice how much more error-prone this code is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We no longer use pattern matching, instead relying on the unsafe &lt;code&gt;head&lt;/code&gt; and &lt;code&gt;tail&lt;/code&gt; methods&lt;sup&gt;&lt;a href=&quot;#user-content-fn-patternmatching&quot; id=&quot;user-content-fnref-patternmatching&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;18&lt;/a&gt;&lt;/sup&gt;, the compiler will no longer have our back when refactoring&lt;/li&gt;
&lt;li&gt;If we forget to update &lt;code&gt;result&lt;/code&gt;, everything will still compile and run, but the final result will be wrong&lt;/li&gt;
&lt;li&gt;If we forget to update &lt;code&gt;board&lt;/code&gt;, everything will still compile and run, but this time we&apos;ll hit an infinite loop&lt;sup&gt;&lt;a href=&quot;#user-content-fn-infinite&quot; id=&quot;user-content-fnref-infinite&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;19&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I won&apos;t disclose how many of those issues I&apos;ve hit while writing this code. But all I&apos;ll say is that I&apos;m happy that I had a robust test suite. Having a solid reference sure pays dividends...&lt;/p&gt;
&lt;p&gt;Looking at this solution though, we might again notice some redundancy. As we are no longer in the nice functional realm, do we really need to pay the price for building an immutable list? Especially as it forces us through all the &lt;code&gt;reverse&lt;/code&gt; shenanigans just to keep us from getting quadratic.&lt;/p&gt;
&lt;p&gt;Might as well go all in on the procedural approach and use a more performant mutable collection:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;traceSpiral&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ListBuffer&lt;/span&gt;&lt;span&gt;&lt;span&gt;.empty&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;board&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; buildCoordinates&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;while&lt;/span&gt;&lt;span&gt; board &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Nil&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;topRow&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; board.head&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;otherRows&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; board.tail&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;result.addAll&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;topRow&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;board &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; rotateCCW&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;otherRows&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;end while&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;result.toList &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;result&lt;/code&gt; is now a mutable &lt;code&gt;ListBuffer&lt;/code&gt; (1), which can be appended to efficiently. Which is what we do in (2), avoiding the need to &lt;code&gt;reverse&lt;/code&gt;. At the very end we build up the final &lt;code&gt;List&lt;/code&gt; result from the buffer (3). This way we can maintain a functional facade while utilizing mutability in the implementation. We gained performance and made the code a bit less confusing.&lt;/p&gt;
&lt;p&gt;Guided by the reference solution we managed to accommodate the needs of our fellow procedural programmers, recursion be damned. Yay us.&lt;/p&gt;
&lt;p&gt;Now let&apos;s move on to the real elephant in the room.&lt;/p&gt;
&lt;h2 id=&quot;laziness-with-class&quot;&gt;Laziness with Class&lt;/h2&gt;
&lt;p&gt;One of my biggest takeaways from dabbling in computer science is that it pays to be lazy&lt;sup&gt;&lt;a href=&quot;#user-content-fn-greed&quot; id=&quot;user-content-fnref-greed&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;20&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Clearly the reference solution is not very lazy. It allocates a potentially large board upfront, while in reality it might not be needed at all. Maybe our clients will need only part of the solution? Or maybe they&apos;ll use it one step at a time, like with streaming? But no, we prevent any such useful use cases by allocating &lt;code&gt;O(w * h)&lt;/code&gt; data upfront, with no way to take it back. Time to fix that.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Nostalgic aside...&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Back in the olden days, in the heyday of OOP, some smart people decreed that we should all &quot;program to interface&quot;. And so you would see programmers gather around in circles and chant &quot;SOLID, SOLID, SOLID...&quot; as they conjure up the latest &lt;code&gt;AbstractSingletonFactoryFactoryVisitater&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-visitater&quot; id=&quot;user-content-fnref-visitater&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;21&lt;/a&gt;&lt;/sup&gt; out of the ether. Those were the days...&lt;/p&gt;
&lt;p&gt;And although these ancient customs may seem strange to us in this day and age, there is a grain of truth to it. Programming to interface can be very useful.&lt;/p&gt;
&lt;p&gt;It is indeed by way of us using a very concrete representation of the board, namely &lt;code&gt;List[List[Point]]&lt;/code&gt;, that we are forced to allocate memory prematurely. But is it actually necessary for the board to be a list? Is it essential to the solution?&lt;/p&gt;
&lt;p&gt;If we could extract some kind of simple interface from the reference maybe we&apos;ll be able to implement a less memory intensive solution.&lt;/p&gt;
&lt;p&gt;We can use the code we started with as guidance:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;traceSpiral&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;peel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;buildCoordinates&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;peel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;board&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;]])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;board &lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Nil&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Nil&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; topRow &lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt; otherRows &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;topRow &lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;&lt;span&gt; peel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;rotateCCW&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;otherRows&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Suppose we had some entity called &lt;code&gt;Board&lt;/code&gt;. What kind of interface should it have? Taking inspiration from the reference solution, what we need is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A way to construct a &lt;code&gt;Board&lt;/code&gt; for given dimensions: &lt;code&gt;Dims =&gt; Board&lt;/code&gt; (1)&lt;/li&gt;
&lt;li&gt;A way to query the state of the board, pattern matching would be a nice way to do it (2)&lt;/li&gt;
&lt;li&gt;A case for empty boards (3)&lt;/li&gt;
&lt;li&gt;A case for non-empty boards, where we can extract the top row and the rest of the rows (4)&lt;/li&gt;
&lt;li&gt;A way to rotate the board counter-clockwise (5)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Any concrete &lt;code&gt;Board&lt;/code&gt; representation that implements this interface can be used to solve our problem. The trick is to find a representation that consumes less memory than &lt;code&gt;List&lt;/code&gt;.
If this was some real code and we wanted to make it polymorphic over different board implementations, we might actually define that interface in code. But here I&apos;ll just use this pseudo-interface as a guide and provide another concrete implementation directly.&lt;/p&gt;
&lt;p&gt;Not using &lt;code&gt;List&lt;/code&gt; is necessary for us to be able to solve the problem lazily, but it&apos;s not sufficient. To actually turn things lazy we&apos;ll use &lt;code&gt;Iterator&lt;/code&gt;s&lt;sup&gt;&lt;a href=&quot;#user-content-fn-streams&quot; id=&quot;user-content-fnref-streams&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;22&lt;/a&gt;&lt;/sup&gt;. So instead of returning &lt;code&gt;List[Point]&lt;/code&gt;, we&apos;ll return &lt;code&gt;Iterator[Point]&lt;/code&gt;. The &lt;a href=&quot;https://github.com/ncreep/first-make-it-correct-blog/blob/master/4.Lazy.scala&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;new signature&lt;/a&gt; that we need to implement is:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;traceSpiral&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the goal being that we never allocate more than &lt;code&gt;O(1)&lt;/code&gt; memory.&lt;/p&gt;
&lt;p&gt;Let&apos;s sketch out our new &lt;code&gt;Board&lt;/code&gt; type, we&apos;ll refine the details as we go along. First define a type:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;sealed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;trait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are using a sealed trait (sum type) as requirements 2-4 indicate that we are going to have more than one case: empty and non-empty. Let&apos;s add those:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Empty&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NonEmpty&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One case per requirement, and we can distinguish between them by pattern matching.&lt;/p&gt;
&lt;p&gt;As per requirement 4 we&apos;ll also need some way of extracting data from the non-empty case, so we&apos;ll add methods:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NonEmpty&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;topRow&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;otherRows&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are using methods rather than class fields as I don&apos;t yet want to commit to a specific representation of &lt;code&gt;NonEmpty&lt;/code&gt;. &lt;code&gt;topRow&lt;/code&gt; returns an &lt;code&gt;Iterator&lt;/code&gt; and not a &lt;code&gt;List&lt;/code&gt; so as to preserve our laziness goals.&lt;/p&gt;
&lt;p&gt;Requirement 5 implies that we need a rotation method that works on any board:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;sealed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;trait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rotateCCW&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Lastly, we need a builder function for requirement 1:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;buildCoordinates&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And this completes our pseudo-interface, we can plug it into the algorithm and get a brand new solution to the spiral problem. To wit:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;traceSpiral&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;peel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;buildCoordinates&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;peel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;board&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;board &lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Empty&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;.empty &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ps&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;NonEmpty&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ps.topRow &lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;&lt;span&gt; peel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ps.otherRows.rotateCCW&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This looks eerily similar to the reference solution. We just replaced every occurrence of &lt;code&gt;List&lt;/code&gt; with &lt;code&gt;Iterator&lt;/code&gt;, as &lt;code&gt;Iterator&lt;/code&gt; and &lt;code&gt;List&lt;/code&gt; share a very similar interface&lt;sup&gt;&lt;a href=&quot;#user-content-fn-bewareiterator&quot; id=&quot;user-content-fnref-bewareiterator&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;23&lt;/a&gt;&lt;/sup&gt; this change is almost seamless, &lt;code&gt;++&lt;/code&gt; works for either type (3). In (1) we return an empty &lt;code&gt;Iterator&lt;/code&gt; rather than an empty &lt;code&gt;List&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The only place things look a bit different is the &lt;code&gt;NonEmpty&lt;/code&gt; pattern match (2), where we no longer extract the rows&lt;sup&gt;&lt;a href=&quot;#user-content-fn-customextractor&quot; id=&quot;user-content-fnref-customextractor&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;24&lt;/a&gt;&lt;/sup&gt;. Instead, we get them with the appropriate method calls on the board value (3).&lt;/p&gt;
&lt;p&gt;As a bonus, we no longer have to care about the non-tail-recursive call to &lt;code&gt;peel&lt;/code&gt;. &lt;code&gt;++&lt;/code&gt; being lazy in its right argument makes that problem go away (left as an exercise to figure out why).&lt;/p&gt;
&lt;p&gt;All that&apos;s left is to actually implement the functions I left abstract. Once we do that, we should have a working solution, but with a very different memory footprint.&lt;/p&gt;
&lt;p&gt;Let&apos;s start with the easy case, &lt;code&gt;Empty&lt;/code&gt;. The empty case has no data associated with it, so we don&apos;t need to add any fields to it. All we need is to be able to rotate it, which is trivial:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Empty&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rotateCCW&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Empty&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A rotated empty board is still an empty board. Done.&lt;/p&gt;
&lt;p&gt;Now comes the tricky bit, how do we implement &lt;code&gt;NonEmpty&lt;/code&gt; without populating a whole board-worth of data?&lt;/p&gt;
&lt;p&gt;Here we can take advantage of the fact that we already have a working solution that we can use to gain some insight about the shape of the data we&apos;re dealing with. Recall the board that we start with for a 4x3 problem:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,0) | (1,0) | (2,0) | (3,0) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,1) | (1,1) | (2,1) | (3,1) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,2) | (1,2) | (2,2) | (3,2) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first row, which is what we want to extract is not made out of arbitrary points, instead it&apos;s very structured. It&apos;s just &lt;code&gt;(0, i)&lt;/code&gt;, where &lt;code&gt;i&lt;/code&gt; is &lt;code&gt;0 &amp;#x3C;= i &amp;#x3C; 4&lt;/code&gt;. We can easily represents this with an &lt;code&gt;Iterator&lt;/code&gt; that will consume a constant amount of space:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;&lt;span&gt;.range&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Iterator.range&lt;/code&gt; creates a lazy range that is equivalent to &lt;code&gt;[0, 1, 2, 3]&lt;/code&gt;, but without actually materializing all the numbers into memory. Then we use &lt;code&gt;map&lt;/code&gt;, which is lazy on &lt;code&gt;Iterator&lt;/code&gt;, to build up &lt;code&gt;Point(i, 0)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For the actual implementation of &lt;code&gt;topRow&lt;/code&gt; we&apos;ll need to replace the hardcoded &lt;code&gt;4&lt;/code&gt; with the width of the board. And that&apos;s the first ingredient that &lt;code&gt;NonEmpty&lt;/code&gt; needs to hold on to: the current &lt;code&gt;Dims&lt;/code&gt; of the board. Leading to this code:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NonEmpty&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;topRow&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;&lt;span&gt;.range&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;, dims.w&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We added a new field to the &lt;code&gt;NonEmpty&lt;/code&gt; case to hold on to the dimensions that this board represents (1). Using the &lt;code&gt;dims&lt;/code&gt; field it&apos;s now possible to create the iterator for the first row (2).&lt;/p&gt;
&lt;p&gt;This seemingly works, but it was suspiciously easy to implement. Sure this is the correct answer on the first step of the solution, but will it work for the second step?&lt;/p&gt;
&lt;p&gt;Recall that as we are solving the problem we keep shrinking and rotating the board. So for the second step we should be working with the following board:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (3,1) | (3,2) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (2,1) | (2,2) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (1,1) | (1,2) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,1) | (0,2) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is what&apos;s left after we remove the first row and rotate the board. The board will now have different dimensions, but we are covered, we have the dimensions as a field of &lt;code&gt;NonEmpty&lt;/code&gt;. We can update its values as we are building up the solution.&lt;/p&gt;
&lt;p&gt;But even with the correct dimensions in place, the code we just had:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;&lt;span&gt;.range&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;, dims.w&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Won&apos;t produce the correct result. We&apos;ll get &lt;code&gt;[(0, 0), (1, 0)]&lt;/code&gt;, which is the first row of a board of dimensions &lt;code&gt;2x4&lt;/code&gt;, instead of the correct &lt;code&gt;[(3, 1), (3, 2)]&lt;/code&gt;. The problem is that we are not actually working with a &lt;code&gt;2x4&lt;/code&gt; board, the entries that we are looking for come from the original board of &lt;code&gt;4x3&lt;/code&gt;. Compared to the &lt;code&gt;2x4&lt;/code&gt; board, the original board is rotated and shifted (since we removed a row). Without the information about how much we are rotated and shifted we won&apos;t be able to compute the correct first row. Let&apos;s add this information to our class:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NonEmpty&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;rotation&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Rotation&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;shift&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Rotation&lt;/code&gt; is a type that represents the current rotation of the board relative to the original board we started with. We do it by wrapping a number that represents the number of 90 degree rotations that we have&lt;sup&gt;&lt;a href=&quot;#user-content-fn-preciserep&quot; id=&quot;user-content-fnref-preciserep&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;25&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Rotation&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;E.g., &lt;code&gt;Rotation(0)&lt;/code&gt; is no rotation, &lt;code&gt;Rotation(1)&lt;/code&gt; is rotation by 90 degrees, and we wrap around at 4: &lt;code&gt;Rotation(4) = Rotation(0)&lt;/code&gt;. For the second step we are at &lt;code&gt;Rotation(1)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;shift&lt;/code&gt; field represents where we need to shift the current board so that it matches the original board&lt;sup&gt;&lt;a href=&quot;#user-content-fn-delta&quot; id=&quot;user-content-fnref-delta&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;26&lt;/a&gt;&lt;/sup&gt;. Or more concretely, where the top-left corner of the current board is located on the original board. We&apos;ll be adding &lt;code&gt;shift&lt;/code&gt; to each point in the result. For the second step the shift is &lt;code&gt;(3, 1)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With these two fields we can define the following helper method:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rotateShift&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;point&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This method takes a single point, and rotates and shifts it using the &lt;code&gt;rotation&lt;/code&gt; and &lt;code&gt;shift&lt;/code&gt; fields. The implementation is not very difficult (you can see it in the &lt;a href=&quot;https://github.com/ncreep/first-make-it-correct-blog/blob/master/4.Lazy.scala#L41&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;repo&lt;/a&gt;), but having nonexistent spatial abilities it took me quite a few trials and errors to get it right. Figuring out the axis of rotation, mixing up signs, the order of operations, and rotation directions. But guess what had my back all along? A strong test suite backed by a correct solution.&lt;/p&gt;
&lt;p&gt;If we keep track of the correct rotation and shift while we compute the solution (we&apos;ll see how later on), we can use them to get the right answer for the first row at every step:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;topRow&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;row&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;&lt;span&gt;.range&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;, dims.w&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;row.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;rotateShift&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we compute the first row like we did before (1), but instead of returning it as is, we &lt;code&gt;rotateShift&lt;/code&gt; each point in the row (2). This is still as lazy as before, but now gives the correct answer at every step (assuming that the &lt;code&gt;rotate&lt;/code&gt; and &lt;code&gt;shift&lt;/code&gt; fields are correct).&lt;/p&gt;
&lt;p&gt;For example, as mentioned above, for the second step of the solution we should have: &lt;code&gt;rotation = Rotation(1)&lt;/code&gt;, &lt;code&gt;shift = Point(3, 1)&lt;/code&gt;, and &lt;code&gt;row = [(0, 0), (1, 0)]&lt;/code&gt;. If we rotate it by 90 degrees we get:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[(0, 0), (0, 1)]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we shift it to get:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[(3, 1), (3, 2)]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which is the correct result for the second step.&lt;/p&gt;
&lt;p&gt;Before we proceed with &lt;code&gt;otherRows&lt;/code&gt;, let&apos;s define a useful helper:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;buildCoordinateFrom&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;                        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rotation&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Rotation&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;                        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;shift&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; dims.area &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Empty&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NonEmpty&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims, rotation, shift&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function takes in all the ingredient that we need for a board (1) and constructs the appropriate representation for it. For dimensions that represent an empty area we return an instance of &lt;code&gt;Empty&lt;/code&gt; (2). Otherwise we return a &lt;code&gt;NonEmpty&lt;/code&gt; instance with all the arguments we got (3). This implementation conveniently hides away the distinction between &lt;code&gt;Empty&lt;/code&gt; and &lt;code&gt;NonEmpty&lt;/code&gt; when we don&apos;t care about it.&lt;/p&gt;
&lt;p&gt;With this function we can also implement the &lt;code&gt;buildCoordinates&lt;/code&gt; function that&apos;s part of our &quot;interface&quot;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;buildCoordinates&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;buildCoordinateFrom&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims, &lt;/span&gt;&lt;span&gt;Rotation&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is just a call to &lt;code&gt;buildCoordinateFrom&lt;/code&gt; with the rotation and shift set to their initial values (no rotation and no shift).&lt;/p&gt;
&lt;p&gt;Next we are ready to implement &lt;code&gt;otherRows&lt;/code&gt;. As we are aiming for laziness here, no &quot;actual&quot; work should be performed by &lt;code&gt;otherRows&lt;/code&gt;. It&apos;s &lt;code&gt;topRow&lt;/code&gt;&apos;s job to produce actual data, &lt;code&gt;otherRows&lt;/code&gt; needs only to keep track of the changing state of the board. That is, it needs to update &lt;code&gt;dims&lt;/code&gt;, &lt;code&gt;rotation&lt;/code&gt;, and &lt;code&gt;shift&lt;/code&gt; to match the new, smaller board.&lt;/p&gt;
&lt;p&gt;Pictorially, what&apos;s happening is this:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;|   x   |   x   |   x   |   x   |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,1) | (1,1) | (2,1) | (3,1) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,2) | (1,2) | (2,2) | (3,2) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We need to remove the top row, marked by &lt;code&gt;x&lt;/code&gt;s. We can do it like so:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;otherRows&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;newDims&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; dims.reduceH&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;newShift&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; rotateShift&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;x &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, y &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;buildCoordinateFrom&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;newDims, rotation, newShift&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the first step we reduce the vertical (&lt;strong&gt;h&lt;/strong&gt;eight) dimension of the board by 1 (1), the intent being to drop the top row. Next we compute the new shift. When removing the top row, the new top-left corner moves one step down, hence the shift is by &lt;code&gt;(0, 1)&lt;/code&gt; (one step down). But we must not forget that we have the current rotation and shift to account for, so we have to &lt;code&gt;rotateShift&lt;/code&gt; &lt;code&gt;(0, 1)&lt;/code&gt; to cover that (2). Finally, we build up a new (smaller) board with new dimensions and shift using &lt;code&gt;buildCoordinateFrom&lt;/code&gt; (3).&lt;/p&gt;
&lt;p&gt;All we did here is to manipulate the parameters that represent the state of the board, and laziness is preserved.&lt;/p&gt;
&lt;p&gt;The last ingredient to complete this lazy solution is &lt;code&gt;rotateCCW&lt;/code&gt;. It&apos;s similar to &lt;code&gt;otherRows&lt;/code&gt; in that it only manipulates the state parameters, but it&apos;s somewhat trickier geometrically. We need this board:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,1) | (1,1) | (2,1) | (3,1) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,2) | (1,2) | (2,2) | (3,2) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To become this:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (3,1) | (3,2) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (2,1) | (2,2) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (1,1) | (1,2) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,1) | (0,2) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Meaning that the top right corner rotates to become the top-left corner.&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rotateCCW&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;newDims&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; dims.transpose &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;newRotation&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; rotation.next &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;newShift&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; rotateShift&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;x &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; dims.w &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, y &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;buildCoordinateFrom&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;newDims, newRotation, newShift&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Step by step:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The new dimensions are just the transpose (swapping &lt;code&gt;w&lt;/code&gt; and &lt;code&gt;h&lt;/code&gt;) of the old ones (1)&lt;/li&gt;
&lt;li&gt;The new rotation is &lt;code&gt;+ 1&lt;/code&gt; on the old one, but we need to wrap around when we reach &lt;code&gt;4&lt;/code&gt;, that&apos;s what &lt;code&gt;next&lt;/code&gt; does (2)&lt;/li&gt;
&lt;li&gt;The new shift is the top-right corner &lt;code&gt;(w - 1, 0)&lt;/code&gt;, which now becomes the top-left corner, appropriately &lt;code&gt;rotateShift&lt;/code&gt;ed, of course (3)&lt;sup&gt;&lt;a href=&quot;#user-content-fn-offbyone&quot; id=&quot;user-content-fnref-offbyone&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;27&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;With the full new state, we can create a new board (4)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&apos;s it! We got a fully lazy solution to our problem. And if you&apos;re thinking to yourselves &quot;wow, so much geometry, must have been tedious and error prone to implement&quot;, you&apos;re completely right. Lucky that I was blessed by a correct reference solution to keep me sane.&lt;/p&gt;
&lt;p&gt;And after so many failed attempts, it was all the more satisfying to see the test suite passing on this one:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;LazySuite:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+ Traces the correct spiral 0.023s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+ Sanity: all points in bounds 0.051s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+ Sanity: dimensions match number of points 0.019s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+ Sanity: no duplicate points 0.021s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+ Matches reference 0.04s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But is it actually lazy?&lt;/p&gt;
&lt;p&gt;While this code using the reference solution throws an out-of-memory exception on my machine:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Reference&lt;/span&gt;&lt;span&gt;&lt;span&gt;.traceSpiral&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;w &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;5000&lt;/span&gt;&lt;span&gt;, h &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;5000&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.foreach&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;println&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The same code with the lazy solution happily chugs along, printing all the spiral elements:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Lazy&lt;/span&gt;&lt;span&gt;&lt;span&gt;.traceSpiral&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;w &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;5000&lt;/span&gt;&lt;span&gt;, h &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;5000&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.foreach&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;println&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Mission accomplished. By using the &quot;interface&quot; of the obviously correct solution as inspiration we managed to derive a functioning lazy solution. At least for me, coming up with such a solution without some concrete (and correct) reference would&apos;ve been challenging&lt;sup&gt;&lt;a href=&quot;#user-content-fn-improvelazy&quot; id=&quot;user-content-fnref-improvelazy&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;28&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;But all these rotations and shifts, they do feel a bit painful.&lt;/p&gt;
&lt;h2 id=&quot;follow-the-pain&quot;&gt;Follow the Pain&lt;/h2&gt;
&lt;p&gt;There&apos;s this phenomenon that I noticed over the years, that software developers tend to build up pain tolerance to the point where they no longer feel any pain while writing code. It seems to be a protection mechanism against all the terrible code we have to deal with daily. &quot;It gets the job done&quot;, &quot;worse is better&quot;, &quot;it is what it is&quot;, they say to themselves and move along the 3,000 LOC function. And that&apos;s a shame. Pain is an excellent driver for improvement. The moment you stop feeling pain, like a shark that stops moving, your creativity and drive to improve wither away.&lt;/p&gt;
&lt;p&gt;Cultivating the appropriate pain sensitivity when writing code is a must-have when working on large and complex codebases.&lt;/p&gt;
&lt;p&gt;With that in mind, I shall follow the pain that we felt in the previous section with all the geometry and rotations. How did we get there? Does it have to be that way?&lt;/p&gt;
&lt;p&gt;We pretty much mechanically followed the &quot;interface&quot; dictated by the reference solution. One might say that we were lulled into a false sense of security by it. Maybe it&apos;s a downside of having an existing solution. But not all is lost. Now that we have the painful solution, we can use it to visualize the problem, and maybe come up with something better.&lt;/p&gt;
&lt;p&gt;The main pain point of the lazy solution are the rotations that we have to keep track of. They contribute the biggest chunk of complication in the code, making the shifts jump all over the place in a seemingly random fashion. Let&apos;s plot the rotations and shifts on a board:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| a,0→ |      |      |      |      |      |      |      |      |      |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;|      | e,0→ |      |      |      |      |      |      |      | ↓b,1 |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;|      |      | i,0→ |      |      |      |      |      | ↓f,1 |      |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;|      |      |      | m,0→ |      |      |      | ↓j,1 |      |      |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;|      |      | l,3↑ |      |      | ←o,2 | ↓n,1 |      |      |      |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;|      | h,3↑ |      |      |      |      | ←k,2 |      |      |      |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| d,3↑ |      |      |      |      |      |      | ←g,2 |      |      |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;|      |      |      |      |      |      |      |      | ←c,2 |      |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here the first letter (a-z) is indexing the shifts, the number is the rotation index (0-3), and the arrow is the direction of the row getting peeled.&lt;/p&gt;
&lt;p&gt;Unsurprisingly, the shifts form a spiral-like pattern, something that was not intuitively clear from the code. Seeing this from a high-level vantage point makes the pattern very clear: all shifts of the same rotation sit on a diagonal. Better yet, the shifts with no rotation (&lt;code&gt;== 0&lt;/code&gt;) sit on the main diagonal &lt;code&gt;(i, i)&lt;/code&gt;, for &lt;code&gt;i&lt;/code&gt; going from 0 to the center of the board.&lt;/p&gt;
&lt;p&gt;This reflects the not very deep insight&lt;sup&gt;&lt;a href=&quot;#user-content-fn-spin&quot; id=&quot;user-content-fnref-spin&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;29&lt;/a&gt;&lt;/sup&gt; about 90 degree rotations: every 4 rotations we are back where we started. So each shift of the same rotation must start on the same angle of the board.&lt;/p&gt;
&lt;p&gt;With this insight in mind we can come up with a new algorithm to solve the riddle:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;On each step, draw a frame around the board&lt;/li&gt;
&lt;li&gt;Remove the frame&apos;s points and add them (in clockwise order) to the solution&lt;/li&gt;
&lt;li&gt;Repeat until the board is empty&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Notice how this algorithm does not imply any rotations. We just remove frames one by one keeping the board in place.&lt;/p&gt;
&lt;p&gt;We can illustrate the first few steps of the algorithm with this board:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,0) | (1,0) | (2,0) | (3,0) | (4,0) | (5,0) | (6,0) | (7,0) | (8,0) | (9,0) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,1) | (1,1) | (2,1) | (3,1) | (4,1) | (5,1) | (6,1) | (7,1) | (8,1) | (9,1) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,2) | (1,2) | (2,2) | (3,2) | (4,2) | (5,2) | (6,2) | (7,2) | (8,2) | (9,2) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,3) | (1,3) | (2,3) | (3,3) | (4,3) | (5,3) | (6,3) | (7,3) | (8,3) | (9,3) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,4) | (1,4) | (2,4) | (3,4) | (4,4) | (5,4) | (6,4) | (7,4) | (8,4) | (9,4) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,5) | (1,5) | (2,5) | (3,5) | (4,5) | (5,5) | (6,5) | (7,5) | (8,5) | (9,5) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,6) | (1,6) | (2,6) | (3,6) | (4,6) | (5,6) | (6,6) | (7,6) | (8,6) | (9,6) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,7) | (1,7) | (2,7) | (3,7) | (4,7) | (5,7) | (6,7) | (7,7) | (8,7) | (9,7) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first frame will be:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,0) | (1,0) | (2,0) | (3,0) | (4,0) | (5,0) | (6,0) | (7,0) | (8,0) | (9,0) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,1) |                                                               | (9,1) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------                                                               ---------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,2) |                                                               | (9,2) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------                                                               ---------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,3) |                                                               | (9,3) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------                                                               ---------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,4) |                                                               | (9,4) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------                                                               ---------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,5) |                                                               | (9,5) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------                                                               ---------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,6) |                                                               | (9,6) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,7) | (1,7) | (2,7) | (3,7) | (4,7) | (5,7) | (6,7) | (7,7) | (8,7) | (9,7) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------------------------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We add the elements of the frame in clockwise direction to the solution. The remaining board becomes:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (1,1) | (2,1) | (3,1) | (4,1) | (5,1) | (6,1) | (7,1) | (8,1) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (1,2) | (2,2) | (3,2) | (4,2) | (5,2) | (6,2) | (7,2) | (8,2) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (1,3) | (2,3) | (3,3) | (4,3) | (5,3) | (6,3) | (7,3) | (8,3) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (1,4) | (2,4) | (3,4) | (4,4) | (5,4) | (6,4) | (7,4) | (8,4) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (1,5) | (2,5) | (3,5) | (4,5) | (5,5) | (6,5) | (7,5) | (8,5) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (1,6) | (2,6) | (3,6) | (4,6) | (5,6) | (6,6) | (7,6) | (8,6) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (1,7) | (2,7) | (3,7) | (4,7) | (5,7) | (6,7) | (7,7) | (8,7) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next frame becomes:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (1,1) | (2,1) | (3,1) | (4,1) | (5,1) | (6,1) | (7,1) | (8,1) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (1,2) |                                               | (8,2) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------                                               ---------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (1,3) |                                               | (8,3) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------                                               ---------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (1,4) |                                               | (8,4) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------                                               ---------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (1,5) |                                               | (8,5) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------                                               ---------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (1,6) |                                               | (8,6) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (1,7) | (2,7) | (3,7) | (4,7) | (5,7) | (6,7) | (7,7) | (8,7) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-----------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We add it to the solution, and repeat until the board is empty. No rotations involved, everything seems simple&lt;sup&gt;&lt;a href=&quot;#user-content-fn-formfunction&quot; id=&quot;user-content-fnref-formfunction&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;30&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Let&apos;s &lt;a href=&quot;https://github.com/ncreep/first-make-it-correct-blog/blob/master/5.LazyFrame.scala&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;code&lt;/a&gt; this up (while still maintaining laziness). We&apos;ll use the description of the algorithm as an inspiration for an &quot;interface&quot;, and solve the problem in terms of the new interface. After that we&apos;ll actually implement the interface itself.&lt;/p&gt;
&lt;p&gt;Like before, we&apos;ll start with two cases for the board:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;sealed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;trait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Empty&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NonEmpty&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getFrame&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dropFrame&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;buildCoordinates&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;Empty&lt;/code&gt; case looks the same (1), no additional methods beyond the datatype itself. &lt;code&gt;NonEmpty&lt;/code&gt; changes completely and now has two new methods:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;getFrame&lt;/code&gt; - returns an iterator for the full frame of the board (1), in clockwise order&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dropFrame&lt;/code&gt;- returns a new board with the outer frame of the board removed (2)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We also still use a factory function to build up a board for given dimensions (3).&lt;/p&gt;
&lt;p&gt;This covers all the functionality we need to implement the new algorithm:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;traceSpiral&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;peel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;span&gt;&lt;span&gt;.buildCoordinates&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;peel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;board&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;board &lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Empty&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;.empty &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nonEmpty&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;NonEmpty&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;nonEmpty.getFrame &lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;&lt;span&gt; peel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;nonEmpty.dropFrame&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The new implementation is still pretty simple, and quite similar to the original lazy solution:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We build a new board for the dimensions of the problem and call the recursive &lt;code&gt;peel&lt;/code&gt; function on it (1)&lt;/li&gt;
&lt;li&gt;On each peeling step we match on the board (2)&lt;/li&gt;
&lt;li&gt;If it&apos;s empty, we are done and produce an empty iterator (3)&lt;/li&gt;
&lt;li&gt;Otherwise, for the non-empty case (4)&lt;/li&gt;
&lt;li&gt;We extract the frame of the board, and prepend it to the result of peeling the board without the frame (5)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We followed the description of the algorithm to the letter, now we just need to implement the interface.&lt;/p&gt;
&lt;p&gt;For the &lt;code&gt;Empty&lt;/code&gt; case there&apos;s nothing more to implement. Rotations are no longer a part of the interface, so the &lt;code&gt;Empty&lt;/code&gt; case just needs to exist.&lt;/p&gt;
&lt;p&gt;Now, what kind of data does &lt;code&gt;NonEmpty&lt;/code&gt; needs to have? Before we kept track of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The dimensions of the board, still relevant&lt;/li&gt;
&lt;li&gt;The rotation of the board, no longer relevant&lt;/li&gt;
&lt;li&gt;The shift of the board, still relevant as we will keep shrinking the board&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But we can simplify the shift. Previously the shift was a &lt;code&gt;Point&lt;/code&gt; because it was difficult to follow the position at each step. But now we just need to move along the main diagonal on each peeling step. So on step &lt;code&gt;i&lt;/code&gt;, the shift is &lt;code&gt;(i, i)&lt;/code&gt;, meaning that we just need a single number, rather than a point to represent the shift.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;NonEmpty&lt;/code&gt; then becomes:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NonEmpty&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;shift&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With these pieces of state we can implement the methods of &lt;code&gt;NonEmpty&lt;/code&gt;. First &lt;code&gt;getFrame&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The idea here is similar to getting the first row of a board like we did in the first lazy solution. But this time we need both horizontal and vertical directions, moving forwards and backwards. So, for example, the vertical segment on the left is given by:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;&lt;span&gt;.range&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;h &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, step &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;, _&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This segment at &lt;code&gt;y = h - 1&lt;/code&gt;, and we step backwards till &lt;code&gt;y = 0&lt;/code&gt; (1). This becomes the &lt;code&gt;y&lt;/code&gt; coordinate of &lt;code&gt;Point&lt;/code&gt;s with a fixed &lt;code&gt;x = 0&lt;/code&gt;. Figuring out the exact offsets and handling off-by-one errors is of course prone to bugs, but we&apos;re not worried, we have a robust test suite to catch such things.&lt;/p&gt;
&lt;p&gt;Getting the full frame becomes:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getFrame&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;path1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;&lt;span&gt;.range&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, w &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;path2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;&lt;span&gt;.range&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, h &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;w &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;, _&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;path3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;&lt;span&gt;.range&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;w &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, step &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_, h &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;path4&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;&lt;span&gt;.range&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;h &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, step &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;, _&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;frame&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;path1 &lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt; path2 &lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt; path3 &lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt; path4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;frame.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_.shiftBy&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dx &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; shift, dy &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; shift&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;We start by importing the fields of the &lt;code&gt;Dims&lt;/code&gt; instance of the board, just to make the code below more concise (1)&lt;/li&gt;
&lt;li&gt;Then we create the different segments of the frame (2), in clockwise order, the details of the numbering are left as an exercise&lt;/li&gt;
&lt;li&gt;The full frame is composed from concatenating all four segments (3)&lt;/li&gt;
&lt;li&gt;Lastly, we need to shift the frame diagonally down, we do it by moving each point by &lt;code&gt;(shift, shift)&lt;/code&gt; (4)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The result is a full frame, shifted to correspond to location of the current step. I don&apos;t know about you, but for me it was much easier to figure out this code compared to all the rotation logic we had before.&lt;/p&gt;
&lt;p&gt;Like before, we&apos;ll use a helper to build boards at the current dimensions and shift:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;buildCoordinateFrom&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;shift&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; dims.area &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Empty&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NonEmpty&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims, shift&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This time we only get the dimensions and shift (1). In (2) we create an &lt;code&gt;Empty&lt;/code&gt; board if the area is &lt;code&gt;0&lt;/code&gt;. If not, we create a &lt;code&gt;NonEmpty&lt;/code&gt; board with the given dimensions and shift (3).&lt;/p&gt;
&lt;p&gt;With the helper, &lt;code&gt;buildCoordinates&lt;/code&gt; is trivial:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;buildCoordinates&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;buildCoordinateFrom&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And lastly, &lt;code&gt;dropFrame&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dropFrame&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;newDims&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; dims.reduceBy&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;newShift&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; shift &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;buildCoordinateFrom&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;newDims, newShift&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;The new dimensions (1) after dropping a frame are computed by reducing the dimensions by 2 both horizontally and vertically (since the frame has two segments in each orientation)&lt;/li&gt;
&lt;li&gt;The new shift (2) is just incrementing by 1 as we move along the main diagonal&lt;/li&gt;
&lt;li&gt;Then we can build a new board with the new state (3)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Done. This should be a fully working, lazy, solution to the problem. As promised it&apos;s greatly simplified by not having to deal with rotations at all.&lt;/p&gt;
&lt;p&gt;Does it work?&lt;/p&gt;
&lt;p&gt;Let&apos;s find out by running the tests&lt;sup&gt;&lt;a href=&quot;#user-content-fn-testflag&quot; id=&quot;user-content-fnref-testflag&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;31&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;LazyFrameBuggySuite:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+ Traces the correct spiral 0.019s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;^C&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The good news are that the unit test is passing. The bad news are that I had to terminate the run due to what seems like an infinite loop.&lt;/p&gt;
&lt;p&gt;The property-based tests are catching some edge case that the unit test is missing. Great to have them watching our back. Unfortunately, as the blight of Turing completeness is upon us, there&apos;s no way for the tests to actually show us what triggered the infinite loop.&lt;/p&gt;
&lt;p&gt;Instead we&apos;ll be doing some good old-fashioned &lt;code&gt;println&lt;/code&gt; debugging. Since the board contains the full state information as we are moving along the solution, we&apos;ll start by printing the board on each step. Which might give us the following (output changes due to randomization in property-based test):&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;NonEmpty&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;NonEmpty&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;NonEmpty&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;NonEmpty&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;NonEmpty&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;7&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;NonEmpty&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;7&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;9&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Somehow, we are getting negative dimensions. Philosophical issues aside, how on earth does the board&apos;s dimension gets negative?&lt;/p&gt;
&lt;p&gt;The clue is in the fact that the dimensions reduce by two on every step. Which was of course part of the algorithm. Unlike the original lazy version where the dimensions reduce by 1 one each step, and we are guaranteed to hit 0 eventually, in the new code we might just jump over it.&lt;/p&gt;
&lt;p&gt;Now, if I had any principles, I would be practicing &lt;a href=&quot;https://www.youtube.com/watch?v=PSh7JUfDstE&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;what I preach&lt;/a&gt; and make this &quot;illegal state unrepresentable&quot; in a way that&apos;s guaranteed by the compiler. But I&apos;m not, and I&apos;ve already spilt too much ink discussing this one problem.&lt;/p&gt;
&lt;p&gt;What we can do instead is to fix the code where we skip over 0 and decide that negative dimensions should yield an empty board:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;buildCoordinateFrom&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;shift&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Board&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; dims.w &lt;/span&gt;&lt;span&gt;&amp;#x3C;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; dims.h &lt;/span&gt;&lt;span&gt;&amp;#x3C;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Empty&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NonEmpty&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims, shift&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now whenever any of the dimensions hops into the negative, we decree it to be the &lt;code&gt;Empty&lt;/code&gt; board. Does it work now?&lt;/p&gt;
&lt;p&gt;The good news is that it no longer goes into an infinite loop, the bad news are that now some tests are actually failing.&lt;/p&gt;
&lt;p&gt;We pass the unit test, and the first sanity check:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;LazyFrameBuggySuite:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+ Traces the correct spiral 0.016s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+ Sanity: all points in bounds 0.033s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That unit test seems awfully resilient. But then we fail everything else.&lt;/p&gt;
&lt;p&gt;Somehow we omit points from the solution&lt;sup&gt;&lt;a href=&quot;#user-content-fn-diffoutput&quot; id=&quot;user-content-fnref-diffoutput&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;32&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;==&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;X&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LazyFrameBuggySuite&lt;/span&gt;&lt;span&gt;. &lt;/span&gt;&lt;span&gt;Sanity&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; dimensions &lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt; number of points  &lt;/span&gt;&lt;span&gt;0.023&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;43&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;44&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;      points.size &lt;/span&gt;&lt;span&gt;====&lt;/span&gt;&lt;span&gt; dims.area&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;45&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LazyFrameBuggySuite&lt;/span&gt;&lt;span&gt;. &lt;/span&gt;&lt;span&gt;Sanity&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; dimensions &lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt; number of &lt;/span&gt;&lt;span&gt;points&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Falsified&lt;/span&gt;&lt;span&gt; after &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt; passed tests&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Not&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Equal&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;span&gt; lhs &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;span&gt; rhs &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We also duplicate points, something that never happened before:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;==&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;X&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LazyFrameBuggySuite&lt;/span&gt;&lt;span&gt;. &lt;/span&gt;&lt;span&gt;Sanity&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; no duplicate points  &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;.003s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;49&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;50&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;      points.distinct &lt;/span&gt;&lt;span&gt;====&lt;/span&gt;&lt;span&gt; points&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LazyFrameBuggySuite&lt;/span&gt;&lt;span&gt;. &lt;/span&gt;&lt;span&gt;Sanity&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; no duplicate &lt;/span&gt;&lt;span&gt;points&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Falsified&lt;/span&gt;&lt;span&gt; after &lt;/span&gt;&lt;span&gt;13&lt;/span&gt;&lt;span&gt; passed tests&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Not&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Equal&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;span&gt; lhs &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;span&gt; rhs &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since we don&apos;t even pass the simple sanity checks, we obviously fail to match the reference solution:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;==&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;X&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LazyFrameBuggySuite&lt;/span&gt;&lt;span&gt;. &lt;/span&gt;&lt;span&gt;Matches&lt;/span&gt;&lt;span&gt; reference  &lt;/span&gt;&lt;span&gt;0.009&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;13&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;14&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;      actual &lt;/span&gt;&lt;span&gt;====&lt;/span&gt;&lt;span&gt; expected&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LazyFrameBuggySuite&lt;/span&gt;&lt;span&gt;. &lt;/span&gt;&lt;span&gt;Matches&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;reference&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Falsified&lt;/span&gt;&lt;span&gt; after &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt; passed tests&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Not&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Equal&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;span&gt; lhs &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;span&gt; rhs &lt;/span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Not for the first time, our comprehensive test suite is paying dividends. Something non-trivial is wrong here.&lt;/p&gt;
&lt;p&gt;Of the sanity checks the duplicate points looks like the most informative one. Where can we get a duplicate point?&lt;/p&gt;
&lt;p&gt;Since the output is small (but not too small, so it&apos;s &quot;interesting&quot;), we can trace the code and see what went wrong. The board we are dealing with is:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,0) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,1) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;| (0,2) |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The board is not empty, so the first non-trivial step is to take the frame of the board with &lt;code&gt;getFrame&lt;/code&gt;. Hmm... What is the frame of a single column?&lt;/p&gt;
&lt;p&gt;Clearly it can&apos;t be this:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;path1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;&lt;span&gt;.range&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, w &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;path2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;&lt;span&gt;.range&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, h &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;w &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;, _&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;path3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;&lt;span&gt;.range&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;w &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, step &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_, h &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;path4&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;&lt;span&gt;.range&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;h &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, step &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;, _&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;frame&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;path1 &lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt; path2 &lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt; path3 &lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt; path4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This implies that we have four sides to the frame, but on a column-only (or row-only) board we don&apos;t actually have four sides. This also explains why we have duplicate points. With a single column there&apos;s an overlapping contribution from &lt;code&gt;path2&lt;/code&gt; and &lt;code&gt;path4&lt;/code&gt; (and similarly for a single row).&lt;/p&gt;
&lt;p&gt;And so, it appears that any board that ends up with a single row or column after peeling enough frames is an edge case in this new solution. We were unlucky that the unit test didn&apos;t catch it. But that&apos;s the nature of unit tests, you&apos;re either lucky, or you miss things. With a solid property-based suite, we can take most of the luck out of the equation&lt;sup&gt;&lt;a href=&quot;#user-content-fn-prob&quot; id=&quot;user-content-fnref-prob&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;33&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Now that we isolated the issue, the fix is fairly straightforward, the frame of single row/column is just that row/column:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;frame&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; dims &lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;w, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;&lt;span&gt;.range&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;, w&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;, h&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;&lt;span&gt;.range&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;, h&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;, _&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;w, h&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;path1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;&lt;span&gt;.range&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, w &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;path2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;&lt;span&gt;.range&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, h &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;w &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;, _&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;path3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;&lt;span&gt;.range&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;w &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, step &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_, h &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;path4&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Iterator&lt;/span&gt;&lt;span&gt;&lt;span&gt;.range&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;h &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, step &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;, _&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;path1 &lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt; path2 &lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt; path3 &lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt; path4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the code for the frame calculation, with two special cases, for a single row (1) and a single column (2). In both cases we just return the corresponding row/column. The last case (3) is the same as the old code.&lt;/p&gt;
&lt;p&gt;Are we there yet?&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;LazyFrameSuite:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+ Traces the correct spiral 0.019s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+ Sanity: all points in bounds 0.036s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+ Sanity: dimensions match number of points 0.012s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+ Sanity: no duplicate points 0.013s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+ Matches reference 0.034s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yep! We pass our test suite. And so we now have a solution that&apos;s both lazy and (despite the edge cases) has what I think of as simpler code, without all the complication of rotations&lt;sup&gt;&lt;a href=&quot;#user-content-fn-negboard&quot; id=&quot;user-content-fnref-negboard&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;34&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;That&apos;s when the product manager steps in with new feature demands...&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;1288&quot; height=&quot;859&quot; src=&quot;https://blog.daniel-beskin.com/_astro/kramer-entrance.C7-xMYSa_1nw1p.webp&quot;&gt;
&lt;h2 id=&quot;spiral-tracing-as-a-service&quot;&gt;Spiral Tracing as a Service&lt;/h2&gt;
&lt;p&gt;Our clients want to get specific points on the spiral on demand. They might say &quot;give me point number 233 on a spiral on a 567x665 board&quot; and our Spiral API™ should quickly produce a result&lt;sup&gt;&lt;a href=&quot;#user-content-fn-realistic&quot; id=&quot;user-content-fnref-realistic&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;35&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Since our clients only ask for a single point on the board, and care about low latency, it would be wasteful to compute the whole spiral. What we should do instead is do the minimal amount of computation to get that one point.&lt;/p&gt;
&lt;p&gt;Unfortunately, I&apos;m not smart enough to come up with a closed-form formula taking an index to its location on the board&apos;s spiral&lt;sup&gt;&lt;a href=&quot;#user-content-fn-closedform&quot; id=&quot;user-content-fnref-closedform&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;36&lt;/a&gt;&lt;/sup&gt;. So all I can do instead is to fetch the point without materializing all intermediate points, and skipping as many steps as I can.&lt;/p&gt;
&lt;p&gt;The new function that we&apos;ll &lt;a href=&quot;https://github.com/ncreep/first-make-it-correct-blog/blob/master/6.Computational.scala&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;implement&lt;/a&gt; is this:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;findPointAt&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Option&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Given an index and dimensions, we compute the point that matches the index (and &lt;code&gt;None&lt;/code&gt; when out of bounds). More precisely, ignoring the efficiency requirements we could define:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;findPointAt&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Option&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Reference&lt;/span&gt;&lt;span&gt;&lt;span&gt;.traceSpiral&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.lift&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This solution will first build the whole spiral with &lt;code&gt;traceSpiral&lt;/code&gt; and access the element at &lt;code&gt;index&lt;/code&gt;. &lt;code&gt;lift&lt;/code&gt; turns indexing into a safe, &lt;code&gt;Option&lt;/code&gt;-returning operation. Logically, this is correct, but we want something faster, that doesn&apos;t go over all the intermediate points, nor should it go to points beyond &lt;code&gt;index&lt;/code&gt;. We&apos;ll call this new approach &quot;computational&quot;.&lt;/p&gt;
&lt;p&gt;For inspiration we can use the frame solution, as it has all the ingredients we need.&lt;/p&gt;
&lt;p&gt;Recall that we are moving along frames of the board. We do that by skipping one frame at a time, and each frame starts on the next point on the main diagonal of the board:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;|  0  |     |     |     |     |     |     |     |     |     |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;|     |  1  |     |     |     |     |     |     |     |     |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;|     |     |  2  |     |     |     |     |     |     |     |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;|     |     |     |  3  |     |     |     |     |     |     |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;|     |     |     |     |     |     |     |     |     |     |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;|     |     |     |     |     |     |     |     |     |     |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;|     |     |     |     |     |     |     |     |     |     |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;|     |     |     |     |     |     |     |     |     |     |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first frame is on the 0 offset, the second on 1, and so on. Each frame corresponds to a range of indices on the spiral. For example, on the board above, the frame on 0 corresponds to indices 0 to 31 (inclusive), the frame on 1 corresponds to indices 32 to 55 (inclusive). In the frame solution we managed to compute all the points of a given frame. By massaging that solution a bit, we can compute a specific index on a frame.&lt;/p&gt;
&lt;p&gt;From this we can split up the problem into two parts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Find the frame that contains the index we are looking for&lt;/li&gt;
&lt;li&gt;Compute the point on the frame that matches the index&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This way we are not wasting our time going around the frames, we just skip directly to the frame we need, and do some computation over it. Yielding a speed up over the original solution (or even the lazy one).&lt;/p&gt;
&lt;p&gt;Let&apos;s code this up. As we had quite enough recursion for one post, let&apos;s go back to the procedural/iterative approach, using the recursive frame solution as inspiration (and remembering how to convert recursion to loops from the previous parts). We shall leave behind any pretense for functional elegance.&lt;/p&gt;
&lt;p&gt;The first question about the algorithm above, is what are we iterating over? That&apos;s the shifts of the board (which correspond to frames), they start from 0 and keep growing, until we find our index.&lt;/p&gt;
&lt;p&gt;The next question, is what do we do on every step of iteration? We need to check where the index is relative to the current frame. We have two possible cases, either the index is after the current frame, or within it. I said the word &quot;case&quot;, so let&apos;s have this coded up as a datatype&lt;sup&gt;&lt;a href=&quot;#user-content-fn-pretense&quot; id=&quot;user-content-fnref-pretense&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;37&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;enum&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Framing&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;After&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Within&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;frame&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;FrameData&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first case is self-evident (1). But why does the second case (2) have an argument? In the spirit of the great &quot;parse don&apos;t validate&quot; &lt;a href=&quot;https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;post&lt;/a&gt;, we (retroactively) predict that the point in the code where we discover that we are within the frame is also the location where we have all the data necessary to compute the result we are after. The &lt;code&gt;Within&lt;/code&gt; constructor holds on to that data so that it can be used later on and not recomputed. The exact contents of &lt;code&gt;FrameData&lt;/code&gt; will be specified later on.&lt;/p&gt;
&lt;p&gt;For now, let&apos;s pretend that we have a function to compute the current &lt;code&gt;Framing&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getFraming&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;shift&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Framing&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;getFraming&lt;/code&gt; takes the index we are looking for, a shift on the board, and the dimensions of the board, it then computes the &lt;code&gt;Framing&lt;/code&gt; of the &lt;code&gt;index&lt;/code&gt; at the given &lt;code&gt;shift&lt;/code&gt;. We&apos;ll implement this function in a bit.&lt;/p&gt;
&lt;p&gt;The last thing we&apos;ll need before we proceed with the main loop is a way to convert the frame we found into a specific point that corresponds to the index we are searching for. Let us again pretend that we already have a function for that:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getPointOnFrame&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;frame&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;FrameData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Given an index and a &lt;code&gt;FrameData&lt;/code&gt; instance, we compute the corresponding &lt;code&gt;Point&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Cool, with these ingredients in place we can implement the main loop:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;findPointAt&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Option&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; index &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; index &lt;/span&gt;&lt;span&gt;&gt;=&lt;/span&gt;&lt;span&gt; dims.area &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;None&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Option&lt;/span&gt;&lt;span&gt;&lt;span&gt;.empty&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;shift&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;while&lt;/span&gt;&lt;span&gt; result.isEmpty &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;getFraming&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;index &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; index, shift &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; shift, dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Framing&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;After&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; shift &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 6&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Framing&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Within&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;point&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 7&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;point&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; getPointOnFrame&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;index, frame&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 8&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;result &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Some&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;point&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 9&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;end while&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;result &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 10&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The main loop has the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We verify that the index we got is not out of bounds (1), if it is, we return &lt;code&gt;None&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-canskip&quot; id=&quot;user-content-fnref-canskip&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;38&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;The variable &lt;code&gt;result&lt;/code&gt; starts out as an empty value (2), to be set to the correct point later on&lt;/li&gt;
&lt;li&gt;The initial shift is &lt;code&gt;0&lt;/code&gt; (3), starting out at the top-left corner of the board, corresponding to the outermost frame&lt;/li&gt;
&lt;li&gt;We set the condition for the loop (4), we iterate until a solution is found, that is, until &lt;code&gt;result&lt;/code&gt; stops being empty&lt;/li&gt;
&lt;li&gt;On each step of the loop, we check the framing for the index on the current shift (5)&lt;/li&gt;
&lt;li&gt;If we aren&apos;t on the frame we are looking for (6), we increment the &lt;code&gt;shift&lt;/code&gt; and move on&lt;/li&gt;
&lt;li&gt;If we are within the correct frame (7), we also have the &lt;code&gt;FrameData&lt;/code&gt; for it which we can use&lt;/li&gt;
&lt;li&gt;We use the &lt;code&gt;FrameData&lt;/code&gt; instance to convert the &lt;code&gt;index&lt;/code&gt; into its &lt;code&gt;Point&lt;/code&gt; (8)&lt;/li&gt;
&lt;li&gt;Which we then assign to the &lt;code&gt;result&lt;/code&gt; (9)&lt;/li&gt;
&lt;li&gt;Once the loop is complete, &lt;code&gt;result&lt;/code&gt; contains the correct &lt;code&gt;Point&lt;/code&gt;, which we return (10)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Easy. All we have to do now is implement &lt;code&gt;getFraming&lt;/code&gt; and &lt;code&gt;getPointOnFrame&lt;/code&gt;. How hard could that be...&lt;/p&gt;
&lt;p&gt;Recall how we moved around in the frame solution. We would increment the shift by 1, and then reduce the dimensions by 2, effectively working on an ever decreasing inner board.&lt;/p&gt;
&lt;p&gt;Schematically, for &lt;code&gt;shift == 2&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;|  x  |  x  |  x  |  x  |  x  |  x  |  x  |  x  |  x  |  x  |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;|  x  |  x  |  x  |  x  |  x  |  x  |  x  |  x  |  x  |  x  |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;|  x  |  x  |  2  |     |     |  i  |     |     |  x  |  x  |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;|  x  |  x  |     |     |  j  |     |     |     |  x  |  x  |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;|  x  |  x  |     |     |     |     |     |     |  x  |  x  |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;|  x  |  x  |     |     |     |     |     |     |  x  |  x  |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;|  x  |  x  |  x  |  x  |  x  |  x  |  x  |  x  |  x  |  x  |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;|  x  |  x  |  x  |  x  |  x  |  x  |  x  |  x  |  x  |  x  |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;-------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;x&lt;/code&gt;s mark the outer part of the board that we already passed. The empty squares mark the inner board, with its corner on shift number &lt;code&gt;2&lt;/code&gt;. &lt;code&gt;i&lt;/code&gt; and &lt;code&gt;j&lt;/code&gt; mark a couple of indices we might be looking for.&lt;/p&gt;
&lt;p&gt;What does it tell us about the current framing? Where is &lt;code&gt;2&lt;/code&gt; located relative to &lt;code&gt;i&lt;/code&gt; or &lt;code&gt;j&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;From the drawing we can compute the index that corresponds to &lt;code&gt;2&lt;/code&gt;: it&apos;s just the area of the &quot;outer board&quot; marked by &lt;code&gt;x&lt;/code&gt;. &lt;code&gt;i&lt;/code&gt; is on the frame of &lt;code&gt;2&lt;/code&gt;, we can see that by computing the size of the frame of the inner board and adding it to the index of &lt;code&gt;2&lt;/code&gt; and comparing that number to &lt;code&gt;i&lt;/code&gt;. We can do the same thing for &lt;code&gt;j&lt;/code&gt; to see that it&apos;s on another frame, after &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So to compute the framing we need to compute the area of the outer board, and the size of the current frame. With this insight, let&apos;s implement &lt;code&gt;getFraming&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getFraming&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;shift&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Framing&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;totalArea&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; dims.area &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;innerDims&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; dims.reduceBy&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;&lt;span&gt; shift&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;frame &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; innerFrame, area &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; innerArea&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;frameAndArea&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;innerDims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;outerArea&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; totalArea &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; innerArea &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; index &lt;/span&gt;&lt;span&gt;&gt;=&lt;/span&gt;&lt;span&gt; outerArea &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; innerFrame &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Framing&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;After&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;frame&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FrameData&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;outerArea, shift, innerDims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 6&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Framing&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Within&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;frame&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 7&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We start by getting the basic measurement of all the parts involved:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The total area of the full board (1)&lt;/li&gt;
&lt;li&gt;The dimensions of the inner board (2), which we obtain by reducing the original &lt;code&gt;Dims&lt;/code&gt; by double the &lt;code&gt;shift&lt;/code&gt; number, as implied by the recursive frame solution&lt;/li&gt;
&lt;li&gt;We then call &lt;code&gt;frameAndArea&lt;/code&gt; on &lt;code&gt;innerDims&lt;/code&gt; (3) to obtain its frame size and area in a tuple (which we intermediately deconstruct)&lt;/li&gt;
&lt;li&gt;By knowing the &lt;code&gt;innerArea&lt;/code&gt; and the &lt;code&gt;totalArea&lt;/code&gt; we can compute the &lt;code&gt;outerArea&lt;/code&gt; (4), which is just subtracting one from the other&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now we can figure out where the index is relative to the current frame:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If it&apos;s outside the outer area and the current frame, we return &lt;code&gt;Framing.After&lt;/code&gt; (5)&lt;/li&gt;
&lt;li&gt;Otherwise, we collect info about the frame into a &lt;code&gt;FrameData&lt;/code&gt; instance (6)&lt;/li&gt;
&lt;li&gt;And return a &lt;code&gt;Frame.Within&lt;/code&gt; with that data (7)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For completeness, here&apos;s the definition of &lt;code&gt;FrameData&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FrameData&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;outerArea&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;shift&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In &lt;code&gt;FrameData&lt;/code&gt; we hold on to the area of the outer board, the shift that got us there, and the dimensions of the (smaller) inner board. We&apos;ll see later on how to use this info to compute a point on the frame.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;frameAndArea&lt;/code&gt; should&apos;ve been relatively simple, but it suffers from the same edge cases like we had in the lazy frame solution. Lucky that we have that solution to learn lessons from. Accounting for this we get:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;frameAndArea&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;frame&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;area&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;dims &lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; _ &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; dims.w &lt;/span&gt;&lt;span&gt;&amp;#x3C;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; dims.h &lt;/span&gt;&lt;span&gt;&amp;#x3C;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;frame &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, area &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;, h&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;frame &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; h, area &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; h&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;w, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;frame &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; w, area &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; w&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;w, h&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;frame &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims.w &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; dims.h &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, area &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; w &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;&lt;span&gt; h&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 6&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function returns a named tuple, with one field for the frame size, and one for the area (1). We do this by matching on the &lt;code&gt;Dims&lt;/code&gt; we got. The first three case (3, 4, 5) are exactly the edge cases we seen before: negative dimensions and row/column-only boards. The last case (6) is for a full size board. If you&apos;re unnerved by all the random numbers here, or by the fear of off-by-one errors. Fret not, we still have the reference solution to back us up with its test suite (&lt;a href=&quot;https://github.com/ncreep/first-make-it-correct-blog/blob/master/6.Computational.scala#L7C7-L7C18&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;adapted&lt;/a&gt; so as to call &lt;code&gt;findPointAt&lt;/code&gt; for all points of the board). So I fearlessly trial-and-errored this bit of code until it worked out. Also, fair warning, we are going to have much scarier &quot;indexology&quot; coming up very soon.&lt;/p&gt;
&lt;p&gt;We are almost done, now that we know where we are relative to the current frame, we need to implement &lt;code&gt;getPointOnFrame&lt;/code&gt; to be able to convert the &lt;code&gt;Frame.Within&lt;/code&gt; into a concrete point. But first, let&apos;s solve a slightly simpler problem.&lt;/p&gt;
&lt;p&gt;Recall that we have the &quot;inner board&quot;, and the frame we found is its outer frame. If we take the perspective of the inner board (similar to how recursion worked in the previous solution), we can implement the following: given an index that is located on the outer frame of a board, find the point that corresponds to it. Once we have that, we&apos;ll need to convert the &lt;code&gt;index&lt;/code&gt; into a corresponding index on the inner board, get the point, and then shift it back to the full board.&lt;/p&gt;
&lt;p&gt;Be warned, the following helper is not for the faint of heart:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getPointOnFrameInner&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;dims &lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;w, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;index, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;, h&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;, index&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;w, h&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;index &lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 6&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; _ &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; index &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; w &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;index, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; _ &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; index &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; w &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; h &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;w &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, index &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; w &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; _ &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; index &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; w &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; h &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; w &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;w &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;index &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; w &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; h &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, h &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; _ &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;h &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;index &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; w &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; h &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; w &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&apos;s break it down:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This is helper for &lt;code&gt;getPointOnFrame&lt;/code&gt;, written from the perspective of the inner board (1)&lt;/li&gt;
&lt;li&gt;The arguments &lt;code&gt;index&lt;/code&gt; and &lt;code&gt;dims&lt;/code&gt; refer to the inner board, not necessarily the originals we got in &lt;code&gt;findPointAt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;We match on &lt;code&gt;dims&lt;/code&gt;, yet again, to suss out the edge cases (2)&lt;/li&gt;
&lt;li&gt;We don&apos;t need to worry about empty/negative boards here, as we only reach this code when the location was found, which cannot happen on an empty/negative board&lt;/li&gt;
&lt;li&gt;For single row/column boards (3, 4), the point is just the &lt;code&gt;index&lt;/code&gt; itself (placed horizontally or vertically)&lt;/li&gt;
&lt;li&gt;For a full board (5), we match on the index&lt;sup&gt;&lt;a href=&quot;#user-content-fn-multiway&quot; id=&quot;user-content-fnref-multiway&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;39&lt;/a&gt;&lt;/sup&gt; (6)&lt;/li&gt;
&lt;li&gt;Now comes the scary part, where we have to figure out the &lt;code&gt;Point&lt;/code&gt; we are on based on the relative values of &lt;code&gt;index&lt;/code&gt;, &lt;code&gt;w&lt;/code&gt;, and &lt;code&gt;h&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Each branch corresponds to one side of the frame, like we had in the original frame solution&lt;/li&gt;
&lt;li&gt;I won&apos;t even try to explain how I got these specific numeric values, suffice it to say that I still have nightmares&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Never in a million years would I have gotten those values correct without a reference solution to keep me honest. The sheer number of back and forth off-by-one errors is more than I&apos;m willing to admit. Unlike the other places before, one of the edge cases here was the surprisingly unintuitive 2x2 board. I had to play around with the values until the tests passed, and since we have property-based tests I&apos;m fairly confident that I didn&apos;t miss any other weird edge cases.&lt;/p&gt;
&lt;p&gt;How far we&apos;ve come from the minimal, elegant, edge case free reference solution...&lt;/p&gt;
&lt;p&gt;Anyways, implementing &lt;code&gt;getPointOnFrame&lt;/code&gt; is now just a matter of shifting perspectives (pun intended) back and forth:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getPointOnFrame&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;frame&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;FrameData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;innerIndex&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; index &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; frame.outerArea &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;point&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; getPointOnFrameInner&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;innerIndex, frame.dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;shiftedPoint&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;point.shiftBy&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dx &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; frame.shift, dy &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; frame.shift&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;shiftedPoint &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To get the index as viewed from the inner board, we take the original index and subtract the outer area from it (1). This shifts the index to start from the top left corner of the inner board. We then call &lt;code&gt;getPointOnFrameInner&lt;/code&gt; (2) with &lt;code&gt;innerIndex&lt;/code&gt; and the dimensions of the inner board (coming from the &lt;code&gt;FrameData&lt;/code&gt;). With the &lt;code&gt;Point&lt;/code&gt; in hand, we must move it back to the full board&apos;s perspective. We do that by shifting the point (3) with the shift the frame is on. And this is what we return (4).&lt;/p&gt;
&lt;p&gt;That wasn&apos;t that hard, was it? Was it?..&lt;/p&gt;
&lt;p&gt;Since I wasted what feels like years of my life running the test suite on this solution till I got it right, I&apos;m fairly certain we can say that having a reference solution was justified. We shall pause for a moment an appreciate the distance we made, starting from the lovely reference and the monster we have now. I wouldn&apos;t&apos;ve made it this far without the experience and intuition that I gained from the previous correct solutions.&lt;/p&gt;
&lt;p&gt;Are we done yet?&lt;/p&gt;
&lt;h2 id=&quot;and-then-make-it-fast&quot;&gt;...And Then Make It Fast&lt;/h2&gt;
&lt;p&gt;Time to wow our interviewer&lt;sup&gt;&lt;a href=&quot;#user-content-fn-stillwith&quot; id=&quot;user-content-fnref-stillwith&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;40&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;It&apos;s true that our computational solution is asymptotically faster than the naive implementation with the reference. It&apos;s linear in &lt;code&gt;max(w, h)&lt;/code&gt;, while the reference is &lt;code&gt;O(w * h)&lt;/code&gt;. But when was any interviewer impressed with anything linear? Can we do better?&lt;/p&gt;
&lt;p&gt;Turns out we can. Taking out our dusty CS books we notice that what we are doing here is searching: searching for a frame that matches a certain criteria. Not only that, but the things we are searching over are sorted: the group of indices in each frame are strictly monotonically increasing. This is almost a textbook case for a binary search. Who could&apos;ve thunk that I&apos;ll ever be implementing binary search outside of school...&lt;/p&gt;
&lt;p&gt;So instead of going from the 0 shift upwards, we can start from the middle, and go up or down depending on where our index is relative to the current frame. This requires only slight modifications to our computational solution.&lt;/p&gt;
&lt;p&gt;For a binary search we need a lower and an upper bound. The lower bound is easy, that&apos;s just the 0 shift. But what is the upper one? Looking at the boards we have above we see that we only need to go to about the middle of the board. We&apos;ll use that as our upper bound.&lt;/p&gt;
&lt;p&gt;Another thing we need to take care of is the &lt;code&gt;Framing&lt;/code&gt; data type. When going from 0 upwards we only had two cases. Either the index was after the current frame or within it. But in a binary search we can go back and forth, so it might happen that index is before the current frame:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;enum&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Framing&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Before&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;After&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Within&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;frame&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;FrameData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We just added another simple case here (1).&lt;/p&gt;
&lt;p&gt;The main loop for a binary search becomes the following:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;findPointAt&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;dims&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Option&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;maxShift&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;math.min&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dims.w, dims.h&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Option&lt;/span&gt;&lt;span&gt;&lt;span&gt;.empty&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Point&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;min&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; maxShift &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;while&lt;/span&gt;&lt;span&gt; result.isEmpty &lt;/span&gt;&lt;span&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; min &lt;/span&gt;&lt;span&gt;&amp;#x3C;=&lt;/span&gt;&lt;span&gt; max &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;shift&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; min &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;max &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; min&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;getFraming&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;index &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; index, shift &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; shift, dims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 6&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Framing&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Before&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; max &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; shift &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 7&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Framing&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;After&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; min &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; shift &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 8&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Framing&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Within&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;frame&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 9&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;point&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; getPointOnFrame&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;index, frame&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;result &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Some&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;point&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;end while&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is still pretty similar to what we had before:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We initialize the &lt;code&gt;maxShift&lt;/code&gt; (2) to the middle of the board, as per usual, the right numbers were found by random trial-and-error backed by tests&lt;/li&gt;
&lt;li&gt;Two new variables are needed to keep track of the process:
&lt;ul&gt;
&lt;li&gt;The current lower bound (2) that starts at the 0 shift&lt;/li&gt;
&lt;li&gt;The current upper bound (3) that starts at &lt;code&gt;maxShift&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;We are no longer checking that &lt;code&gt;index&lt;/code&gt; is within the correct bounds, instead relying on the stopping condition of the loop (4)&lt;/li&gt;
&lt;li&gt;We stop the loop when either the result stopped being empty, or the lower bound went beyond the upper bound (meaning that the index is not on the board)&lt;/li&gt;
&lt;li&gt;Inside the loop we set the &lt;code&gt;shift&lt;/code&gt; to be in the middle between &lt;code&gt;min&lt;/code&gt; and &lt;code&gt;max&lt;/code&gt; (5)&lt;/li&gt;
&lt;li&gt;With the current &lt;code&gt;shift&lt;/code&gt; we can get the &lt;code&gt;Framing&lt;/code&gt; for it (6), but now we deal with one of three outcomes:
&lt;ul&gt;
&lt;li&gt;The index is before the current frame (7), we have to go backwards, so &lt;code&gt;max&lt;/code&gt; is brought down to the current shift&lt;/li&gt;
&lt;li&gt;The index is after the current frame (8), we have to go forwards, so &lt;code&gt;min&lt;/code&gt; is brought up to the current shift&lt;/li&gt;
&lt;li&gt;We are within the frame, in which we proceed the same way as before (9)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To finish this, we need to adapt &lt;code&gt;getFraming&lt;/code&gt; as well. We just add another branch to the conditional logic there:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; index &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; outerArea &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Framing&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Before&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; index &lt;/span&gt;&lt;span&gt;&gt;=&lt;/span&gt;&lt;span&gt; outerArea &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; innerFrame &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Framing&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;After&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;frame&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FrameData&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;outerArea, shift, innerDims&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Framing&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Within&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;frame&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;Frame.Before&lt;/code&gt; case is produced when the index is in the &quot;outer board&quot; part (1). The rest remains the same.&lt;/p&gt;
&lt;p&gt;Finally! We are done. This solution is logarithmic in &lt;code&gt;max(w, h)&lt;/code&gt;, way better than what we had before&lt;sup&gt;&lt;a href=&quot;#user-content-fn-logs&quot; id=&quot;user-content-fnref-logs&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;41&lt;/a&gt;&lt;/sup&gt;. Both our clients and our interviewer will be thrilled by the snappy latency that the Spiral API™ can now provide.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Woah, that was quite a journey!&lt;/p&gt;
&lt;p&gt;We started out by laying down a solid foundation with an (obviously) correct solution, backed by a property-based test suite. From there we used the initial correct solution both as reference to test against, and as an inspiration to help us think about the problem we are solving.&lt;/p&gt;
&lt;p&gt;Guided by various requirements and improvements, we were able to both mechanically adapt the solution, and to use it as a guide and safeguard for more creative approaches. Yielding a diverse collection of solutions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A &lt;a href=&quot;https://github.com/ncreep/first-make-it-correct-blog/blob/master/1.Reference.scala&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;simple recursive solution&lt;/a&gt;, closely following the problem statement&lt;/li&gt;
&lt;li&gt;A &lt;a href=&quot;https://github.com/ncreep/first-make-it-correct-blog/blob/master/2.TailRecursive.scala&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;tail recursive solution&lt;/a&gt;, to avoid stack overflows; by a mechanical transformation of the original correct solution&lt;/li&gt;
&lt;li&gt;An &lt;a href=&quot;https://github.com/ncreep/first-make-it-correct-blog/blob/master/3.Iterative.scala&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;iterative solution&lt;/a&gt;, to accommodate languages that don&apos;t support tail-call elimination; by mechanical transformation&lt;/li&gt;
&lt;li&gt;A &lt;a href=&quot;https://github.com/ncreep/first-make-it-correct-blog/blob/master/4.Lazy.scala&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;lazy solution&lt;/a&gt;, to avoid blowing out the memory; by using the original solution to inspire an interface to code to&lt;/li&gt;
&lt;li&gt;A &lt;a href=&quot;https://github.com/ncreep/first-make-it-correct-blog/blob/master/5.LazyFrame.scala&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;lazy frame by frame solution&lt;/a&gt;, to avoid the pain of dealing with rotations; by following the pain and using an existing solution for visualization&lt;/li&gt;
&lt;li&gt;A &lt;a href=&quot;https://github.com/ncreep/first-make-it-correct-blog/blob/master/6.Computational.scala&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;computational solution&lt;/a&gt;, to avoid unnecessary computation when adapting to new requirements; by heavily relying on the reference property-based test suite to weed out edge cases&lt;/li&gt;
&lt;li&gt;An &lt;a href=&quot;https://github.com/ncreep/first-make-it-correct-blog/blob/master/7.Fast.scala&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;even faster solution&lt;/a&gt;, just because we can and because we wanted to impress the hypothetical interviewer; by throwing a CS book at an existing solution&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Starting out with a correct solution gave us clarity of thought about the problem, and a tangible way to test any other solutions that we can come up with. Property-based testing allowed us to maintain confidence even as we tried more and more daring approaches. Without the initial correct solution, none of this would be possible.&lt;/p&gt;
&lt;p&gt;And so we circle back to my original statement:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;First make it correct, then make it anything you want.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Did I mention that I hate leetcode problems?&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-attorney&quot;&gt;
&lt;p&gt;I would like to consult with my attorney first. &lt;a href=&quot;#user-content-fnref-attorney&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-nottoodifficult&quot;&gt;
&lt;p&gt;I.e., not too difficult. &lt;a href=&quot;#user-content-fnref-nottoodifficult&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-toomuchtime&quot;&gt;
&lt;p&gt;I&apos;ve spent way too much time writing the code for this &quot;visualization&quot;... &lt;a href=&quot;#user-content-fnref-toomuchtime&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-ordont&quot;&gt;
&lt;p&gt;Or don&apos;t, I don&apos;t really care. &lt;a href=&quot;#user-content-fnref-ordont&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-stillfriend&quot;&gt;
&lt;p&gt;Is he still considered a friend after that? &lt;a href=&quot;#user-content-fnref-stillfriend&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-github&quot;&gt;
&lt;p&gt;The code for this post is available on &lt;a href=&quot;https://github.com/ncreep/first-make-it-correct-blog&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-github&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 6&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-firstwrong&quot;&gt;
&lt;p&gt;In case you get the directions wrong, which I obviously did on my first attempt. &lt;a href=&quot;#user-content-fnref-firstwrong&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 7&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-othersolutions&quot;&gt;
&lt;p&gt;If you can come up with an even more obviously correct solution, please reach out and let me know. &lt;a href=&quot;#user-content-fnref-othersolutions&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 8&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-veryequal&quot;&gt;
&lt;p&gt;The &lt;code&gt;====&lt;/code&gt; operator stands for &quot;very much indeed equals&quot;. I guess the other &lt;code&gt;=&lt;/code&gt; variants were already taken... &lt;a href=&quot;#user-content-fnref-veryequal&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 9&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-bounds&quot;&gt;
&lt;p&gt;In theory we can make it unbounded, but for the tests not to take too long, the specific generator I defined creates dimensions with a linear size of up to 30. &lt;a href=&quot;#user-content-fnref-bounds&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 10&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-initconditions&quot;&gt;
&lt;p&gt;Kind of like solving differential equations. The unit test is like the initial condition, and the properties are like the smooth function that acts as a solution. &lt;a href=&quot;#user-content-fnref-initconditions&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 11&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-keepsanity&quot;&gt;
&lt;p&gt;But we will still continue to use the sanity properties that we defined above, as they will be easier to use as debugging tools in case a candidate solution has a bug. &lt;a href=&quot;#user-content-fnref-keepsanity&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 12&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-jvmparams&quot;&gt;
&lt;p&gt;Of course the specific numbers depend on the JVM parameters that we use. We could reduce the size of the stack on purpose and get a stack overflow first. &lt;a href=&quot;#user-content-fnref-jvmparams&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 13&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-testflaws&quot;&gt;
&lt;p&gt;I leave it as an exercise to my dear readers to figure out how to test for these flaws. &lt;a href=&quot;#user-content-fnref-testflaws&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 14&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-shrinking&quot;&gt;
&lt;p&gt;In this post I&apos;m using the &lt;a href=&quot;https://hedgehogqa.github.io/scala-hedgehog/&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;Hedgehog&lt;/a&gt; library which does a good job at automatically maintaining shrinking even for custom types. If you&apos;re using something like &lt;a href=&quot;https://scalacheck.org/&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;ScalaCheck&lt;/a&gt;, you might need some extra code to achieve the same thing. &lt;a href=&quot;#user-content-fnref-shrinking&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 15&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-fold&quot;&gt;
&lt;p&gt;What we are experiencing here is basically the difference between a &lt;code&gt;foldRigt&lt;/code&gt; and a &lt;code&gt;foldLeft&lt;/code&gt;. As illustrated &lt;a href=&quot;https://en.wikipedia.org/wiki/Fold_(higher-order_function)#As_structural_transformations&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-fold&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 16&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-java&quot;&gt;
&lt;p&gt;Java, anyone? &lt;a href=&quot;#user-content-fnref-java&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 17&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-patternmatching&quot;&gt;
&lt;p&gt;We could still use pattern matching, but then we&apos;ll have to somehow ignore the empty case, as it is already handled by the loop condition. Probably leading to some other form of unsafe code. &lt;a href=&quot;#user-content-fnref-patternmatching&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 18&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-infinite&quot;&gt;
&lt;p&gt;For the life of me, I couldn&apos;t write a property that catches infinite loops... &lt;a href=&quot;#user-content-fnref-infinite&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 19&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-greed&quot;&gt;
&lt;p&gt;The other is being greedy. &lt;a href=&quot;#user-content-fnref-greed&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 20&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-visitater&quot;&gt;
&lt;p&gt;With a nod to &lt;a href=&quot;https://sites.google.com/site/steveyegge2/singleton-considered-stupid&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;Steve Yegge&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-visitater&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 21&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-streams&quot;&gt;
&lt;p&gt;You could use proper streams for what I&apos;m about to do, the resulting code will be very similar. &lt;a href=&quot;#user-content-fnref-streams&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 22&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-bewareiterator&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.daniel-beskin.com/2025-01-15-random-scala-tip-568-beware-of-leaking-iterators&quot;&gt;For better or for worse&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-bewareiterator&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 23&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-customextractor&quot;&gt;
&lt;p&gt;If we really wanted to preserve the pattern matching syntax from &lt;code&gt;List&lt;/code&gt; we could define a &lt;a href=&quot;https://docs.scala-lang.org/tour/extractor-objects.html&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;custom extractor&lt;/a&gt; to achieve this goal. &lt;a href=&quot;#user-content-fnref-customextractor&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 24&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-preciserep&quot;&gt;
&lt;p&gt;In the spirit of &quot;make illegal states unrepresentable&quot; we could come up with a more precise representation that won&apos;t allow us to express more than 4 rotations. Or at least make the constructor private to prevent the possibility of invalid values. For this small example it&apos;s probably not worth it. &lt;a href=&quot;#user-content-fnref-preciserep&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 25&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-delta&quot;&gt;
&lt;p&gt;This is an annoying case of &lt;a href=&quot;https://blog.daniel-beskin.com/2024-06-19-overloading&quot;&gt;overloading&lt;/a&gt;. The &lt;code&gt;Point&lt;/code&gt; type represents two different things: absolute points and relative deltas (shifts). We could resolve this by introducing a dedicated type for each use case, but the margin of this page is too small... There&apos;s some useful &lt;a href=&quot;https://hackage.haskell.org/package/acts&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;mathematics&lt;/a&gt; underlying this concept. &lt;a href=&quot;#user-content-fnref-delta&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 26&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-offbyone&quot;&gt;
&lt;p&gt;Guess who had some off by one errors here... &lt;a href=&quot;#user-content-fnref-offbyone&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 27&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-improvelazy&quot;&gt;
&lt;p&gt;Having obtained this solution we can improve on it. E.g., the interface we came up with is more flexible than actually required. We don&apos;t need separate &lt;code&gt;otherRows&lt;/code&gt; and &lt;code&gt;rotateCCW&lt;/code&gt; methods, we always use them together. By combining them into one we can skip some intermediate steps in the solution. &lt;a href=&quot;#user-content-fnref-improvelazy&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 28&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-spin&quot;&gt;
&lt;p&gt;Unless &lt;a href=&quot;https://en.wikipedia.org/wiki/Spinor&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;it is&lt;/a&gt;... &lt;a href=&quot;#user-content-fnref-spin&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 29&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-formfunction&quot;&gt;
&lt;p&gt;As an aside, why didn&apos;t we get to this solution in the original reference solution? One reason would be that the representation of the board that we used (a nested list of lists) is naturally suited for removing single rows (due to the way pattern matching on lists works), while removing a frame would require some coding effort. This is an example of &quot;function follows form&quot;. Choose your forms wisely... &lt;a href=&quot;#user-content-fnref-formfunction&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 30&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-testflag&quot;&gt;
&lt;p&gt;To see the output below I&apos;m using the &lt;code&gt;-b&lt;/code&gt; flag passed to the test runner, to avoid buffering output. &lt;a href=&quot;#user-content-fnref-testflag&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 31&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-diffoutput&quot;&gt;
&lt;p&gt;If you run the tests yourselves you might get different outputs, since minimization doesn&apos;t always reach the same cases. &lt;a href=&quot;#user-content-fnref-diffoutput&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 32&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-prob&quot;&gt;
&lt;p&gt;But not all of it, since the generators are still probabilistic. &lt;a href=&quot;#user-content-fnref-prob&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 33&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-negboard&quot;&gt;
&lt;p&gt;A &quot;fun&quot; riddle for the bored: suppose the board condition in &lt;code&gt;buildCoordinateFrom&lt;/code&gt; was (wrongly) set to &lt;code&gt;dims.area &amp;#x3C;= 0&lt;/code&gt;. What will happen? Will the test suite pass or fail? Why? &lt;a href=&quot;#user-content-fnref-negboard&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 34&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-realistic&quot;&gt;
&lt;p&gt;Who said that leetcode problems are not realistic enough to reflect the demands of a real job? &lt;a href=&quot;#user-content-fnref-realistic&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 35&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-closedform&quot;&gt;
&lt;p&gt;And if you come up with such a solution, please reach out and let me know. &lt;a href=&quot;#user-content-fnref-closedform&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 36&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-pretense&quot;&gt;
&lt;p&gt;Okay, so I haven&apos;t completely let go of functional pretense. And you could solve this without a custom datatype, but it will be more elegant this way. Goes to show that you can mix the procedural and declarative approaches. &lt;a href=&quot;#user-content-fnref-pretense&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 37&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-canskip&quot;&gt;
&lt;p&gt;We could skip this and just assume that the &lt;code&gt;index&lt;/code&gt; input is valid, just like we do with &lt;code&gt;dims&lt;/code&gt;. But I wanted this code to be compatible with the reference solution including the &lt;code&gt;lift&lt;/code&gt; bit, which handles out-of-bound indices. I also coded up this condition as a property-test that you can see in the &lt;a href=&quot;https://github.com/ncreep/first-make-it-correct-blog/blob/master/test/FindPointAtSuite.scala&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;test suite&lt;/a&gt; for this solution. &lt;a href=&quot;#user-content-fnref-canskip&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 38&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-multiway&quot;&gt;
&lt;p&gt;I&apos;m using pattern matching as a cheap substitute for a multiway-if, which is something I usually avoid. Here it will get too clumsy if do it with regular if/else expressions. &lt;a href=&quot;#user-content-fnref-multiway&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 39&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-stillwith&quot;&gt;
&lt;p&gt;Assuming said interviewer is still with us after all this time. &lt;a href=&quot;#user-content-fnref-stillwith&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 40&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-logs&quot;&gt;
&lt;p&gt;If you really want to take advantage of this speed up, you should probably stop using &lt;code&gt;Int&lt;/code&gt;s though... Also see the riddle from &lt;a href=&quot;#user-content-fn-negboard&quot;&gt;above&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-logs&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 41&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><author>Daniel Beskin</author></item><item><title>Random Scala Tip #624: The Pitfalls of Option Blindness</title><link>https://blog.daniel-beskin.com/2025-05-01-random-scala-tip-624-option-blindness</link><guid isPermaLink="true">https://blog.daniel-beskin.com/2025-05-01-random-scala-tip-624-option-blindness</guid><description>Before wrapping anything in `Option`, consider whether you should use a more meaningful domain-specific data type instead.</description><pubDate>Thu, 01 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://blog.daniel-beskin.com/social-cards/2025-05-01-random-scala-tip-624-option-blindness.png&quot; alt=&quot;Cover image for Random Scala Tip #624: The Pitfalls of Option Blindness&quot; style=&quot;width: 100%; height: auto; margin-bottom: 2rem;&quot;&gt;&lt;p&gt;Given this class:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;User&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserId&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;email&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Option&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Email&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;address&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Option&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Address&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Option&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;&lt;span&gt;]]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;lastLogin&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Option&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Timestamp&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What does &lt;code&gt;Option&lt;/code&gt; on each of the fields actually &lt;em&gt;means&lt;/em&gt;?&lt;/p&gt;
&lt;p&gt;Well, after spending hours digging in the code, documentation&lt;sup&gt;&lt;a href=&quot;#user-content-fn-unmaintained&quot; id=&quot;user-content-fnref-unmaintained&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, and use sites, I gleaned the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;email&lt;/code&gt; is actually mandatory, but that requirement was only added recently, so it was made optional for backwards compatibility with older clients&lt;/li&gt;
&lt;li&gt;&lt;code&gt;address&lt;/code&gt; is taken from user input, and it&apos;s not mandatory and can be missing&lt;/li&gt;
&lt;li&gt;&lt;code&gt;posts&lt;/code&gt; is fetched from a database, but some times the database is down, so we fallback to &lt;code&gt;None&lt;/code&gt;, which is distinct from &lt;code&gt;Some(Nil)&lt;/code&gt;, and should be handled appropriately&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lastLogin&lt;/code&gt; will be empty if the user never logged in previously, we need to provide a special greeting when a user logs in for the first time&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We have four distinct &lt;em&gt;meanings&lt;/em&gt; for &lt;code&gt;Option&lt;/code&gt;, but only one data type to represent them all. This phenomenon is called &quot;&lt;code&gt;Option&lt;/code&gt; blindness&quot;.&lt;/p&gt;
&lt;h2 id=&quot;differently-typed-blindnesses&quot;&gt;Differently Typed Blindnesses&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://existentialtype.wordpress.com/2011/03/15/boolean-blindness/&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;Boolean blindness&lt;/a&gt; is a well known phenomenon. It has a somewhat lesser known cousin called &lt;a href=&quot;https://github.com/quchen/articles/blob/14dc3eb40b08431e5ef96b4dc969bdc1b47c5035/algebraic-blindness.md&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;algebraic blindness&lt;/a&gt;. That&apos;s when we use very generic algebraic data types, like &lt;code&gt;Option&lt;/code&gt;, &lt;code&gt;Either&lt;/code&gt;, and &lt;code&gt;These/Ior&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-these&quot; id=&quot;user-content-fnref-these&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; to represent domain-specific data, and in doing so we miss that domain-specific context in our code.&lt;/p&gt;
&lt;p&gt;Here I&apos;m picking specifically on &lt;code&gt;Option&lt;/code&gt; because it&apos;s so common. As it&apos;s often touted as a solution to the &quot;null problem&quot;, it easy to think about it as an &quot;never wrong&quot; solution, even more so if you&apos;re new to functional programming. On top of that, &lt;code&gt;Option&lt;/code&gt; enjoys a privileged status in the wider Scala ecosystem. Especially in various serialization libraries, where it commonly gets special handling, making it all the more tempting to use it all over the place.&lt;/p&gt;
&lt;h2 id=&quot;the-tip&quot;&gt;The Tip&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Before wrapping anything in &lt;code&gt;Option&lt;/code&gt;, consider whether you should use a more meaningful domain-specific data type instead.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;examples&quot;&gt;Examples&lt;/h2&gt;
&lt;p&gt;There are plenty of domain-specific meanings that an &lt;code&gt;Option&lt;/code&gt; can take, but I&apos;ll use the four examples above. Hopefully this will illustrate the general principle well enough.&lt;/p&gt;
&lt;h3 id=&quot;backwards-compatibility&quot;&gt;Backwards Compatibility&lt;/h3&gt;
&lt;p&gt;This one is particularly annoying, as it tends to get more widespread as the system evolves over time. You add a new field to a class that&apos;s constrained by backwards compatibility (e.g., it is used for communication, or stored in a database), and then you mark it &lt;code&gt;Option&lt;/code&gt;al for perpetuity. Time passes by, and you no longer have any idea whether the field can be missing &quot;for real&quot;, or it&apos;s just some vestigial &lt;code&gt;Option&lt;/code&gt; supporting a no longer relevant version of code.&lt;/p&gt;
&lt;p&gt;Instead, I would suggest to use a custom generic type, isomorphic to &lt;code&gt;Option&lt;/code&gt;, to communicate the backwards compatibility concern directly&lt;sup&gt;&lt;a href=&quot;#user-content-fn-syntax&quot; id=&quot;user-content-fnref-syntax&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;enum&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;BackCompat&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Present&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Missing&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;How you end up treating such &lt;code&gt;BackCompat&lt;/code&gt; values is very domain-dependent, maybe you can replace them with &lt;code&gt;A&lt;/code&gt; over time, maybe not. But in any case, at least now you know &lt;em&gt;why&lt;/em&gt; they are there and treat them accordingly.&lt;/p&gt;
&lt;h3 id=&quot;missing-user-inputs&quot;&gt;Missing User Inputs&lt;/h3&gt;
&lt;p&gt;For user input that is not mandatory I&apos;m on the fence, this might be the case where just using &lt;code&gt;Option&lt;/code&gt; is the right fit, with all the convenience that you get from using a standard data type. The data is there or not, and that&apos;s all you care about. On the other hand, knowing that the data came from user input, and that it was explicitly not provided, might be relevant to your domain, in which case a custom &lt;code&gt;Option&lt;/code&gt;-like data type (see above) may be in order.&lt;/p&gt;
&lt;p&gt;This dilemma highlights the point that choosing the correct way to model your data is to an extent an art rather than just science.&lt;/p&gt;
&lt;h3 id=&quot;feature-toggle&quot;&gt;Feature Toggle&lt;/h3&gt;
&lt;p&gt;The next example is using &lt;code&gt;Option&lt;/code&gt; to indirectly communicate that a certain feature in the system (data fetching in the example) was turned off. That means that we have to remember that &lt;code&gt;None&lt;/code&gt; has a special meaning. It&apos;s not just an empty value, but instead it&apos;s empty because a feature was turned off, and we might want to apply special handling to it. Using the terminology from the boolean blindness &lt;a href=&quot;https://existentialtype.wordpress.com/2011/03/15/boolean-blindness/&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;post&lt;/a&gt;, the provenance of the &lt;code&gt;Option&lt;/code&gt; is just as important as the value of the data itself.&lt;/p&gt;
&lt;p&gt;Instead of using &lt;code&gt;Option&lt;/code&gt; we can communicate the provenance with yet another domain-specific, &lt;code&gt;Option&lt;/code&gt;-like data type:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;enum&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FetchedData&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Available&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Disabled&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, every time someone has a &lt;code&gt;FetchedData&lt;/code&gt; value, they have to decide what to do with it based on the informatively named cases. No longer do we have to remember the special meaning of &lt;code&gt;None&lt;/code&gt;, the type system will guide us in the right direction.&lt;/p&gt;
&lt;p&gt;One day, if and when we&apos;ll have more data fetching toggles, it&apos;ll be easy to add them to the custom &lt;code&gt;FetchedData&lt;/code&gt; type, and then let the compiler guide us with fixing up the code&lt;sup&gt;&lt;a href=&quot;#user-content-fn-exhaustivity&quot; id=&quot;user-content-fnref-exhaustivity&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;. Piggybacking on &lt;code&gt;Option&lt;/code&gt; for this might get cumbersome.&lt;/p&gt;
&lt;h3 id=&quot;system-state&quot;&gt;System State&lt;/h3&gt;
&lt;p&gt;The last example is when &lt;code&gt;Option&lt;/code&gt; is used to represent different states of the system. Now &lt;code&gt;None&lt;/code&gt; means that the user never logged in. Information that we have to hold in our heads rather than in the type system&lt;sup&gt;&lt;a href=&quot;#user-content-fn-illegal&quot; id=&quot;user-content-fnref-illegal&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Unsurprisingly, the solution would be to create a domain-specific data type. But to add some variety, for this case I will go with a non-generic type. The &quot;last logged in&quot; state doesn&apos;t have a meaningful value that&apos;s not a &lt;code&gt;Timestamp&lt;/code&gt; (at least in my interpretation of this example).&lt;/p&gt;
&lt;p&gt;The state machine for this part of the system can be represented as:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;enum&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LastLogin&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;At&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Timestamp&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Never&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this little state machine the compiler itself will remind us of whatever special behavior that we need to apply when a user logs in for the first time.&lt;/p&gt;
&lt;h2 id=&quot;trade-offs&quot;&gt;Trade Offs&lt;/h2&gt;
&lt;p&gt;Like most things in life, using custom data types is a trade off. Even if we ignore the boilerplate of actually defining a new type every time, we still pay in at least two ways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We lose all the standard &lt;code&gt;Option&lt;/code&gt; functions&lt;/li&gt;
&lt;li&gt;We cannot automatically participate in whatever special treatment that &lt;code&gt;Option&lt;/code&gt; gets in various libraries&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We can try to mitigate the first point by defining conversions to/from &lt;code&gt;Option&lt;/code&gt; and then use those to gain back some of the &lt;code&gt;Option&lt;/code&gt; functionality&lt;sup&gt;&lt;a href=&quot;#user-content-fn-conversions&quot; id=&quot;user-content-fnref-conversions&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;. Even better, if you&apos;re into functional programming you can use some standard typeclasses to imbue our custom types with &lt;code&gt;Option&lt;/code&gt;-like functionality&lt;sup&gt;&lt;a href=&quot;#user-content-fn-typeclasses&quot; id=&quot;user-content-fnref-typeclasses&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;. Some libraries will even let you derive those typeclasses automatically.&lt;/p&gt;
&lt;p&gt;For the second point, special &lt;code&gt;Option&lt;/code&gt; handling is very common in serialization libraries, e.g., converting &lt;code&gt;Option&lt;/code&gt; to &lt;code&gt;null&lt;/code&gt; in JSON. If you&apos;re lucky, whatever special functionality &lt;code&gt;Option&lt;/code&gt; gets is guided by implicits. In which case custom data types can participate just as well (at the price of some more boilerplate). If you&apos;re not that lucky, and &lt;code&gt;Option&lt;/code&gt; handling is hardcoded into the library, then you can try to petition the library authors to change that. Feel free to use this post as a form of justification.&lt;/p&gt;
&lt;p&gt;Despite these cons, I still think that the maintainability gains we get from domain-specific, &lt;code&gt;Option&lt;/code&gt;-like data types often outweigh the downsides.&lt;/p&gt;
&lt;p&gt;May you never be blinded by &lt;code&gt;Option&lt;/code&gt; ever again, till next time!&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-unmaintained&quot;&gt;
&lt;p&gt;Obviously unmaintained and misleading. &lt;a href=&quot;#user-content-fnref-unmaintained&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-these&quot;&gt;
&lt;p&gt;Like &lt;a href=&quot;https://typelevel.org/cats/datatypes/ior.html&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;, or &lt;a href=&quot;https://github.com/scalaz/scalaz/blob/master/core/src/main/scala/scalaz/These.scala&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-these&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-syntax&quot;&gt;
&lt;p&gt;I&apos;m using Scala 3 syntax which is pleasantly compact. The same applies to Scala 2 albeit with noisier syntax. &lt;a href=&quot;#user-content-fnref-syntax&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-exhaustivity&quot;&gt;
&lt;p&gt;Assuming that you have pattern match exhaustivity errors turned on, and if not, go now and enable them. What are you still doing here? Go! &lt;a href=&quot;#user-content-fnref-exhaustivity&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-illegal&quot;&gt;
&lt;p&gt;Going down the route of moving system states into the type system is a good first step towards &quot;&lt;a href=&quot;https://www.youtube.com/watch?v=PSh7JUfDstE&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;making illegal states unrepresentable&lt;/a&gt;&quot;. &lt;a href=&quot;#user-content-fnref-illegal&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-conversions&quot;&gt;
&lt;p&gt;If you&apos;re feeling particularly adventurous you can make those conversions implicit. &lt;a href=&quot;#user-content-fnref-conversions&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 6&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-typeclasses&quot;&gt;
&lt;p&gt;Things like &lt;code&gt;Functor&lt;/code&gt;, &lt;code&gt;Applicative&lt;/code&gt;, or even &lt;a href=&quot;https://www.javadoc.io/doc/org.scalaz/scalaz_2.13/7.4.0-M10/scalaz/Optional.html&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;Optional&lt;/code&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-typeclasses&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 7&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><author>Daniel Beskin</author></item><item><title>Whiteboxish Macro Powers with Named Tuples</title><link>https://blog.daniel-beskin.com/2025-04-14-whiteboxish-named-tuples</link><guid isPermaLink="true">https://blog.daniel-beskin.com/2025-04-14-whiteboxish-named-tuples</guid><description>I&apos;ve recently stumbled upon a post on Scala Contributors by Kit Langton describing how he managed to create the illusion of adding automatically derived (publicly visible) members to a companion of a…</description><pubDate>Mon, 14 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://blog.daniel-beskin.com/social-cards/2025-04-14-whiteboxish-named-tuples.png&quot; alt=&quot;Cover image for Whiteboxish Macro Powers with Named Tuples&quot; style=&quot;width: 100%; height: auto; margin-bottom: 2rem;&quot;&gt;&lt;p&gt;I&apos;ve recently stumbled upon a &lt;a href=&quot;https://contributors.scala-lang.org/t/scala-3-macro-annotations-and-code-generation/6035/69&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;post&lt;/a&gt; on Scala Contributors by Kit Langton describing how he managed to create the illusion of adding automatically derived (publicly visible) members to a companion of a class. Something that&apos;s no longer possible to achieve with macro annotations in Scala 3. This was done by daisy-chaining a number of features together: &lt;code&gt;transparent&lt;/code&gt; macros, &lt;code&gt;Selectable&lt;/code&gt; types, and implicit conversions. Read the full post for the details.&lt;/p&gt;
&lt;p&gt;As Kit mentions, the solution is just 2% shy of syntactic perfection, which is good enough for me. But another downside is the fact that it uses &lt;code&gt;transparent&lt;/code&gt; macros, meaning that IDEA cannot infer anything about the result. And that&apos;s a bummer.&lt;/p&gt;
&lt;p&gt;In an &lt;a href=&quot;https://contributors.scala-lang.org/t/scala-3-macro-annotations-and-code-generation/6035/70&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;entry&lt;/a&gt; below Kit&apos;s post, Guillaume Martres, mentions that we can get rid of the implicit conversions using a new  feature: &quot;computed field names&quot;, a sub feature of named tuples. This got me curious, as I suspected that we can make it even better and get rid of the &lt;code&gt;transparent&lt;/code&gt; macros as well. Maybe IntelliJ can join the party after all?&lt;/p&gt;
&lt;p&gt;You can see the full example code for this post in the &lt;a href=&quot;https://github.com/ncreep/named-tuples-whitebox&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;repo&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;computed-field-names&quot;&gt;Computed Field Names&lt;/h2&gt;
&lt;p&gt;For the purposes of this post I&apos;ll assume that you&apos;re already familiar with &lt;a href=&quot;https://www.scala-lang.org/api/current/docs/docs/reference/experimental/named-tuples.html&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;named tuples&lt;/a&gt;, and if not, please watch Jamie Thompson&apos;s excellent &lt;a href=&quot;https://www.youtube.com/watch?v=Qeavi9M65Qw&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;talk&lt;/a&gt; from Scalar. Additionally, basic knowledge of the mechanics of macros is also assumed (see &lt;a href=&quot;https://rockthejvm.com/articles/scala-3-macros-comprehensive-guide&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; if not).&lt;/p&gt;
&lt;p&gt;But let&apos;s see what &quot;&lt;a href=&quot;https://www.scala-lang.org/api/current/docs/docs/reference/experimental/named-tuples.html#computed-field-names-1&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;computed field names&lt;/a&gt;&quot; actually means with a small example (the video above contains an explanation as well):&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Foo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Selectable&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Fields&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;hello&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;meaningOfLife&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;selectDynamic&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Any&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;field &lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;hello&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;world&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;meaningOfLife&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;42&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; _ &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; sys.error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;cannot happen unless `selectDynamic` is called directly&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we are defining a new &lt;code&gt;Selectable&lt;/code&gt; object (1) to enable &quot;dynamically&quot; computed fields. In (2) we define the type member &lt;code&gt;Fields&lt;/code&gt; which is a named tuple that specifies the members that the compiler will allow us to select on &lt;code&gt;Foo&lt;/code&gt;. These are the &quot;computed field names&quot;. The named tuple that we use here can come from an arbitrary (type-level) computation. Something that&apos;ll we&apos;ll take advantage of later on.&lt;/p&gt;
&lt;p&gt;Next we implement field access with &lt;code&gt;selectDynamic&lt;/code&gt; (3), which receives the name of the field being accessed as a string, and returns &lt;code&gt;Any&lt;/code&gt;. Although nothing is enforcing the correctness of the &lt;code&gt;selectDynamic&lt;/code&gt; implementation, type safety is somewhat retained by the fact that the user is  only allowed to call the fields that are specified in the &lt;code&gt;Fields&lt;/code&gt; named tuple (almost, see below).&lt;/p&gt;
&lt;p&gt;With the field name in hand, we match on it (4), and produce the appropriately typed result (as specified by &lt;code&gt;Fields&lt;/code&gt;). Though we return &lt;code&gt;Any&lt;/code&gt; here, the compiler will cast the value to the correct type at runtime. We must be careful to obey the contract, otherwise we&apos;ll get a runtime cast exception.&lt;/p&gt;
&lt;p&gt;Unfortunately, despite having a solid, typeful contract with the &lt;code&gt;Fields&lt;/code&gt; tuple, nothing is stopping the user from calling &lt;code&gt;selectDynamic&lt;/code&gt; directly with an arbitrary string. We handle that with a custom error (5), just in case.&lt;/p&gt;
&lt;p&gt;With the computed field names, &lt;code&gt;Foo&lt;/code&gt; now behaves as if we added the &lt;code&gt;hello: String&lt;/code&gt; and &lt;code&gt;meaningOfLife: Int&lt;/code&gt; fields to it. Which we can safely use, like so:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Foo&lt;/span&gt;&lt;span&gt;.hello&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;b&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Foo&lt;/span&gt;&lt;span&gt;.meaningOfLife&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But this won&apos;t compile:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Foo&lt;/span&gt;&lt;span&gt;.stuff&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As &lt;code&gt;stuff&lt;/code&gt; was not specified in &lt;code&gt;Fields&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-directselect&quot; id=&quot;user-content-fnref-directselect&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;This new capability of &lt;code&gt;Selectable&lt;/code&gt; is a nontrivial improvement over what we had before. Previously we would typically have to use macros to compute the precise type of a &lt;code&gt;Selectable&lt;/code&gt;. Now we can use this new power to recreate Kit&apos;s example sans the &lt;code&gt;transparent&lt;/code&gt; macros.&lt;/p&gt;
&lt;h2 id=&quot;automatic-lenses&quot;&gt;Automatic Lenses&lt;/h2&gt;
&lt;p&gt;The example in the original post tackles the problem of automatically adding lenses for the fields of a case class to its companion. I&apos;ll shamelessly steal the same example here.&lt;/p&gt;
&lt;p&gt;In the post we have the following code that we want to automate:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;age&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;isAlive&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Boolean&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Lens&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;&lt;span&gt;](&lt;/span&gt;&lt;span&gt;_.name&lt;/span&gt;&lt;span&gt;)(&lt;/span&gt;&lt;span&gt;p &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; name &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; p.copy&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;name &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;age&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Lens&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;&lt;span&gt;](&lt;/span&gt;&lt;span&gt;_.age&lt;/span&gt;&lt;span&gt;)(&lt;/span&gt;&lt;span&gt;p &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; age &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; p.copy&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;age &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; age&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;isAlive&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Lens&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;&lt;span&gt;](&lt;/span&gt;&lt;span&gt;_.isAlive&lt;/span&gt;&lt;span&gt;)(&lt;/span&gt;&lt;span&gt;p &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; isAlive &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; p.copy&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;isAlive &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; isAlive&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As we can see the code here is very mechanical and tedious. It can be described algorithmically as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For each field of the case class&lt;/li&gt;
&lt;li&gt;Add a &lt;code&gt;Lens&lt;/code&gt; val to the companion with the field&apos;s name&lt;/li&gt;
&lt;li&gt;Implement the val with a getter/setter pair for the field&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In Scala 2 we could use annotation macros to automate this completely:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;deriveLenses&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;age&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;isAlive&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Boolean&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This would automatically add all the necessary vals to the companion of &lt;code&gt;Person&lt;/code&gt;, without us even needing to name it ourselves. Unfortunately, this is no longer possible in Scala 3, as macro annotations are not allowed to define new, externally visible, members on a class.&lt;/p&gt;
&lt;p&gt;Computed field names to the rescue!&lt;/p&gt;
&lt;h2 id=&quot;computed-lens-fields&quot;&gt;Computed Lens Fields&lt;/h2&gt;
&lt;p&gt;As we saw before, we can create the illusion of added field names by specifying the appropriate &lt;code&gt;Fields&lt;/code&gt; named tuple on our class. What would it be for the example above?&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PersonLenses&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Lens&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;                     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;age&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Lens&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;                     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;isAlive&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Lens&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Boolean&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we pass this as the &lt;code&gt;Fields&lt;/code&gt; member of a &lt;code&gt;Selectable&lt;/code&gt;, we will get the appropriately typed values on our target class. But of course, we wouldn&apos;t want to do it manually, we want to compute this automatically from the &lt;code&gt;Person&lt;/code&gt; type.&lt;/p&gt;
&lt;p&gt;We can use the various tuple utilities to achieve this. The first stop is to convert the &lt;code&gt;Person&lt;/code&gt; type to the equivalent named tuple. The magic &lt;a href=&quot;https://dotty.epfl.ch/docs/reference/other-new-features/named-tuples.html#the-namedtuplefrom-type&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;NameTuple.From&lt;/code&gt;&lt;/a&gt; lets us do just that:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;scala&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PersonFields&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NamedTuple&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;From&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; defined alias type PersonFields = (name : String, age : Int, isAlive : Boolean)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;PersonFields&lt;/code&gt; now has the same labels and types as the &lt;code&gt;Person&lt;/code&gt; case class. Now we need to manipulate the types so that they have the corresponding &lt;code&gt;Lens&lt;/code&gt; wrappers.&lt;/p&gt;
&lt;p&gt;First, let&apos;s define a type-level &quot;function&quot; that can convert a type to the matching &lt;code&gt;Lens&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ToLens&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;S&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/span&gt;&lt;span&gt;T&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Lens&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;S&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;T&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the equivalent of a curried function of two arguments, but at the type-level. The first argument is the source of the lens, the second is the target. We just curried the &lt;code&gt;Lens&lt;/code&gt; type constructor. We can use it like so:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;summon&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Lens&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=:=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ToLens&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;]]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; compiles&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(Using &lt;code&gt;=:=&lt;/code&gt; to check the result as the REPL refuses to expand the resulting type.)&lt;/p&gt;
&lt;p&gt;Now we can use this anonymous function to map over the named tuple:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;scala&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;NamedTuple&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Map&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LensesForPerson&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PersonFields&lt;/span&gt;&lt;span&gt; `Map` &lt;/span&gt;&lt;span&gt;ToLens&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Leaning on the intuition that tuples are like collections, we import the &lt;code&gt;Map&lt;/code&gt; &quot;function&quot; for named tuples&lt;sup&gt;&lt;a href=&quot;#user-content-fn-tuplemanipulation&quot; id=&quot;user-content-fnref-tuplemanipulation&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, and then we use it as an infix operator to apply the &lt;code&gt;ToLens&lt;/code&gt; &quot;function&quot; over the fields of &lt;code&gt;Person&lt;/code&gt;. We partially apply &lt;code&gt;ToLens&lt;/code&gt; to the &lt;code&gt;Person&lt;/code&gt; type, so that it becomes the source of all the lenses that we get.&lt;/p&gt;
&lt;p&gt;This automatically converts &lt;code&gt;Person&lt;/code&gt; to the corresponding field lenses:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;summon&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Lens&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;age&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Lens&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;isAlive&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Lens&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Boolean&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=:=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;LensesForPerson&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; compiles&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cool, we managed to automatically compute the lenses fields for &lt;code&gt;Person&lt;/code&gt;. Of course, what we really care about is doing this generically for any case class, not just &lt;code&gt;Person&lt;/code&gt;. It&apos;s easy enough to generify this code, just replace &lt;code&gt;Person&lt;/code&gt; with &lt;code&gt;A&lt;/code&gt; all over the place:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LensesFor&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NamedTuple&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;From&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; `Map` &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ToLens&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Short and to the point.&lt;/p&gt;
&lt;p&gt;This still works the same for &lt;code&gt;Person&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;summon&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Lens&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;age&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Lens&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;isAlive&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Lens&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Boolean&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=:=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;LensesFor&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; compiles&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the key ingredient that we need to be able to avoid transparent macros. Once we computed the desired type of the output, transparency is no longer needed. Let&apos;s tie this all together into automatic lenses.&lt;/p&gt;
&lt;h2 id=&quot;automatic-lensy-members&quot;&gt;Automatic Lensy Members&lt;/h2&gt;
&lt;p&gt;We need one more ingredient to be able to implement the lens members: the actual lenses. For this we&apos;ll use a combination of givens and macros. First a helper type:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LensesMap&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;S&lt;/span&gt;&lt;span&gt;](&lt;/span&gt;&lt;span&gt;lenses&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Map&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Lens&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;S&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;]])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is just a wrapper around a map of lenses, all pointing to the same source &lt;code&gt;S&lt;/code&gt;. The intent being that &lt;code&gt;S&lt;/code&gt; stands for our target class, e.g., &lt;code&gt;Person&lt;/code&gt;. Each map entry is keyed by a field name with the value being the field&apos;s lens. Since the fields can have different types, we cannot type the lenses with anything better than &lt;code&gt;?&lt;/code&gt;. But that&apos;s okay, as we are going to hide this unsafety behind a safe &lt;code&gt;Selectable&lt;/code&gt; interface.&lt;/p&gt;
&lt;p&gt;For our &lt;code&gt;Person&lt;/code&gt; example this map would look something like this:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;LensesMap&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;](&lt;/span&gt;&lt;span&gt;Map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Lens&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;](&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;age&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Lens&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;&lt;span&gt;](&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;isAlive&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Lens&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Boolean&lt;/span&gt;&lt;span&gt;&lt;span&gt;](&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is pretty much the same structure that we wanted to generate on the companion object but written out in an unsafe and stringly manner. Creating these values is also completely mechanical, and we can automate this with a macro:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;inline&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;given&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;derived&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;S&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LensesMap&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;S&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&lt;span&gt; derivedImpl&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/span&gt;&lt;span&gt;S&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;derivedImpl&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;S&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Type&lt;/span&gt;&lt;span&gt;](&lt;/span&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Quotes&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Expr&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;LensesMap&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;S&lt;/span&gt;&lt;span&gt;&lt;span&gt;]]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;???&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We auto-derive &lt;code&gt;LensesMap&lt;/code&gt; for a given type &lt;code&gt;S&lt;/code&gt;. The implementation is a trivial adaptation of the code from Kit&apos;s original example, which you can see in the &lt;a href=&quot;https://github.com/ncreep/named-tuples-whitebox/blob/master/LensesMap.scala&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;repo&lt;/a&gt;. Crucially, the macro is not transparent, it has a well-defined type known at the definition site of the macro.&lt;/p&gt;
&lt;p&gt;With this building block we can finally create our automatic lens deriver:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;trait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DeriveLenses&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;](&lt;/span&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lensesMap&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;LensesMap&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Selectable&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Fields&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LensesFor&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;inline&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;selectDynamic&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Lens&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;lensesMap.lenses.getOrElse&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sys.error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Invalid field name [&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The implementation is very short, but it packs a punch due to the power of named tuples. Let&apos;s break it down:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;DeriveLenses&lt;/code&gt; take a type argument, the case class we want to create lenses for (1)&lt;/li&gt;
&lt;li&gt;We also take as a given argument for the automatically derived lenses for the same type (1)&lt;/li&gt;
&lt;li&gt;And extend &lt;code&gt;Selectable&lt;/code&gt; to be able to dynamically add fields (1)&lt;/li&gt;
&lt;li&gt;Next we compute the lens fields that correspond to the type we got (2), this ensures that we can safely access all the field lenses&lt;/li&gt;
&lt;li&gt;Lastly, we implement &lt;code&gt;selectDynamic&lt;/code&gt; by extracting the lens that matches the string field name we got as input, this is safe as &lt;code&gt;LensesMap&lt;/code&gt; and &lt;code&gt;LensesFor&lt;/code&gt; both refer to the same type&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Done! But does it work?&lt;/p&gt;
&lt;p&gt;First we need to apply derivation to the &lt;code&gt;Person&lt;/code&gt; companion:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DeriveLenses&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now all the lenses are available to us as if they are regular fields:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;person&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Alice&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;42&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;&lt;span&gt;.name.get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;person&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;age&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;&lt;span&gt;.age.get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;person&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;isAlive&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Person&lt;/span&gt;&lt;span&gt;&lt;span&gt;.isAlive.get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;person&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;println&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Name: &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;, Age: &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;age&lt;/span&gt;&lt;span&gt;, Is Alive: &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;isAlive&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; Name: Alice, Age: 42, Is Alive: true&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Great success. It all works, fully type-safe, with zero transparent macros. Computed fields really boosted our expressivity here.&lt;/p&gt;
&lt;p&gt;Some observations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This is not as efficient as the Scala 2 version, where the code compiled to regular field access. Here we have to convert from a string to get to the actual lens &lt;sup&gt;&lt;a href=&quot;#user-content-fn-moreefficient&quot; id=&quot;user-content-fnref-moreefficient&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/li&gt;
&lt;li&gt;We lowered our syntactic perfection score to be 3% shy of perfection, due to the extra &lt;code&gt;Person&lt;/code&gt; type parameter we have to specify. It may be possible to avoid it with another macro, but I don&apos;t see how it can be done without using &lt;code&gt;transparent&lt;/code&gt; macros again.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;NameTuple.From&lt;/code&gt; type is very magical indeed, without it we would probably have to resort to using &lt;code&gt;Mirror&lt;/code&gt;, which in turn would force us into implicit conversions like in the original code. Although the proposed &lt;a href=&quot;https://scala-lang.org/api/3.6.2/docs/docs/reference/experimental/modularity.html&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;modularity improvements&lt;/a&gt; might make this a non-issue even without &lt;code&gt;From&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Having said that, I think that we got one step closer towards whiteboxish macro capabilities from the days of yore, without actually needing to be whitebox. Well done named tuples!&lt;/p&gt;
&lt;h2 id=&quot;aftermath&quot;&gt;Aftermath&lt;/h2&gt;
&lt;p&gt;Recall that the motivation to avoid transparent macros (other than to just show that we can) was to enable IDEA to infer the lenses. We succeeded in removing the transparent macros, but as of writing IntelliJ is yet to support computed field names with named tuples.&lt;/p&gt;
&lt;p&gt;There goes trying to invite IDEA to the party&lt;sup&gt;&lt;a href=&quot;#user-content-fn-credit&quot; id=&quot;user-content-fnref-credit&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;...&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-directselect&quot;&gt;
&lt;p&gt;But this unfortunately does compile and fails at runtime: &lt;code&gt;Foo.selectDynamic(&quot;stuff&quot;)&lt;/code&gt; &lt;a href=&quot;#user-content-fnref-directselect&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-tuplemanipulation&quot;&gt;
&lt;p&gt;To gain more intuition you can see more examples of generic tuple manipulation in a &lt;a href=&quot;https://www.youtube.com/watch?v=7qeTuqrFNYc&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;talk of mine&lt;/a&gt;, or &lt;a href=&quot;https://rockthejvm.com/articles/scala-3-type-level-programming&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;this&lt;/a&gt; blog post. &lt;a href=&quot;#user-content-fnref-tuplemanipulation&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-moreefficient&quot;&gt;
&lt;p&gt;We could somewhat improve efficiency by using a macro that creates a pattern match for the lenses rather than a &lt;code&gt;Map&lt;/code&gt;. But this is still probably not on par with direct field access. Maybe some kind of trickery with inline matches can get us closer to direct field access. &lt;a href=&quot;#user-content-fnref-moreefficient&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-credit&quot;&gt;
&lt;p&gt;Jokes aside though, the Scala team at Jetbrains deserves much credit for having quite a bit of support for named tuples, even though they are still marked experimental. &lt;a href=&quot;#user-content-fnref-credit&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><author>Daniel Beskin</author></item><item><title>Random Scala Tip #568: Beware of Leaking Iterators</title><link>https://blog.daniel-beskin.com/2025-01-15-random-scala-tip-568-beware-of-leaking-iterators</link><guid isPermaLink="true">https://blog.daniel-beskin.com/2025-01-15-random-scala-tip-568-beware-of-leaking-iterators</guid><description>Beware of leaking `Iterator` instances outside the scope you initialized it in.</description><pubDate>Wed, 15 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://blog.daniel-beskin.com/social-cards/2025-01-15-random-scala-tip-568-beware-of-leaking-iterators.png&quot; alt=&quot;Cover image for Random Scala Tip #568: Beware of Leaking Iterators&quot; style=&quot;width: 100%; height: auto; margin-bottom: 2rem;&quot;&gt;&lt;h2 id=&quot;the-tip&quot;&gt;The Tip&lt;/h2&gt;
&lt;p&gt;The Scala collections library defines an &lt;a href=&quot;https://www.scala-lang.org/api/current/scala/collection/Iterator.html&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;Iterator&lt;/code&gt;&lt;/a&gt; trait. As you might expect, a typical &lt;code&gt;Iterator&lt;/code&gt;&apos;s implementation relies on mutable state.&lt;/p&gt;
&lt;p&gt;And so for the today&apos;s tip:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Beware of leaking &lt;code&gt;Iterator&lt;/code&gt; instances outside the scope you initialized it in.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;but-whats-so-special-about-iterator&quot;&gt;But What&apos;s So Special About &lt;code&gt;Iterator&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;Glad you asked.&lt;/p&gt;
&lt;p&gt;We&apos;re all functional adults here, we&apos;re avoiding mutable state anyways. And even when &lt;a href=&quot;https://github.com/scala/scala/blob/b6f70d2347f2857695e5c0fe544b0f921544b02a/src/library/scala/collection/immutable/List.scala#L655&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;we don&apos;t&lt;/a&gt;, obviously we won&apos;t be leaking any mutable state outside some tightly guarded scope. So why am I singling out &lt;code&gt;Iterator&lt;/code&gt; specifically?&lt;/p&gt;
&lt;p&gt;The reason is that &lt;code&gt;Iterator&lt;/code&gt; can be sneaky. It&apos;s returned in places you might not expect it, and it superficially behaves like any other (immutable) collection&lt;sup&gt;&lt;a href=&quot;#user-content-fn-overloading&quot; id=&quot;user-content-fnref-overloading&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Let me demonstrate. Suppose you&apos;re working on this code&lt;sup&gt;&lt;a href=&quot;#user-content-fn-github&quot; id=&quot;user-content-fnref-github&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;process&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;datas&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;datas&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;enrich&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.foreach&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;storeToDB&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;enrich&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;???&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;storeToDB&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;???&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What you have here is a nice, lightly functional, data processing pipeline. The reason why we all love using functional collections for data processing. In detail:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The main &lt;code&gt;process&lt;/code&gt; function (1) takes a bunch of &lt;code&gt;UserData&lt;/code&gt; values&lt;/li&gt;
&lt;li&gt;It then enriches each one (2) using the &lt;code&gt;enrich&lt;/code&gt; function (4)&lt;/li&gt;
&lt;li&gt;Lastly, it stores each enriched entry into the database (3) using the &lt;code&gt;storeToDB&lt;/code&gt; procedure (5)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can run this code with some sample inputs and get the following output:&lt;/p&gt;
&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Stored ID: 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Stored ID: 2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Stored ID: 3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Stored ID: 4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Stored ID: 1000&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nothing fancy, I&apos;m sure that most Scala programmers are familiar with such code.&lt;/p&gt;
&lt;p&gt;But then requirements change. We&apos;re told that the database is getting overloaded with all the &lt;code&gt;storeToDB&lt;/code&gt; requests. We scratch our head a bit and decide to do some batching.&lt;/p&gt;
&lt;p&gt;Easy enough:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;process&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;datas&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;datas&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;enrich&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.grouped&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.foreach&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;storeToDB&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;enrich&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;???&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;storeToDB&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;batch&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;???&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With a minor tweak to the code we managed to adapt to the new requirements&lt;sup&gt;&lt;a href=&quot;#user-content-fn-streaming&quot; id=&quot;user-content-fnref-streaming&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;. By calling &lt;code&gt;grouped&lt;/code&gt; (1) on our list, we split it in batches. We can now send each batch to the modified &lt;code&gt;storeToDB&lt;/code&gt; procedure (2) to be stored as a whole batch.&lt;/p&gt;
&lt;p&gt;Who doesn&apos;t love code that&apos;s so adaptable to fluctuating requirements? That&apos;s functional modularity at its best.&lt;/p&gt;
&lt;p&gt;And you can run it again and see how each batch gets stored:&lt;/p&gt;
&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Stored IDs: 1, 2, 3...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Stored IDs: 101, 102, 103...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Stored IDs: 201, 202, 203...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Stored IDs: 301, 302, 303...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Stored IDs: 401, 402, 403...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Stored IDs: 901, 902, 903...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So far, we have no issues. But as always, requirements change yet again. This time we are required to add some monitoring to the batches that we produce.
For monitoring we have the following procedure:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;monitor&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;batch&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;???&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It takes a batch and side-effectfully does some monitoring. Another requirement is that we don&apos;t want to delay our main flow due to monitoring, so we should run it in a &quot;fire and forget&quot; &lt;code&gt;Future&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-terrible&quot; id=&quot;user-content-fnref-terrible&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;That&apos;s all easy enough&lt;sup&gt;&lt;a href=&quot;#user-content-fn-contrived&quot; id=&quot;user-content-fnref-contrived&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;process&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;datas&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;batches&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;datas&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;enrich&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.grouped&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Future&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;batches.foreach&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;monitor&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;batches.foreach&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;storeToDB&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;We now assign our batches to a variable (1) so that we can use it twice&lt;/li&gt;
&lt;li&gt;We run monitoring in a separate &lt;code&gt;Future&lt;/code&gt; (2)&lt;/li&gt;
&lt;li&gt;And we do the data storing just like in the previous snippet (3)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Although the code got a bit uglier, it&apos;s not that terrible, so we move on with our day.&lt;/p&gt;
&lt;p&gt;Until the production bug hits us in the face...&lt;/p&gt;
&lt;p&gt;If we try to run this code like we did before we might get the following output:&lt;/p&gt;
&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Monitored: 100 items&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Monitored: 100 items&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Monitored: 100 items&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Monitored: 1 items&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Stored IDs: 1, 101, 102...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It seems that we monitored most of what we need to monitor, but we only stored one batch of mismatched IDs.&lt;/p&gt;
&lt;p&gt;Or worse yet:&lt;/p&gt;
&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Exception in thread &quot;main&quot; java.lang.NullPointerException: Cannot invoke &quot;scala.collection.IterableOnce.knownSize()&quot; because &quot;prefix&quot; is null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;at scala.collection.immutable.List.prependedAll(List.scala:148)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;at scala.collection.immutable.List$.from(List.scala:685)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Monitored: 100 items&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Monitored: 100 items&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Monitored: 100 items&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Monitored: 100 items&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Monitored: 20 items&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We only monitored some of the batches, but we did no storing at all, instead ending up with an exception.&lt;/p&gt;
&lt;p&gt;What&apos;s going on?&lt;/p&gt;
&lt;h2 id=&quot;the-case-of-the-sneaky-iterator&quot;&gt;The Case of the Sneaky Iterator&lt;/h2&gt;
&lt;p&gt;What just happened is that unbeknownst to us the &lt;code&gt;grouped&lt;/code&gt; method returns an &lt;code&gt;Iterator[List[UserData]]&lt;/code&gt;. Now, whether or not this is the correct choice of a return type for this method is above my pay grade. But it&apos;s there in the standard library (along with some other &lt;code&gt;Iterator&lt;/code&gt;-returning methods like &lt;code&gt;sliding&lt;/code&gt;) and so we have to deal with it.&lt;/p&gt;
&lt;p&gt;The problem here is how invisible it is. Due to type-inference we didn&apos;t have to specify the type of &lt;code&gt;batches&lt;/code&gt;, so we missed it there. And since &lt;code&gt;Iterator&lt;/code&gt; being part of the collections library has a &lt;code&gt;foreach&lt;/code&gt; method, there was nothing to draw our attention to the fact that we moved from a regular immutable collection to a mutable &lt;code&gt;Iterator&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As we are sharing the &lt;code&gt;batches&lt;/code&gt; value between two flows we get the weird and racy behavior that we just observed. Both flows are competing to consume the &lt;code&gt;Itertor&lt;/code&gt;, and the results are mixed.&lt;/p&gt;
&lt;p&gt;And this is why I singled out &lt;code&gt;Iterator&lt;/code&gt;: it can sneak mutable state where you don&apos;t expect it, resulting in code that leaks &lt;code&gt;Iterator&lt;/code&gt;s into unexpected places.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Possible objection&lt;/em&gt;: &quot;but I&apos;m doing pure FP&quot;. Indeed, doing pure FP is great. But this means there&apos;s all the more reason to be careful with the standard collections, lest you inject an impure value into your pure computation and wreak havoc on your referentially transparent code.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Possible workaround&lt;/em&gt;: I might even go as far as adding a linter rule (for, e.g., &lt;a href=&quot;https://www.wartremover.org/&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;WartRemover&lt;/code&gt;&lt;/a&gt;) to the build to forbid the usage of these somewhat dangerous, &lt;code&gt;Iterator&lt;/code&gt;-returning, methods.&lt;/p&gt;
&lt;p&gt;That&apos;s all for now, stay &lt;code&gt;Iterator&lt;/code&gt;-safe.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-overloading&quot;&gt;
&lt;p&gt;Yet another instance of &lt;a href=&quot;https://blog.daniel-beskin.com/2024-06-19-overloading&quot;&gt;&quot;overloading considered evil&quot;&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-overloading&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-github&quot;&gt;
&lt;p&gt;The full example code is available on &lt;a href=&quot;https://github.com/ncreep/leaking-iterators&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-github&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-streaming&quot;&gt;
&lt;p&gt;This is a great use-case for a streaming library. Unfortunately not all of us get to live in the enlightened world of streaming. &lt;a href=&quot;#user-content-fnref-streaming&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-terrible&quot;&gt;
&lt;p&gt;A terrible idea in general, but sometimes you just do what they tell you to do. &lt;a href=&quot;#user-content-fnref-terrible&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-contrived&quot;&gt;
&lt;p&gt;Yes, I know the code looks a bit contrived, but it&apos;s &quot;based on a true story&quot;. &lt;a href=&quot;#user-content-fnref-contrived&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><author>Daniel Beskin</author></item><item><title>Random Scala Tip #697: Avoid Anonymous Functions as Dependencies</title><link>https://blog.daniel-beskin.com/2025-01-07-random-scala-tip-697-avoid-anon-func-deps</link><guid isPermaLink="true">https://blog.daniel-beskin.com/2025-01-07-random-scala-tip-697-avoid-anon-func-deps</guid><description>Avoid using anonymous functions as class dependencies.</description><pubDate>Tue, 07 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://blog.daniel-beskin.com/social-cards/2025-01-07-random-scala-tip-697-avoid-anon-func-deps.png&quot; alt=&quot;Cover image for Random Scala Tip #697: Avoid Anonymous Functions as Dependencies&quot; style=&quot;width: 100%; height: auto; margin-bottom: 2rem;&quot;&gt;&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;/h2&gt;
&lt;p&gt;Imagine that you&apos;re building an app and somewhere in your code you write something like this:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DoFinanceStuff&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cc&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CreditCardService&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;grabClientMoney&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;card&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CardData&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;amount&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cc.processPayment&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;amount, card&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And like the good developer that you pretend to be, you want to write some unit tests to verify that you really are grabbing the client&apos;s money. Since the codebase pervasively uses dependency injection, this should be a breeze.&lt;/p&gt;
&lt;p&gt;So you want to mock out the &lt;code&gt;CreditCardService&lt;/code&gt; instance you have in the &lt;code&gt;DoFinanceStuff&lt;/code&gt;. &quot;No problem&quot;, you say to yourself, let&apos;s see what functionality we need to provide. You open up the &lt;code&gt;CreditCardService&lt;/code&gt; trait, and lo and behold:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;trait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreditCardService&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;validateCard&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cardNumber&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CardNumber&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;expiryDate&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ExpiryDate&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;cvv&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CVV&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ValidationResult&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;processPayment&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;amount&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;MonetaryAmount&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;cardData&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CardDetails&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TransactionId&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getCardholderDetails&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cardNumber&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CardNumber&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CardholderInfo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;authorizeTransaction&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cardData&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CardDetails&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;merchantId&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;MerchantId&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AuthToken&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;checkTransactionStatus&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;transactionId&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;TransactionId&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TransactionStatus&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;calculateRewards&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;transactionAmount&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;MonetaryAmount&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RewardPoints&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;updateCardLimit&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cardNumber&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CardNumber&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;newLimit&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CreditLimit&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreditLimit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getBillingCycleInfo&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cardNumber&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CardNumber&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;BillingCycle&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;reportLostCard&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cardNumber&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CardNumber&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CardStatus&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;initiateDispute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;transactionId&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;TransactionId&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;reason&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;DisputeReason&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DisputeStatus&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;freezeCard&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cardNumber&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CardNumber&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CardStatus&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getTransactionHistory&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cardNumber&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CardNumber&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;startDate&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Date&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;endDate&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Date&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Transaction&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And it goes on like this for another ~15 methods.&lt;/p&gt;
&lt;p&gt;Now it could be argued that someone messed up with this trait and it should be broken apart. But you just wanted to write a measly test&lt;sup&gt;&lt;a href=&quot;#user-content-fn-nogooddeed&quot; id=&quot;user-content-fnref-nogooddeed&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, not refactor the whole universe. Creating a stub implementation with this many &lt;code&gt;???&lt;/code&gt; seems daunting, why do we need to suffer through so much boilerplate?..&lt;/p&gt;
&lt;p&gt;A thought goes through your mind, &quot;a mocking library will save me from this hell&quot;. But you promptly discard this thought having remembered that mocking libraries are evil and there&apos;s a whole other hell reserved for those that use them&lt;sup&gt;&lt;a href=&quot;#user-content-fn-hyperbole&quot; id=&quot;user-content-fnref-hyperbole&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Finally you remember that you&apos;re using Scala, a language that supports and encourages functional programming.&lt;/p&gt;
&lt;p&gt;It now seems obvious, we&apos;re only using one method from &lt;code&gt;CreditCardService&lt;/code&gt;, we can replace it with an anonymous function. Like so:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DoFinanceStuff&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;processPayment&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;MonetaryAmount&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;CardDetails&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TransactionId&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;grabClientMoney&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;card&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CardData&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;amount&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;processPayment&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;amount, card&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cool. Now we can easily provide a test implementation for this one function. The change that we need to apply to the production code is minimal:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cc&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreditCardService&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;financialThing&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DoFinanceStuff&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cc.processPayment&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we use the fact that Scala automatically converts methods to anonymous functions.&lt;/p&gt;
&lt;p&gt;All is great, we have very little boilerplate and writing the test is a breeze. No?&lt;/p&gt;
&lt;h2 id=&quot;the-tip&quot;&gt;The Tip&lt;/h2&gt;
&lt;p&gt;Nope.&lt;/p&gt;
&lt;p&gt;So here&apos;s the actual tip:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Avoid using anonymous functions as class dependencies.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Why? Ergonomics.&lt;/p&gt;
&lt;p&gt;Imagine a realistic full size application, with all its transitive interdependencies all over the place. Now you look at this class header:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DoFinanceStuff&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;processPayment&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;MonetaryAmount&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;CardDetails&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TransactionId&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And you ask yourself:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;What is the &lt;code&gt;processPayment&lt;/code&gt; function doing?&lt;/li&gt;
&lt;li&gt;Where did it come from?&lt;/li&gt;
&lt;li&gt;Where was it initialized?&lt;/li&gt;
&lt;li&gt;What are the relevant production implementations of this function?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;All important questions to ask, and all might not have immediately obvious answers in a sufficiently &lt;del&gt;convoluted&lt;/del&gt; realistic code.
The problem with using anonymous functions like this is that no mainstream tool for Scala is equipped to answer those questions. Or more concretely, you can&apos;t hit &quot;find implementations/references&quot; on a function type&lt;sup&gt;&lt;a href=&quot;#user-content-fn-hoogle&quot; id=&quot;user-content-fnref-hoogle&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;What do we do instead then? We bite the bullet, settle for a bit more boilerplate, and define a custom trait:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;trait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PaymentProcessor&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;processPayment&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;amount&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;MonetaryAmount&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;cardData&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CardDetails&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TransactionId&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Along with an implementation that simply forwards calls to a &lt;code&gt;CreditCardService&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PaymentProcessor&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Default&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cc&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CreditCardService&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PaymentProcessor&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;processPayment&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;amount&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;MonetaryAmount&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;cardData&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CardDetails&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TransactionId&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cc.processPayment&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;amount, cardData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our original class now looks like this:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DoFinanceStuff&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;pp&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;PaymentProcessor&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And finally the initialization becomes:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cc&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreditCardService&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;financialThing&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DoFinanceStuff&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;PaymentProcessor&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Default&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cc&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A single method trait (a SAM) is indeed a more boilerplate-y equivalent of an anonymous function. Testability remains just as easy as before. So what we did here is to pay some boilerplate tax to get the exact same functionality as before. Did we gain anything in return?&lt;/p&gt;
&lt;p&gt;What we gained is that now all the questions that we wanted to answer before can easily be answered with any reasonable Scala IDE&lt;sup&gt;&lt;a href=&quot;#user-content-fn-reasonableide&quot; id=&quot;user-content-fnref-reasonableide&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;. To wit:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;What is the &lt;code&gt;processPayment&lt;/code&gt; function doing?&lt;br&gt;
Hit &quot;find implementations&quot; on the &lt;code&gt;PaymentProcessor&lt;/code&gt; and find the relevant implementation (there&apos;s likely just one&lt;sup&gt;&lt;a href=&quot;#user-content-fn-convention&quot; id=&quot;user-content-fnref-convention&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;).&lt;/li&gt;
&lt;li&gt;Where did it come from?&lt;br&gt;
Hit &quot;find usages&quot; on the &lt;code&gt;PaymentProcessor&lt;/code&gt; type and get a good overview of where it came from&lt;/li&gt;
&lt;li&gt;Where was it initialized?&lt;br&gt;
Hit &quot;find usages&quot; on the relevant implementation from the first question and find the place it gets initialized in&lt;/li&gt;
&lt;li&gt;What are the relevant production implementations of this function?&lt;br&gt;
Hit &quot;find implementations&quot; and... You get the general idea&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is just the beginning. As you now have a named entity in your code there are lots of things that become better:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Giving names to things forces you to think about them more, and only good can come out of that&lt;/li&gt;
&lt;li&gt;There&apos;s a natural place for documentation about this entity&lt;/li&gt;
&lt;li&gt;It&apos;s easier to refactor, since both automated tooling can help and you can more easily find all the relevant usages&lt;/li&gt;
&lt;li&gt;Future evolution becomes easier, as you can add methods to your new trait if the need arises &lt;sup&gt;&lt;a href=&quot;#user-content-fn-betterworse&quot; id=&quot;user-content-fnref-betterworse&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;You&apos;ve just taken the first step to breaking down the monster &lt;code&gt;CreditCardService&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And the list goes on. None of those benefits are available for an anonymous function.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Caveat&lt;/em&gt;: as most things in life, this is a trade off. Sometimes all the boilerplate ceremony and naming effort is just not worth it, and you might prefer to go down the anonymous road. Use your judgement&lt;sup&gt;&lt;a href=&quot;#user-content-fn-ordont&quot; id=&quot;user-content-fnref-ordont&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Addendum&lt;/em&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-reddit&quot; id=&quot;user-content-fnref-reddit&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;8&lt;/a&gt;&lt;/sup&gt;: One can also improve things a bit by using a type alias for the function type. And although it somewhat improves readability, and forces you to name the thing; it&apos;s still not as ergonomically convenient as a newly defined type (be it a trait, or some other wrapper). Type aliases are only searchable when they are explicitly named, and nothing is forcing you to name them when passing an anonymous function along, you just instantiate the anonymous function as you would any other function, without using the name of the alias.&lt;/p&gt;
&lt;h2 id=&quot;bonus-tip&quot;&gt;Bonus Tip&lt;/h2&gt;
&lt;p&gt;You might&apos;ve noticed that I paid for a bit more boilerplate than strictly necessary by introducing the &lt;code&gt;Default&lt;/code&gt; class. I could&apos;ve just created a quick method with an anonymous implementation, and since the trait is a SAM, it could&apos;ve just been implemented with an anonymous function. The result is pretty much equivalent, but again I&apos;d avoid the anonymous route.&lt;/p&gt;
&lt;p&gt;This too is an ergonomics/tooling issue, but it seems that most tools are better at displaying named implementations rather than anonymous ones. And although I might use an anonymous implementation for testing, the &quot;real&quot; implementation is better be named (ideally with some consistent naming convention).&lt;/p&gt;
&lt;p&gt;It&apos;s a shame to avoid nice language features due to minor ergonomic nits, but you can&apos;t have it all...&lt;/p&gt;
&lt;p&gt;Hope you found these tips useful, or at least thought provoking. Let me know in the comments.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-nogooddeed&quot;&gt;
&lt;p&gt;As they say, no good deed goes unpunished. &lt;a href=&quot;#user-content-fnref-nogooddeed&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-hyperbole&quot;&gt;
&lt;p&gt;You think I&apos;m being hyperbolic, don&apos;t you? &lt;a href=&quot;#user-content-fnref-hyperbole&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-hoogle&quot;&gt;
&lt;p&gt;Although it&apos;s not difficult to imagine a &lt;a href=&quot;https://hoogle.haskell.org/&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;Hoogle&lt;/a&gt; style query engine in the editor that can do this, we are not there yet. &lt;a href=&quot;#user-content-fnref-hoogle&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-reasonableide&quot;&gt;
&lt;p&gt;Was that an oxymoron? &lt;a href=&quot;#user-content-fnref-reasonableide&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-convention&quot;&gt;
&lt;p&gt;Coupled with an enstablished convention for naming the trait implementations (e.g., &lt;code&gt;Default/Impl/Live/Prod&lt;/code&gt;) this becomes almost automatic. Too bad we can&apos;t express the convention in the type system. If you have ideas on how to do that, please reach out. &lt;a href=&quot;#user-content-fnref-convention&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-betterworse&quot;&gt;
&lt;p&gt;For better or for worse, we don&apos;t want to end up with another &lt;code&gt;CreditCardService&lt;/code&gt; monster... &lt;a href=&quot;#user-content-fnref-betterworse&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 6&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-ordont&quot;&gt;
&lt;p&gt;Or don&apos;t, it&apos;s up to you. &lt;a href=&quot;#user-content-fnref-ordont&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 7&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-reddit&quot;&gt;
&lt;p&gt;Thanks &lt;a href=&quot;https://www.reddit.com/r/scala/comments/1hw3l5g/comment/m61x66t/&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;Reddit&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-reddit&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 8&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><author>Daniel Beskin</author></item><item><title>Purify Your Tests Episode IV: The Monoids Strike Back</title><link>https://blog.daniel-beskin.com/2024-12-02-purify-tests-4</link><guid isPermaLink="true">https://blog.daniel-beskin.com/2024-12-02-purify-tests-4</guid><description>Welcome back to yet another episode of test purification. In the last part we explored another benefit of adding type parameters to our code, the ability to work with very lean mocks. It&apos;s been a…</description><pubDate>Mon, 02 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://blog.daniel-beskin.com/social-cards/2024-12-02-purify-tests-4.png&quot; alt=&quot;Cover image for Purify Your Tests Episode IV: The Monoids Strike Back&quot; style=&quot;width: 100%; height: auto; margin-bottom: 2rem;&quot;&gt;&lt;p&gt;Welcome back to yet another episode of test purification. In the &lt;a href=&quot;https://blog.daniel-beskin.com/2024-05-30-purify-tests-3&quot;&gt;last part&lt;/a&gt; we explored another benefit of adding type parameters to our code, the ability to work with very lean mocks. It&apos;s been a while since the last part, so please take a look &lt;a href=&quot;https://blog.daniel-beskin.com/2024-05-30-purify-tests-3&quot;&gt;there&lt;/a&gt; to refresh your memory.&lt;/p&gt;
&lt;p&gt;In this part, rather than further improving our test code, we are going to see how to leverage our newly minted type parameters to improve our production code.&lt;/p&gt;
&lt;p&gt;Onwards and forwards!&lt;/p&gt;
&lt;h2 id=&quot;a-long-time-ago-in-a-galaxy-far-far-away&quot;&gt;A Long Time Ago in a Galaxy Far, Far Away...&lt;/h2&gt;
&lt;p&gt;Recall that last time we ended up with the following code&lt;sup&gt;&lt;a href=&quot;#user-content-fn-github&quot; id=&quot;user-content-fnref-github&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UberService&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt;](&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fetcher&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Fetcher&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;enricher&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Enricher&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bookkeeper&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Bookkeeper&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;storage&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetchAndStore&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; fetcher.fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;enriched&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; enricher.enrich&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user, data&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bookkept&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; bookkeeper.bookkeep&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data, enriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stored&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; storage.store&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;bookkept, enriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;stored&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With a glorious sum total of 5 parameters, we managed to make this code highly testable with stateless mocks and maximally lean input data. Those were the days...&lt;/p&gt;
&lt;p&gt;Unfortunately, since then, someone in management discovered that what we so bravely called &lt;code&gt;UberService&lt;/code&gt; is actually a glorified ETL job. And so, management decreed, our service was demoted to be a batch job. Oh well... Might as well use this opportunity to refactor our code and improve it, that&apos;s the thing us developers love doing the most.&lt;/p&gt;
&lt;h2 id=&quot;stream-data-stream&quot;&gt;Stream Data, Stream...&lt;/h2&gt;
&lt;p&gt;As a batch job we now need to handle multiple users all at once. To make this manageable and fun, we&apos;ll use streaming to get the data into our process.&lt;/p&gt;
&lt;p&gt;The new &lt;code&gt;Fetcer&lt;/code&gt; now looks like this:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;trait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Fetcher&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Stream&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of receiving a &lt;code&gt;UserID&lt;/code&gt; argument and returning data for a single user, we now produce a &lt;code&gt;Stream&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-stream&quot; id=&quot;user-content-fnref-stream&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; of &lt;code&gt;UserData&lt;/code&gt;. This is the all data that we need to process in our current run.&lt;/p&gt;
&lt;p&gt;It also turned out that nobody was ever looking at the bookkeeping data and so we decided to surreptitiously drop the bookkeeping logic from our new job
&lt;sup&gt;&lt;a href=&quot;#user-content-fn-bookkeeping&quot; id=&quot;user-content-fnref-bookkeeping&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Lastly, since we are in batch mode, it would be more efficient to store data in batches so as to not invoke the storage procedure on per entry basis. So our data storage now looks like this&lt;sup&gt;&lt;a href=&quot;#user-content-fn-sinkstore&quot; id=&quot;user-content-fnref-sinkstore&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;trait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;storeBatch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With all those requirements in place, we can now write our new job:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UberJob&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;fetcher&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Fetcher&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;enricher&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Enricher&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;storage&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetchAndStore&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fetcher&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.fetch &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;enricher.enrich&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.grouped&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.foreach&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;storage.storeBatch&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.to&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Sink&lt;/span&gt;&lt;span&gt;&lt;span&gt;.discard&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 6&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;We have a new class appropriately called &lt;code&gt;UberJob&lt;/code&gt; (1).&lt;/li&gt;
&lt;li&gt;For now we forget all the lessons we learned about type parameters, and write the code without them.&lt;/li&gt;
&lt;li&gt;The streaming pipeline now starts from &lt;code&gt;fetch&lt;/code&gt;ing (2).&lt;/li&gt;
&lt;li&gt;The data is enriched in a streaming fashion using &lt;code&gt;map&lt;/code&gt; (3).&lt;/li&gt;
&lt;li&gt;We seamlessly create batches using the streaming &lt;code&gt;grouped&lt;/code&gt; operator (4).&lt;/li&gt;
&lt;li&gt;Which we store with &lt;code&gt;storeBatch&lt;/code&gt;, since this is a pure side-effect we use &lt;code&gt;foreach&lt;/code&gt; rather than &lt;code&gt;map&lt;/code&gt; (5).&lt;/li&gt;
&lt;li&gt;To actually run the stream we use the &lt;code&gt;to&lt;/code&gt; operator, which &quot;pulls&quot; the stream into the provided sink (6).&lt;/li&gt;
&lt;li&gt;The argument to &lt;code&gt;to&lt;/code&gt; is a &lt;code&gt;Sink[In, Out]&lt;/code&gt; which is a sink that consumes entries of type &lt;code&gt;In&lt;/code&gt; and produces a single result of type &lt;code&gt;Out&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In this case, since we are only running the stream for its side-effects, the sink is &lt;code&gt;Sink[Unit, Unit]&lt;/code&gt;, which discards the stream&apos;s (&lt;code&gt;Unit&lt;/code&gt;) output&lt;sup&gt;&lt;a href=&quot;#user-content-fn-cleancode&quot; id=&quot;user-content-fnref-cleancode&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Although I promised you that we&apos;ll be improving the production code using type parameters, let&apos;s start first by asking how testable is this code? A good question to ask, since it&apos;s often the case that testable code happens to be better in ways that are not directly related to testing&lt;sup&gt;&lt;a href=&quot;#user-content-fn-surprise&quot; id=&quot;user-content-fnref-surprise&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h2 id=&quot;tests-tests-everywhere&quot;&gt;Tests, Tests Everywhere&lt;/h2&gt;
&lt;p&gt;Well, this code is at least as bad as the original code in the very &lt;a href=&quot;https://blog.daniel-beskin.com/2024-05-09-purify-tests&quot;&gt;first post&lt;/a&gt; in the series. Since all the flow is producing is &lt;code&gt;Unit&lt;/code&gt; the only way to test the logic here is to use some form of mutable mocks. But just like in the first post, we can try to improve testability by (ab)using type parameters, which we will scatter liberally all through the code.&lt;/p&gt;
&lt;p&gt;First the helper traits:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;trait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Fetcher&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Stream&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;trait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Enricher&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;enrich&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;trait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;storeBatch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We now have a type-parameter for every piece of data involved in the flow. Learning the lessons from the previous parts, this should mean that we can avoid mutable mocks (to inspect the outputs of each step) and can use lean fake data (for the inputs of the test).&lt;/p&gt;
&lt;p&gt;Note the &lt;code&gt;Stored&lt;/code&gt; type parameter that &lt;code&gt;Storage&lt;/code&gt; now has. This stands for the &lt;code&gt;Unit&lt;/code&gt; return type that &lt;code&gt;storeBatch&lt;/code&gt; used to have, and should help us in reflecting the flow of data in the test, where we can choose &lt;code&gt;Stored&lt;/code&gt; to be something more informative than &lt;code&gt;Unit&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This leads us to this second version of &lt;code&gt;UberJob&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UberJob&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt;&lt;span&gt;](&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fetcher&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Fetcher&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;enricher&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Enricher&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;storage&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetchAndStore&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fetcher&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.fetch&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;enricher.enrich&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.grouped&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;storage.storeBatch&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.to&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Sink&lt;/span&gt;&lt;span&gt;&lt;span&gt;.discard&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The new code is fairly similar to the one we started with. We use a multitude of type parameters on &lt;code&gt;UberJob&lt;/code&gt; (1). But the only real difference is that we use &lt;code&gt;map&lt;/code&gt; rather than &lt;code&gt;foreach&lt;/code&gt; when calling &lt;code&gt;storeBatch&lt;/code&gt; (2), since the result is a non-&lt;code&gt;Unit&lt;/code&gt; value.&lt;/p&gt;
&lt;p&gt;The promise of all these fake type parameters is better testability. Is this what we got here?&lt;/p&gt;
&lt;p&gt;Well, not quite. We still have &lt;code&gt;Sink.discard&lt;/code&gt; stuck at the very end of our flow. Meaning that the output of &lt;code&gt;fetchAndStore&lt;/code&gt; is still &lt;code&gt;Unit&lt;/code&gt;, and is as untestable as ever&lt;sup&gt;&lt;a href=&quot;#user-content-fn-mutablemocks&quot; id=&quot;user-content-fnref-mutablemocks&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;In the non-streaming variant we could return a single &lt;code&gt;Stored&lt;/code&gt; value to reflect everything we wanted to know about the flow in the test. But in the streaming version we have (potentially) more or less than one &lt;code&gt;Stored&lt;/code&gt; value, one per every batch stored. How do we reflect that in the final output?&lt;/p&gt;
&lt;p&gt;One way to achieve this would be to swap out the &lt;code&gt;Sink&lt;/code&gt; we are using in favor of &lt;code&gt;Sink.toList&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-othersinks&quot; id=&quot;user-content-fnref-othersinks&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;8&lt;/a&gt;&lt;/sup&gt;, a sink that accumulates all of the stream&apos;s output: &lt;code&gt;Sink[A, List[A]]&lt;/code&gt;. If we choose the output of &lt;code&gt;storeBatch&lt;/code&gt; to be &lt;code&gt;List[X]&lt;/code&gt;, where &lt;code&gt;X&lt;/code&gt; contains all the data about a single processed entry&lt;sup&gt;&lt;a href=&quot;#user-content-fn-string&quot; id=&quot;user-content-fnref-string&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;9&lt;/a&gt;&lt;/sup&gt;, we&apos;ll get the  following signature:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetchAndStore&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;X&lt;/span&gt;&lt;span&gt;]]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This solves the testing problems, we can get all the info we need for the test by embedding it inside the &lt;code&gt;List[List[X]]&lt;/code&gt; value, just like we did in previous parts. But this comes at the cost of modifying the production code in a noticeable way. Now, instead of returning &lt;code&gt;Unit&lt;/code&gt; we are going to return a &lt;code&gt;List[Unit]&lt;/code&gt;, needlessly allocating a (potentially) large list just to improve our tests. This is too high a price to pay for a bit of testability.&lt;/p&gt;
&lt;p&gt;We can do better though.&lt;/p&gt;
&lt;h2 id=&quot;is-there-an-algebraic-abstraction-in-the-room&quot;&gt;Is There an Algebraic Abstraction in the Room?&lt;/h2&gt;
&lt;p&gt;The magic of using type parameters for testing stemmed from the fact that we can write the code once and instantiate the type parameters in two different ways. Once for testing and once in a way that&apos;s compatible with the original production code. That was &lt;a href=&quot;https://en.wikipedia.org/wiki/Parametric_polymorphism&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;parametric polymorphism&lt;/a&gt; in action.&lt;/p&gt;
&lt;p&gt;In &lt;code&gt;UberJob&lt;/code&gt; things are different, not only do we need two different return types (&lt;code&gt;Unit&lt;/code&gt; vs. &lt;code&gt;List[...]&lt;/code&gt;), but we also need distinct behaviors, one for each return type (discarding results vs. result accumulation). The whole point of parametric polymorphism is that the code&apos;s behavior is independent of the choice of type parameters. And so parametric polymorphism doesn&apos;t seem to cut it for us now.&lt;/p&gt;
&lt;p&gt;Not all is lost though. Luckily, there are other forms of polymorphism we can take advantage of. Namely &lt;a href=&quot;https://en.wikipedia.org/wiki/Ad_hoc_polymorphism&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;ad hoc polymorphism&lt;/a&gt;, and more specifically &lt;a href=&quot;https://en.wikipedia.org/wiki/Type_class&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;typeclasses&lt;/a&gt;, which allow us to choose different behaviors based on the specific instantiation of a type parameter.&lt;/p&gt;
&lt;p&gt;Our task now is to find an appropriate typeclass and concrete types that capture both our production use case and the test use case. Let&apos;s try to &quot;derive&quot; the typeclass we need from the requirements that we have. Step by step:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Let&apos;s call the input to our final &lt;code&gt;Sink&lt;/code&gt; &lt;code&gt;In&lt;/code&gt; and the final output &lt;code&gt;Out&lt;/code&gt;, i.e., we are working with &lt;code&gt;Sink[In, Out]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In the production use case the requirement is that:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;type In = Unit&lt;/code&gt; - the result of the &lt;code&gt;storeBatch&lt;/code&gt; in production.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;type Out = Unit&lt;/code&gt; - the whole process is a side effect, so we don&apos;t keep anything in memory from it.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Conclusion from &lt;code&gt;2&lt;/code&gt;: in the general case &lt;code&gt;In =:= Out&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-constraint&quot; id=&quot;user-content-fnref-constraint&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;10&lt;/a&gt;&lt;/sup&gt;, we are working with &lt;code&gt;Sink[Unit, Unit]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In the test use case we have:
&lt;ul&gt;
&lt;li&gt;For &lt;code&gt;storeBatch&lt;/code&gt; we choose &lt;code&gt;Stored&lt;/code&gt; to be &lt;code&gt;List[X]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;From &lt;code&gt;3&lt;/code&gt; we know that &lt;code&gt;type In = Out = List[X]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Conclusion from &lt;code&gt;4&lt;/code&gt;: we need a &lt;code&gt;Sink[List[X], List[X]]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The requirements for this &lt;code&gt;Sink&lt;/code&gt; are:
&lt;ul&gt;
&lt;li&gt;We need to be able to handle the &lt;strong&gt;empty&lt;/strong&gt; case, if there&apos;s no data in the stream, the result should be &lt;code&gt;List.empty&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;To be able to assert on all the relevant data, we want to &lt;strong&gt;combine&lt;/strong&gt; all the individual &lt;code&gt;List[X]&lt;/code&gt; values that we get&lt;sup&gt;&lt;a href=&quot;#user-content-fn-nestedlist&quot; id=&quot;user-content-fnref-nestedlist&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;11&lt;/a&gt;&lt;/sup&gt;, we can use &lt;code&gt;++&lt;/code&gt; for that.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Ergo, we need a &lt;code&gt;Sink&lt;/code&gt; that supports the following:
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mysterySink&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;empty&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;X&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;               &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;combine&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;X&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;X&lt;/span&gt;&lt;span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;X&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Sink&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;X&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;X&lt;/span&gt;&lt;span&gt;]]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;This signature has a very familiar shape. If we generalize this signature to arbitrary types, this looks exactly like &lt;code&gt;fold&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-genericfold&quot; id=&quot;user-content-fnref-genericfold&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;12&lt;/a&gt;&lt;/sup&gt;:
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fold&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;](&lt;/span&gt;&lt;span&gt;empty&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;           &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;combine&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Sink&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;In the test this can be called as:
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Sink&lt;/span&gt;&lt;span&gt;&lt;span&gt;.fold&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;&lt;span&gt;.empty&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/span&gt;&lt;span&gt;X&lt;/span&gt;&lt;span&gt;&lt;span&gt;])(&lt;/span&gt;&lt;span&gt;_ &lt;/span&gt;&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;&lt;span&gt; _&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;The generic &lt;code&gt;Sink.fold&lt;/code&gt; can be used with &lt;code&gt;Unit&lt;/code&gt; as well, we just need to choose the appropriate implementations for &lt;code&gt;empty&lt;/code&gt; and &lt;code&gt;combine&lt;/code&gt;:
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;empty&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;combine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;y&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;The types match, we still have a &lt;code&gt;Sink[Unit, Unit]&lt;/code&gt;, but we also avoid in-memory accumulation when running the production code, we just return &lt;code&gt;Unit&lt;/code&gt; on every step.&lt;/li&gt;
&lt;li&gt;To be (ad hoc) polymorphic over our inputs we need to support only two functions, &lt;code&gt;empty&lt;/code&gt; and &lt;code&gt;combine&lt;/code&gt;, since these are the only requirements for using &lt;code&gt;Sink.fold&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Which brings us to the following typeclass:
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;trait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MysteryTypeclass&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;empty&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;combine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;y&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;If you&apos;ve ever been exposed to functional programming, this should ring a bell, we just rediscovered &lt;code&gt;Monoid&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-modularity&quot; id=&quot;user-content-fnref-modularity&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;13&lt;/a&gt;&lt;/sup&gt;:
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;trait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Monoid&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;empty&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;combine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;y&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Both &lt;code&gt;List[X]&lt;/code&gt; and &lt;code&gt;Unit&lt;/code&gt; have a &lt;code&gt;Monoid&lt;/code&gt; implementation, one with &lt;code&gt;List.empty&lt;/code&gt; and &lt;code&gt;++&lt;/code&gt; and the other is the trivial &lt;code&gt;Monoid&lt;/code&gt; with the code from &lt;code&gt;10&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We can use &lt;code&gt;Sink.fold&lt;/code&gt; with any type that has a &lt;code&gt;Monoid&lt;/code&gt; instance:
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;foldMonoid&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Monoid&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Sink&lt;/span&gt;&lt;span&gt;&lt;span&gt;.fold&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;&lt;span&gt;.empty&lt;/span&gt;&lt;span&gt;)(&lt;/span&gt;&lt;span&gt;_ combine _&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And so, using this rather lengthy step by step derivation, we conclude that we can make our code (ad hoc) polymorphic by requiring a &lt;code&gt;Monoid&lt;/code&gt; instance on the &lt;code&gt;Stored&lt;/code&gt; parameter:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UberJob&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Monoid&lt;/span&gt;&lt;span&gt;&lt;span&gt;](&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fetcher&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Fetcher&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;enricher&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Enricher&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;storage&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetchAndStore&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fetcher&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.fetch&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;enricher.enrich&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.grouped&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;storage.storeBatch&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.to&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;summarize&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;summarize&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Sink&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Sink&lt;/span&gt;&lt;span&gt;&lt;span&gt;.foldMonoid&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the final version of &lt;code&gt;UberJob&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We now have a &lt;code&gt;Monoid&lt;/code&gt; constraint on the &lt;code&gt;Stored&lt;/code&gt; type parameter (1).&lt;/li&gt;
&lt;li&gt;Meaning that we can run this code with any concrete instantiation of &lt;code&gt;Stored&lt;/code&gt; as long as it has a &lt;code&gt;Monoid&lt;/code&gt; implementation. The code will adapt its behavior accordingly.&lt;/li&gt;
&lt;li&gt;Instead of &lt;code&gt;Sink.discard&lt;/code&gt; we now call &lt;code&gt;summarize&lt;/code&gt; (3), which is just (a slightly more fluent) way to call &lt;code&gt;Sink.foldMonoid&lt;/code&gt; (4), which compiles thanks to the &lt;code&gt;Monoid&lt;/code&gt; constraint.&lt;/li&gt;
&lt;li&gt;Now the return type of &lt;code&gt;fetchAndStore&lt;/code&gt; is no longer &lt;code&gt;Unit&lt;/code&gt; but rather &lt;code&gt;Stored&lt;/code&gt; (2), a value we can use in a test to collect data without affecting the production logic with &lt;code&gt;Unit&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The promise of ad hoc polymorphism has been fulfilled, with the same code we get different behaviors based on types, matching the requirements of both the production and test scenarios &lt;sup&gt;&lt;a href=&quot;#user-content-fn-differentsinks&quot; id=&quot;user-content-fnref-differentsinks&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;14&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h2 id=&quot;but-is-it-actually-testable&quot;&gt;But Is It Actually Testable?&lt;/h2&gt;
&lt;p&gt;Yes!&lt;/p&gt;
&lt;p&gt;We can now write a purely functional test using an appropriate selection of types and implementations.&lt;/p&gt;
&lt;p&gt;To wit, we select the following types for the test:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Recall that using type parameters for the inputs lets us use &quot;lean mocks&quot;, that is, we can choose whatever we want to be our inputs. In this case we choose everything to be &lt;code&gt;String&lt;/code&gt;. We will augment the contents of the strings as we move between &lt;code&gt;UserData&lt;/code&gt; to &lt;code&gt;EnrichedUserData&lt;/code&gt;, finally reaching &lt;code&gt;Stored&lt;/code&gt; which becomes a &lt;code&gt;List[String]&lt;/code&gt; due to working with batches. Notably, the choice of &lt;code&gt;type Stored = List[String]&lt;/code&gt; fulfills the requirement of having a &lt;code&gt;Monoid&lt;/code&gt; instance.&lt;/p&gt;
&lt;p&gt;Next, we implement the corresponding mocks:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TestFetcher&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Fetcher&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Stream&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Stream&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;data1&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;data2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;data3&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TestEnricher&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Enricher&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;enrich&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;enriched: &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TestStorage&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;storeBatch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;data.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;stored: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&lt;span&gt; _&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We produce some mock data in a stream in the &lt;code&gt;Fetcher&lt;/code&gt; (1). We augment the string with the word &lt;code&gt;enriched&lt;/code&gt; in the &lt;code&gt;Enricher&lt;/code&gt; (2). And finally, for each entry in a batch, &lt;code&gt;Storage&lt;/code&gt; marks it with the word &lt;code&gt;stored&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Our mocks are just pure functions working with lean data, just like we learned to use type parameters in the previous installments.&lt;/p&gt;
&lt;p&gt;Lastly, we have the actual test:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;The uber-service&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;should&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fetch the user data, enrich it, and store the results&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;service&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UberJob&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;TestFetcher&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;TestEnricher&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;TestStorage&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; service.fetchAndStore &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;result shouldBe &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;stored: enriched: data1&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;stored: enriched: data2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;stored: enriched: data3&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;We initialize a new &lt;code&gt;UberJob&lt;/code&gt; with our mocks (1).&lt;/li&gt;
&lt;li&gt;We run &lt;code&gt;fetchAndStore&lt;/code&gt; producing a &lt;code&gt;List[String]&lt;/code&gt; result (2)&lt;/li&gt;
&lt;li&gt;We assert that the &lt;code&gt;List&lt;/code&gt; contains the correct entries (3)&lt;/li&gt;
&lt;li&gt;Each entry contains traces of going through all the steps in the flow: enrichment and storage.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This test is once again a pure function&lt;sup&gt;&lt;a href=&quot;#user-content-fn-listfunction&quot; id=&quot;user-content-fnref-listfunction&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;15&lt;/a&gt;&lt;/sup&gt;, taking inputs and producing deterministic outputs, thanks to the &lt;code&gt;Monoid&lt;/code&gt; instance in the background that accumulates results for us&lt;sup&gt;&lt;a href=&quot;#user-content-fn-foodforthought&quot; id=&quot;user-content-fnref-foodforthought&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;16&lt;/a&gt;&lt;/sup&gt; &lt;sup&gt;&lt;a href=&quot;#user-content-fn-nobatching&quot; id=&quot;user-content-fnref-nobatching&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;17&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;The production code can still work just as before:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;service&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UberJob&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Fetcher&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Default&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Enricher&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Default&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Default&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;service.fetchAndStore &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; produces `Unit`&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;Monoid[Unit]&lt;/code&gt; instance doesn&apos;t accumulate anything, and &lt;code&gt;fetcheAndStore&lt;/code&gt; only performs side-effects ending with a single &lt;code&gt;Unit&lt;/code&gt; value.&lt;/p&gt;
&lt;p&gt;This is all great, but somewhere at the beginning I promised you that the gratuitous addition of type parameters will improve our production code, not just tests. What was that all about?&lt;/p&gt;
&lt;h2 id=&quot;a-product-manager-steps-in&quot;&gt;A Product Manager Steps In...&lt;/h2&gt;
&lt;p&gt;And tells that we that our clients demand observability. More concretely, the data we&apos;re dealing with looks like this:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;userId&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;registered&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Boolean&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;friends&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our clients want to know:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How many users we processed&lt;/li&gt;
&lt;li&gt;How many of them were registered&lt;/li&gt;
&lt;li&gt;What is the average number of friends per user in the current run&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&apos;s a lot of data that we need to collect. How shall we approach this?&lt;/p&gt;
&lt;p&gt;One thing we could do is to scatter some mutable variables in the flow and accumulate the statistics into them. But that would be iffy, because mutable variables suck&lt;sup&gt;&lt;a href=&quot;#user-content-fn-concurrency&quot; id=&quot;user-content-fnref-concurrency&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;18&lt;/a&gt;&lt;/sup&gt;. And also, who wants to change code due to spurious product demands? Especially with such nice and clean looking code, polluting it with secondary concerns such as logging is unwarranted. Add to that the fact that we&apos;ll also have to modify the test we just written.&lt;/p&gt;
&lt;p&gt;Lucky for us, our code is written generically for any instance of a monoid, and monoids are all about data accumulation (namely the &lt;code&gt;combine&lt;/code&gt; function). If we can choose the appropriate monoid for the &lt;code&gt;Stored&lt;/code&gt; type parameter we will get data accumulation for free. No need to modify &lt;code&gt;fetchAndStore&lt;/code&gt;, nor will we need to change any of the existing tests.&lt;/p&gt;
&lt;p&gt;Our first task is to come up with a type that will be accumulate all the data specified in the requirements. Here it is:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Stats&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;count&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;registered&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;friends&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;averageFriends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; friends.toDouble &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; count &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we have one field per a piece of data we want to count (1). Additionally we have a method to compute the average number of friends given the totals in the fields (2)&lt;sup&gt;&lt;a href=&quot;#user-content-fn-avgmonoid&quot; id=&quot;user-content-fnref-avgmonoid&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;19&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;The next step is to provide a monoid instance for this type. For that we can just rely on the fact that integers form a monoid under addition, and a &lt;code&gt;0&lt;/code&gt; as the empty value:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;given&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Monoid&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Stats&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;empty&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Stats&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;combine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Stats&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;y&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Stats&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Stats&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Stats&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;x.count &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; y.count, x.registered &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; y.registered, x.friends &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&lt;span&gt; y.friends&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We also need a way to collect a summary from an &lt;code&gt;EnrichedUserData&lt;/code&gt; instance:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fromUserData&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Stats&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Stats&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;count &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;registered &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; data.registered &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;friends &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; data.friends.length&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Each entry counts for &lt;code&gt;1&lt;/code&gt; in the summary, when we combine &lt;code&gt;Stats&lt;/code&gt; instances we&apos;ll eventually get the full count of entries (1).&lt;/li&gt;
&lt;li&gt;We only add to the &lt;code&gt;registered&lt;/code&gt; field if the current user is actually registered (2).&lt;/li&gt;
&lt;li&gt;For every entry we accumulate the sum total of friends, ignoring the actual values (3).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Lastly, since we operate in batches, we&apos;ll actually need to create a &lt;code&gt;Stats&lt;/code&gt; instance per batch:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fromBatch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Stats&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Monoid&lt;/span&gt;&lt;span&gt;&lt;span&gt;.foldMap&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;)(&lt;/span&gt;&lt;span&gt;fromUserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here &lt;code&gt;foldMap&lt;/code&gt; is a generic monoid function&lt;sup&gt;&lt;a href=&quot;#user-content-fn-monoidfunction&quot; id=&quot;user-content-fnref-monoidfunction&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;20&lt;/a&gt;&lt;/sup&gt; that converts each &lt;code&gt;EnrichedUserData&lt;/code&gt; list entry into a &lt;code&gt;Stats&lt;/code&gt; instance, and then accumulates the results into a single &lt;code&gt;Stats&lt;/code&gt; value using &lt;code&gt;combine&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With all that in place we can participate in the monoids game we have going on here. The only thing that&apos;s missing is an actual &lt;code&gt;Storage&lt;/code&gt; that produces &lt;code&gt;Stats&lt;/code&gt; values instead of &lt;code&gt;Unit&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here it is:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;WithStats&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Stats&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;storeBatch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Stats&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;println&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Storing: [&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;&lt;span&gt;data.map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_.userId&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.mkString&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Stats&lt;/span&gt;&lt;span&gt;&lt;span&gt;.fromBatch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;This implementation chooses &lt;code&gt;Stats&lt;/code&gt; for its output, which is compatible with the contract of &lt;code&gt;UberJob&lt;/code&gt; as we defined a &lt;code&gt;Monoid&lt;/code&gt; instance for &lt;code&gt;Stats&lt;/code&gt; (1).&lt;/li&gt;
&lt;li&gt;Accordingly, the output of &lt;code&gt;storeBatch&lt;/code&gt; is now &lt;code&gt;Stats&lt;/code&gt; (2).&lt;/li&gt;
&lt;li&gt;For demonstration purposes we simulate data storage with an informative &lt;code&gt;println&lt;/code&gt; that shows us the currently stored batch (3).&lt;/li&gt;
&lt;li&gt;And then we summarize the current batch into a single &lt;code&gt;Stats&lt;/code&gt; value (4).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Done! We have all the pieces in place to produce statistics about our job.&lt;/p&gt;
&lt;p&gt;Here&apos;s a sample run:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;service&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UberJob&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Fetcher&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Default&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Enricher&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Default&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;WithStats&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stats&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; service.fetchAndStore &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;println&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;---- Done ----&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;println&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;stats&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;In this very simplified running script we instantiate an &lt;code&gt;UberJob&lt;/code&gt; (1).&lt;/li&gt;
&lt;li&gt;We swap the default &lt;code&gt;Storage&lt;/code&gt; instance with the new &lt;code&gt;WithStats&lt;/code&gt; implementation (2).&lt;/li&gt;
&lt;li&gt;Then we run as usual, but unlike the original production code, the result is no longer &lt;code&gt;Unit&lt;/code&gt;, but rather a &lt;code&gt;Stats&lt;/code&gt; value (3).&lt;/li&gt;
&lt;li&gt;We can further process the stats for the run, by e.g., sending them to some monitoring dashboard. Here we just print the stats to the console (4).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A sample output for running this code might look like the following&lt;sup&gt;&lt;a href=&quot;#user-content-fn-runcommand&quot; id=&quot;user-content-fnref-runcommand&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;21&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Storing: [a, b, c, d, e]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Storing: [f, g, h, i, j]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Storing: [k]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;---- Done ----&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Stats(count = 11, registered = 6, friends = 31, avgFriends = 2.8181818181818183)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can see here how each batch is being stored, and at the very end we have all the statistics that we required.&lt;/p&gt;
&lt;p&gt;Notice what we didn&apos;t have to do. We didn&apos;t further modify the code of &lt;code&gt;UberJob&lt;/code&gt;, nor did we have to modify the test we&apos;ve written. Everything just works due to the code being parameterized with the &lt;code&gt;Monoid&lt;/code&gt; constraint, and the &lt;code&gt;Monoid&lt;/code&gt; implementation for &lt;code&gt;Stats&lt;/code&gt; doing the right thing&lt;sup&gt;&lt;a href=&quot;#user-content-fn-moreteste&quot; id=&quot;user-content-fnref-moreteste&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;22&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Adding a type parameter gained us flexibility that turns out to be useful beyond just tests. The type parameter revealed an essential truth about our code, a truth that we can use as requirements evolve. In this case the truth we got is about the accumulating nature of our flow. Constraining with different typeclasses can reveal different capabilities of the code.&lt;/p&gt;
&lt;p&gt;The way we got to this code structure is also interesting. All we did was to try and improve the testability of the code, but what we got in return is more flexible and maintainable flow. This points to the even more general idea that it can be sometimes useful to think about code in a context that is more general then the one being currently solved for. Using type parameters is one (very powerful) way that we can remove code from its specific context. Even if you don&apos;t end up using type parameters in your final code, just thinking about code that way can also be beneficial.&lt;/p&gt;
&lt;p&gt;Although the transformation to using type parameters was a bit more involved this time, with some practice you too will be able to spot those patterns and gain the benefits of parametric code.&lt;/p&gt;
&lt;p&gt;That&apos;s it for this time. If you come up with any other benefits of using type parameters for testability, please reach out.&lt;/p&gt;
&lt;p&gt;Happy parametrizing!&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-github&quot;&gt;
&lt;p&gt;The full code for the examples is available on &lt;a href=&quot;https://github.com/ncreep/purify-tests-blog&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-github&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-stream&quot;&gt;
&lt;p&gt;I&apos;m not specifying the particular streaming library that we are using here. The example code can be adapted to any reasonable streaming library. Although the code below looks a bit like (a blocking version of) Akka Streams, the ideas will apply to any library that uses explicit effects (like fs2 and ZIO Streams). You can see the concrete (toy) implementation &lt;a href=&quot;https://github.com/ncreep/purify-tests-blog/blob/master/stream.scala&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-stream&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-bookkeeping&quot;&gt;
&lt;p&gt;It&apos;s almost as if it was there only for pedagogic reasons to begin with. &lt;a href=&quot;#user-content-fnref-bookkeeping&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-sinkstore&quot;&gt;
&lt;p&gt;Seeing the way we use streaming in the code that follows, it could be argued that we can leverage streaming abstractions to create a streaming version of the &lt;code&gt;Storage&lt;/code&gt; trait that can hide away the batching logic as an implementation detail, while only exposing an interface that receives entries one by one. Streaming is indeed quite a powerful code structuring method. We won&apos;t be doing that here so as to not get too bogged down in streaming mechanics. &lt;a href=&quot;#user-content-fnref-sinkstore&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-cleancode&quot;&gt;
&lt;p&gt;Notice how clean streaming makes this code, imagine doing this kind of batching &quot;by hand&quot;. There are many benefits to using streaming (like how easy it would be to parallelize this code), but that&apos;s a topic for another post. &lt;a href=&quot;#user-content-fnref-cleancode&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-surprise&quot;&gt;
&lt;p&gt;Surprising exactly no one... &lt;a href=&quot;#user-content-fnref-surprise&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 6&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-mutablemocks&quot;&gt;
&lt;p&gt;Without mutable mocks that is. &lt;a href=&quot;#user-content-fnref-mutablemocks&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 7&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-othersinks&quot;&gt;
&lt;p&gt;We could take another approach where the final &lt;code&gt;Sink&lt;/code&gt; is passed in as a parameter to &lt;code&gt;UberJob&lt;/code&gt;. But doing that will still force us to use a mutable mock for the test sink, one that mutably accumulates results. So we don&apos;t count that as a solution to the problem that we posed. &lt;a href=&quot;#user-content-fnref-othersinks&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 8&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-string&quot;&gt;
&lt;p&gt;E.g., in the tests we can choose &lt;code&gt;X&lt;/code&gt; to be &lt;code&gt;String&lt;/code&gt;. &lt;a href=&quot;#user-content-fnref-string&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 9&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-constraint&quot;&gt;
&lt;p&gt;The production case constrains the general case. &lt;a href=&quot;#user-content-fnref-constraint&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 10&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-nestedlist&quot;&gt;
&lt;p&gt;That&apos;s why we had &lt;code&gt;List[List[X]]&lt;/code&gt; before. &lt;a href=&quot;#user-content-fnref-nestedlist&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 11&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-genericfold&quot;&gt;
&lt;p&gt;The actual signature in the repo is slightly more general than this, more similar to &lt;code&gt;foldLeft[A, B]&lt;/code&gt; &lt;a href=&quot;#user-content-fnref-genericfold&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 12&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-modularity&quot;&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/ncreep/purify-tests-blog/blob/master/monoid.scala&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;actual code&lt;/a&gt; in the repo uses a slightly different syntax, using the currently experimental &lt;a href=&quot;https://scala-lang.org/api/3.6.2/docs/docs/reference/experimental/typeclasses.html&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&quot;better support for typeclasses&quot;&lt;/a&gt; and support for &lt;code&gt;infix&lt;/code&gt; operations, but the essence is still the same. &lt;a href=&quot;#user-content-fnref-modularity&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 13&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-differentsinks&quot;&gt;
&lt;p&gt;It could be argued that we are still paying a bit of a tax for testability as we are needlessly &quot;combining&quot; unit values, something that wasn&apos;t happening in the original code. Although I don&apos;t think it really matters for any practical purposes, we can try to tackle this by parametrizing over the final &lt;code&gt;Sink&lt;/code&gt;. Once passing a &lt;code&gt;Sink.discard&lt;/code&gt; and once passing in &lt;code&gt;Sink.foldMonoid&lt;/code&gt;. That might be okay, but would mean that we are not testing an important feature of the production flow: that the logic of &lt;code&gt;Sink.discard&lt;/code&gt; does the right thing for our purposes. This is not an issue in the approach described in the main text as the code uses the same sink in both cases. And if you keep on reading we&apos;ll see another reason to favor the &lt;code&gt;Monoid&lt;/code&gt; approach later on. &lt;a href=&quot;#user-content-fnref-differentsinks&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 14&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-listfunction&quot;&gt;
&lt;p&gt;If we go a step further and parameterize over the choice of streaming type, then given we don&apos;t use any too advanced streaming tools, we can choose &lt;code&gt;List&lt;/code&gt; to stand for the stream in the test. Making the whole test a pure function over lists, which is something that&apos;s even easier to write tests for. &lt;a href=&quot;#user-content-fnref-listfunction&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 15&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-foodforthought&quot;&gt;
&lt;p&gt;Food for thought: our code is using monoids, and the test code is relying on lists, which are the &quot;&lt;a href=&quot;https://en.wikipedia.org/wiki/Free_monoid&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;free monoid&lt;/a&gt;&quot;. It seems that more generally &lt;a href=&quot;https://en.wikipedia.org/wiki/Free_object&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;free structures&lt;/a&gt; are useful for testing. &lt;a href=&quot;#user-content-fnref-foodforthought&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 16&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-nobatching&quot;&gt;
&lt;p&gt;The eagle-eyed readers amongst you might notice that this doesn&apos;t check that batching was properly performed. I leave it as an exercise for the reader to choose the appropriate &lt;code&gt;Monoid&lt;/code&gt; instance that can check that and amend the test accordingly. &lt;a href=&quot;#user-content-fnref-nobatching&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 17&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-concurrency&quot;&gt;
&lt;p&gt;Although not specifically relevant to the current example, but if we add concurrency into the flow (like with some kind &lt;code&gt;mapAsync&lt;/code&gt;), things get even more complicated when we mix mutability with concurrency. &lt;a href=&quot;#user-content-fnref-concurrency&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 18&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-avgmonoid&quot;&gt;
&lt;p&gt;It&apos;s an unfortunate fact of life that the averaging operation is not itself a monoidal action. We cannot, in general, combine two averages two produce a new average value. &lt;a href=&quot;#user-content-fnref-avgmonoid&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 19&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-monoidfunction&quot;&gt;
&lt;p&gt;One of the biggest advantages of using generic abstractions such as monoids in our code is that we gain access to a wealth of library functions that work with these abstractions, for free.
[^The collection &quot;posts&quot; does not exist or is empty. Please check your content config file for errors.] &lt;a href=&quot;#user-content-fnref-monoidfunction&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 20&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-runcommand&quot;&gt;
&lt;p&gt;If you&apos;re following along with the repo, you can run this yourself with the command:&lt;/p&gt;
&lt;pre data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;scala-cli&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--main-class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ncreep.streaming_purified.runUberJobStats&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;a href=&quot;#user-content-fnref-runcommand&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 21&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-moreteste&quot;&gt;
&lt;p&gt;Of course one would have to write tests for the new code we just written (the monoid instance and the new &lt;code&gt;Storage&lt;/code&gt; implementation). But that&apos;s the beauty of the modularity that we gained: the new tests are completely self-contained and only need to deal with the new code. &lt;a href=&quot;#user-content-fnref-moreteste&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 22&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><author>Daniel Beskin</author></item><item><title>ANY Overloading Considered Evil</title><link>https://blog.daniel-beskin.com/2024-06-19-overloading</link><guid isPermaLink="true">https://blog.daniel-beskin.com/2024-06-19-overloading</guid><description>This is not just clickbait, I really do mean that ANY form overloading in programming should be considered evil.</description><pubDate>Wed, 19 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://blog.daniel-beskin.com/social-cards/2024-06-19-overloading.png&quot; alt=&quot;Cover image for ANY Overloading Considered Evil&quot; style=&quot;width: 100%; height: auto; margin-bottom: 2rem;&quot;&gt;&lt;p&gt;This is not just clickbait, I really do mean that ANY form overloading in programming should be considered evil.&lt;/p&gt;
&lt;p&gt;By &quot;overloading&quot; I shall refer to any instance of using one name to refer to multiple different things&lt;sup&gt;&lt;a href=&quot;#user-content-fn-overriding&quot; id=&quot;user-content-fnref-overriding&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. A common example would be operator overloading, where the operator &lt;code&gt;+&lt;/code&gt; can refer both to integer addition and to floating point addition.&lt;/p&gt;
&lt;p&gt;Although I cannot possibly make an exhaustive list of all possible means of overloading, I shall try to illustrate this point with a few examples of different forms of it&lt;sup&gt;&lt;a href=&quot;#user-content-fn-better&quot; id=&quot;user-content-fnref-better&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. Hopefully, by the end of this post you too will be of the opinion that overloading is quite evil. Though possibly a form of necessary evil.&lt;/p&gt;
&lt;p&gt;On to our first example.&lt;/p&gt;
&lt;h2 id=&quot;operator-overloading&quot;&gt;Operator Overloading&lt;/h2&gt;
&lt;p&gt;Overloading the meaning of arithmetic operators is so common that we don&apos;t even notice it. Even in high-school one can find the &lt;code&gt;+&lt;/code&gt; operator overloaded to mean both real number addition and complex number addition.&lt;/p&gt;
&lt;p&gt;Despite this innocuous seeming usage, in sufficiently complex applications, evil may lurk. Consider the following &lt;sup&gt;&lt;a href=&quot;#user-content-fn-contrived&quot; id=&quot;user-content-fnref-contrived&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;record Foo (int bar, double baz) &lt;/span&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;record Bar (int qux) &lt;/span&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;double&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;doStuff&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;Foo&lt;/span&gt;&lt;span&gt; foo&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Bar&lt;/span&gt;&lt;span&gt; bar&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;foo&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;bar&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bar&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;qux&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;foo&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;baz&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this code we have two records &lt;code&gt;Foo&lt;/code&gt; (1) and &lt;code&gt;Bar&lt;/code&gt; (2), containing some numbers. We then have a method &lt;code&gt;doStuff&lt;/code&gt; (3) that takes instances of &lt;code&gt;Foo&lt;/code&gt; and &lt;code&gt;Bar&lt;/code&gt; and does some arithmetic (4). This code looks confusing, and that&apos;s on purpose. In a large enough codebase, following things might get difficult.&lt;/p&gt;
&lt;p&gt;Now we can invoke this code as follows:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;doStuff&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Foo&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Bar&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the result will be:&lt;/p&gt;
&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;0.0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is because we&apos;re doing integer division with &lt;code&gt;1&lt;/code&gt; and &lt;code&gt;3&lt;/code&gt;. Nothing particularly interesting here. But now imagine that we are refactoring our code and deciding for some reason that the &lt;code&gt;qux&lt;/code&gt; field should actually be a &lt;code&gt;double&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;record Bar (double qux) &lt;/span&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Can you guess what the very same call to &lt;code&gt;doStuff&lt;/code&gt; will return now?&lt;/p&gt;
&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;0.6666666666666666&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As we changed the type of &lt;code&gt;qux&lt;/code&gt; to &lt;code&gt;double&lt;/code&gt; the meaning of the &lt;code&gt;/&lt;/code&gt; operator changed from integer division to floating point division. And so the result is now completely different.&lt;/p&gt;
&lt;p&gt;Whether this is a bug in our application or not depends on context. But the point here is that we weren&apos;t warned about it in any way. The meaning of &lt;code&gt;/&lt;/code&gt; changed silently without any kind of compile-time warning.&lt;/p&gt;
&lt;p&gt;And given that the definition of &lt;code&gt;doStuff&lt;/code&gt; can be far away from the definition of &lt;code&gt;Bar&lt;/code&gt;, this is also an example of spooky action at a distance. Nobody likes spooky action at a distance...&lt;/p&gt;
&lt;p&gt;It&apos;s reasonable for things to change when changing types, but it&apos;s not reasonable for it to happen silently and only be visible at run time.&lt;/p&gt;
&lt;p&gt;The culprit here is the overloaded meaning of &lt;code&gt;/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let&apos;s move on to the next example.&lt;/p&gt;
&lt;h2 id=&quot;literal-overloading&quot;&gt;Literal Overloading&lt;/h2&gt;
&lt;p&gt;This was hinted at in the previous section. Note this call:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;doStuff&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Foo&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Bar&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What are the types of &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;2&lt;/code&gt;, and &lt;code&gt;3&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;The answer is: it depends. Depends on the expected types of the arguments to the &lt;code&gt;Foo&lt;/code&gt; and &lt;code&gt;Bar&lt;/code&gt; constructors.&lt;/p&gt;
&lt;p&gt;As we changed the type of &lt;code&gt;Bar.qux&lt;/code&gt; the meaning of &lt;code&gt;3&lt;/code&gt; changed as well. Yet again, silently. If we had some compile-time warning about that change, we might&apos;ve caught the change in the &lt;code&gt;doStuff&lt;/code&gt; call. But alas, that&apos;s not the case. As the meaning of integer numeric literals is overloaded, we just silently changed behavior at run time.&lt;/p&gt;
&lt;p&gt;I won&apos;t dwell on this point, as from my experience code usually doesn&apos;t rely too much on literals beyond tests and examples. And in any case, in most languages literal overloading is usually fairly limited&lt;sup&gt;&lt;a href=&quot;#user-content-fn-literalhaskell&quot; id=&quot;user-content-fnref-literalhaskell&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h2 id=&quot;method-overloading&quot;&gt;Method Overloading&lt;/h2&gt;
&lt;p&gt;A more common example of overloading, at least in the Java world, is the classic method overloading. That&apos;s when one class contains multiple methods with the same name but different type signatures. To wit, &lt;code&gt;Arrays.sort&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sort&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt; a&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sort&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;long&lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt; a&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sort&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;char&lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt; a&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sort&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;byte&lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt; a&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sort&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;double&lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt; a&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, there are many overloads of the &lt;code&gt;sort&lt;/code&gt; method, accommodating different array types. All aptly named &lt;code&gt;sort&lt;/code&gt; for convenience&apos;s sake.&lt;/p&gt;
&lt;p&gt;Now imagine the following scenario. You have an array of prices, represented as &lt;code&gt;double[]&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-doubleprice&quot; id=&quot;user-content-fnref-doubleprice&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;. And you have some complicated logic to process them like so:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;processPrices&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;double&lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt; prices&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; ... do stuff with prices&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Arrays&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;sort&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; ... do more stuff with prices&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Deep inside the processing logic we have an &lt;code&gt;Arrays.sort&lt;/code&gt; hiding. But that&apos;s okay, it all works perfectly fine:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;double&lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;processPrices&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After running &lt;code&gt;processPrices&lt;/code&gt; the contents of &lt;code&gt;prices&lt;/code&gt; is as expected:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;2.0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;4.0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;5.0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Move along, nothing to see here...&lt;/p&gt;
&lt;p&gt;But then we think to ourselves, is it good practice to represent business entities like &quot;price&quot; using raw doubles? Of course not.&lt;/p&gt;
&lt;p&gt;Instead we decide to make a small domain wrapper record to represent the concept of &quot;price&quot; in our system:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;record&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Price&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;double&lt;/span&gt;&lt;span&gt; value&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a good practice, as we now have a point of reference and a single source of truth for anything that relates to the concept of &quot;price&quot; in our system.&lt;/p&gt;
&lt;p&gt;As part of this refactor we now have to change the &lt;code&gt;processPrices&lt;/code&gt; method as well:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;processPrices&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Price&lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt; prices&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; ... the rest as before...&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Great, this now all compiles and we can run the code from before:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Price&lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Price&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Price&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Price&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;processPrices&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Any guesses as to the result of running this code?&lt;/p&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Exception in thread &quot;main&quot; java.lang.ClassCastException: class overloading_post.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Price cannot be cast to class java.lang.Comparable (Price is in unnamed module of loader &apos;app&apos;; java.lang.Comparable is in module java.base of loader &apos;bootstrap&apos;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;at java.base/java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:320)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Right, we got a class cast exception. That makes prefect sense.&lt;/p&gt;
&lt;p&gt;How did that happen? Well, there is no &lt;code&gt;Arrays.sort&lt;/code&gt; overload for &lt;code&gt;Price&lt;/code&gt;, but one of the overloads is this one:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sort&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt; a&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It takes &lt;em&gt;any&lt;/em&gt; array of &lt;code&gt;Object&lt;/code&gt;s and tries to sort them. How can you possibly sort &lt;code&gt;Object&lt;/code&gt;s? Obviously you can&apos;t as &lt;code&gt;Object&lt;/code&gt; doesn&apos;t have any sorting methods. What you can do instead is to cross your fingers and cast an &lt;code&gt;Object&lt;/code&gt; to &lt;code&gt;Comparable&lt;/code&gt; and then sort that.&lt;/p&gt;
&lt;p&gt;In our case though, &lt;code&gt;Price&lt;/code&gt; did not implement comparable, and the cast failed spectacularly.&lt;/p&gt;
&lt;p&gt;Now a lot of bad things are going on here. Using &lt;code&gt;Object&lt;/code&gt; directly, array covariance, relying on casting &lt;sup&gt;&lt;a href=&quot;#user-content-fn-oldjava&quot; id=&quot;user-content-fnref-oldjava&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;, and of course, method overloading.&lt;/p&gt;
&lt;p&gt;And yet, method overloading in Java is determined statically, the compiler knew that we just changed the type from &lt;code&gt;double&lt;/code&gt; to &lt;code&gt;Price&lt;/code&gt;, and it determined, at compile-time, to use the &lt;code&gt;Object&lt;/code&gt; overload of &lt;code&gt;sort&lt;/code&gt;. In another world the same compiler making all these decisions could&apos;ve warned us that we are doing something suspicious, and ask us to sign off on that&lt;sup&gt;&lt;a href=&quot;#user-content-fn-wartremover&quot; id=&quot;user-content-fnref-wartremover&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;. This, sadly, is not the world we live in.&lt;/p&gt;
&lt;p&gt;The things we do for a bit of overloaded convenience&lt;sup&gt;&lt;a href=&quot;#user-content-fn-methodoverloadingexception&quot; id=&quot;user-content-fnref-methodoverloadingexception&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;8&lt;/a&gt;&lt;/sup&gt;...&lt;/p&gt;
&lt;p&gt;To paraphrase Benjamin Franklin:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Those who would give up essential Safety, to purchase a little temporary Convenience, deserve neither Safety nor Convenience.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;runtime-polymorphism&quot;&gt;Runtime Polymorphism&lt;/h2&gt;
&lt;p&gt;A staple of Object Oriented Programming, runtime polymorphism is arguably one of the most characteristic features of doing OOP. But it too falls under my loose definition of &quot;overloading&quot; given above: we are using a single name for an (abstract) method to provide two or more different implementations.&lt;/p&gt;
&lt;p&gt;Unlike the examples above, runtime polymorphism, as the name implies only happens at runtime, and is not determined at compile-time. As a result, chaos must obviously ensue.&lt;/p&gt;
&lt;p&gt;Continuing our &lt;code&gt;Price&lt;/code&gt; example from above, suppose we need to do some further processing: we have to avoid using illegal prices (like negative ones). We also decided that using array is so 90s. We should modernize and program to interface, the &lt;code&gt;List&lt;/code&gt; interface.&lt;/p&gt;
&lt;p&gt;As a result we have this fine method:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;removeIllegalPrices&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Price&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; prices&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;removeIf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;price &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;price&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we can use it like so:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Price&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ArrayList&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&gt;&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Price&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Price&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Price&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;removeIllegalPrices&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you might expect, the result will be:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Price&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;1.0&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Price&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;2.0&lt;/span&gt;&lt;span&gt;]]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, plain, old, boring Java code.&lt;/p&gt;
&lt;p&gt;But then yet again the winds of change are blowing. Somehow you ended up with a copy of that newfangled book everybody&apos;s talking about, &lt;a href=&quot;https://www.amazon.com/Effective-Java-Joshua-Bloch/dp/0134685997&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;Effective Java&lt;/a&gt;, and you read about item 15&lt;sup&gt;&lt;a href=&quot;#user-content-fn-effectivejavaedition&quot; id=&quot;user-content-fnref-effectivejavaedition&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;9&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Minimize mutability&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sure enough, that rings true in the depths of your immutable soul, and you go on implementing this in your code:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Price&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ArrayList&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&gt;&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Price&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Price&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Price&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;prices &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Collections&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;unmodifiableList&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There you go, item 15 successfully applied. And what&apos;s super convenient is that &lt;code&gt;unmodifiableList&lt;/code&gt; returns an instance of the same &lt;code&gt;List&lt;/code&gt; interface. So we can happily keep on using it like we did before.&lt;/p&gt;
&lt;p&gt;I hope that by now you see where the path of convenience is leading us...&lt;/p&gt;
&lt;p&gt;Running this:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;removeIllegalPrices&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;prices&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Can only end in tears:&lt;/p&gt;
&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Exception in thread &quot;main&quot; java.lang.UnsupportedOperationException&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;at java.base/java.util.Collections$UnmodifiableCollection.removeIf(Collections.java:1096)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;remove&lt;/code&gt; method (and by extension &lt;code&gt;removeIf&lt;/code&gt; method) on &lt;code&gt;List&lt;/code&gt; is overloaded to mean different things depending on the concrete runtime implementation. In one case it removes an element, in another it throws an exception. All these two scenarios share in common is a name. And that&apos;s plenty enough to wreak havoc in our code.&lt;/p&gt;
&lt;p&gt;Silent breakage haunts us yet again. This time, though, we can&apos;t blame the compiler. Runtime polymorphism was not meant to be tracked at compile-time. The culprit is the (runtime) overloaded meaning of the &lt;code&gt;remove&lt;/code&gt; method.&lt;/p&gt;
&lt;h2 id=&quot;just-random-things-breaking-all-over&quot;&gt;Just Random Things Breaking All Over&lt;/h2&gt;
&lt;p&gt;Okay, okay. This all sucks. But maybe, maybe it&apos;s just this outdated Object Oriented thing? Who does that anymore? I hear that all the young people are doing something called &quot;Functional Oriented Programming&quot;, or some such. Surely this kind of legacy mess doesn&apos;t happen there.&lt;/p&gt;
&lt;p&gt;We sure can join that party. Except we are still in the confines of our enterprise cage. So baby steps, baby steps. Let&apos;s use &lt;a href=&quot;https://github.com/functionaljava/functionaljava&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;Functional Java&lt;/a&gt;. It&apos;s quite an oldie, so maybe our corporate masters won&apos;t object too much.&lt;/p&gt;
&lt;p&gt;You&apos;re really sold on the whole functional thing. And you learned to &lt;a href=&quot;https://www.youtube.com/watch?v=PSh7JUfDstE&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&quot;make illegal state unrepresentable&quot;&lt;/a&gt;. As a result you&apos;re using the &lt;a href=&quot;https://javadoc.io/static/org.functionaljava/functionaljava/5.0/fj/data/NonEmptyList.html&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;NonEmptyList&lt;/code&gt;&lt;/a&gt; type religiously. This way you can enforce the presence of at least one item in your lists at compile-time, which can be handy for some business requirements.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;record&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Debtor&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;NonEmptyList&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Payment&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; payments&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Payment&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;maxPayment&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;payments&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;maximum&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Payment&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ord&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As unfortunately we are working for an evil corporation, we have to model some debtors. A &lt;code&gt;Debtor&lt;/code&gt; has a list of payments owed to us. And here we make an illegal state unrepresentable: the list of payments must be non-empty, as otherwise there is no actual debt. This is modeled by the &lt;code&gt;NonEmptyList&lt;/code&gt; type (1). We are enforcing at compile-time the business requirement we have in our code. And all is well.&lt;/p&gt;
&lt;p&gt;Furthermore, this &lt;code&gt;Debtor&lt;/code&gt; entity has a utility method &lt;code&gt;maxPayment&lt;/code&gt; that returns the maximum from the list of payments owed by the debtor (2). We use this for reporting purposes&lt;sup&gt;&lt;a href=&quot;#user-content-fn-depress&quot; id=&quot;user-content-fnref-depress&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;10&lt;/a&gt;&lt;/sup&gt;. Notice that unlike the legacy Java code, despite being able to invoke &lt;code&gt;maximum&lt;/code&gt; on any instance of &lt;code&gt;NonEmptyList&lt;/code&gt; we are obligated to provide an &lt;code&gt;Ord&lt;/code&gt;ering instance that tells the list how to actually choose the maximum. So no unsafe casting is involved.&lt;/p&gt;
&lt;p&gt;Even more importantly though, this is completely safe. Since the list is enforced to be non-empty at the type level, the call to &lt;code&gt;maximum&lt;/code&gt; can never fail, there must be at least one element in our list.&lt;/p&gt;
&lt;p&gt;Safety... What a breath of fresh air. Enter the product manager...&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Well actually, there&apos;s a new business feature, we now sometimes consider a person a debtor without an actual debt. Don&apos;t ask questions, it was all cleared with the accountants.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Right.&lt;/p&gt;
&lt;p&gt;Our careful modelling now goes down the drain, and we obediently replace our &lt;code&gt;NonEmptyList&lt;/code&gt; with the &lt;a href=&quot;https://www.javadoc.io/doc/org.functionaljava/functionaljava/latest/fj/data/List.html&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;List&lt;/code&gt;&lt;/a&gt; type from Functional Java. As we still want to retain that immutable functional goodness with all the cool data processing APIs.&lt;/p&gt;
&lt;p&gt;Now we have this:&lt;/p&gt;
&lt;pre data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;record&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Debtor&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Payment&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; payments&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; the rest as before&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Where the &lt;code&gt;List&lt;/code&gt; type here is not the builtin one, but rather &lt;code&gt;fj.data.List&lt;/code&gt;. We can now support the new business requirement of debtless debtors. Well done.&lt;/p&gt;
&lt;p&gt;Some time later...&lt;/p&gt;
&lt;p&gt;Our reporting mechanism is randomly crashing. You dig into the logs and find this:&lt;/p&gt;
&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Exception in thread &quot;main&quot; java.lang.Error: Undefined: foldLeft1 on empty list&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;at fj.Bottom.error(Bottom.java:29)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;at fj.data.List.foldLeft1(List.java:972)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;at fj.data.List.maximum(List.java:1624)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Recall our cool &lt;code&gt;maximum&lt;/code&gt; method? Well apparently it&apos;s not safe for empty lists, as you cannot in any way produce an actual element (maximal or not). And yet the &lt;code&gt;fd.data.List&lt;/code&gt; class defines the &lt;code&gt;maximum&lt;/code&gt; method for, um, convenience. The reports for our debtless debtors now trigger &lt;code&gt;maximum&lt;/code&gt; on an empty &lt;code&gt;List&lt;/code&gt; and the result is this exception.&lt;/p&gt;
&lt;p&gt;We can question the choice of having the patently unsafe &lt;code&gt;maximum&lt;/code&gt; method on &lt;code&gt;List&lt;/code&gt;. But be that as it may, the real culprit here is, yet again, overloading. &lt;code&gt;List&lt;/code&gt; and &lt;code&gt;NonEmptyList&lt;/code&gt; are completely unrelated classes, they only share and  overload the method name &lt;code&gt;maximum&lt;/code&gt; &quot;by coincidence&quot;. There&apos;s no common interface that forces them to do that. As we changed the type of the &lt;code&gt;payments&lt;/code&gt; field we silently changed the meaning of &lt;code&gt;maximum&lt;/code&gt; in our code. The compiler knew what we were doing but chose to let us shoot ourselves in the foot.&lt;/p&gt;
&lt;p&gt;The silence of the compiler is deafening...&lt;/p&gt;
&lt;h2 id=&quot;and-now-for-something-completely-different&quot;&gt;And Now For Something Completely Different&lt;/h2&gt;
&lt;p&gt;You&apos;re finally tired of all these Java shenanigans and your enterprise cage and decide to move to a hip new startup.&lt;/p&gt;
&lt;p&gt;What a wondrous world, they use the very niche Haskell language. The pinnacle of &quot;functional oriented programming&quot; and pretty much the polar opposite of Java, in being both principled and safe.&lt;/p&gt;
&lt;p&gt;Even better, function overloading is forbidden in Haskell. Every imported function name must refer to exactly one function, otherwise you get a compile-time ambiguity error. It might be a bit tedious to muck around with all the qualified imports, but at least now you&apos;re safe from the woes of overloading.&lt;/p&gt;
&lt;p&gt;But one day you discover that actually, Haskell supports a form of &lt;a href=&quot;https://wiki.haskell.org/Polymorphism#Ad-hoc_polymorphism&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;ad-hoc polymorphism&lt;/a&gt; called &quot;type classes&quot;. And the moment you see a footgun you quickly go and shoot yourself in the foot.&lt;/p&gt;
&lt;p&gt;As follows&lt;sup&gt;&lt;a href=&quot;#user-content-fn-nonemptyalias&quot; id=&quot;user-content-fnref-nonemptyalias&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;11&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;newtype&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Debtor&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Debtor&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;payments&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NonEmptyList&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Payment&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;maxPayment&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Debtor&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Payment&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;maxPayment &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; maximum &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt; payments &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Turns out you work for a debt collection startup...&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We use AI and the blockchain to make debt collection as pleasant as possible to all parties involved.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is basically exactly the same example as before but rewritten in Haskell. We now have a &lt;code&gt;Debtor&lt;/code&gt; type with a single field of a &lt;code&gt;NonEmptyList&lt;/code&gt; of payments (1). We then define a utility function &lt;code&gt;maxPayment&lt;/code&gt; (2) which uses the &lt;code&gt;maximum&lt;/code&gt; function to compute the maximum of the non-empty list of &lt;code&gt;payments&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;What is notable here is that &lt;code&gt;maximum&lt;/code&gt; is part of the &lt;a href=&quot;https://hackage.haskell.org/package/base-4.20.0.1/docs/Data-Foldable.html&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;Foldable&lt;/code&gt;&lt;/a&gt; type class. And so is actually overloaded to mean different things depending on the target type.&lt;/p&gt;
&lt;p&gt;As a result, when once again our dear product manager&lt;sup&gt;&lt;a href=&quot;#user-content-fn-sameone&quot; id=&quot;user-content-fnref-sameone&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;12&lt;/a&gt;&lt;/sup&gt; forces us to break our invariants we end up with this:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;newtype&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Debtor&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Debtor&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;payments&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Payment&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; the rest is the same&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And once again our reports explode when stumbling on debtless debtors:&lt;/p&gt;
&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;Prelude.maximum: empty list&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Overloading just keeps haunting us. We changed types, and a function silently changed its meaning without warning. Since type classes are resolved at compile-time this is pretty much the same pitfall as we had with method overloading in Java &lt;sup&gt;&lt;a href=&quot;#user-content-fn-laws&quot; id=&quot;user-content-fnref-laws&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;13&lt;/a&gt;&lt;/sup&gt;. The compiler knew, but kept mum.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Does the flap of a butterfly&apos;s wings in Brazil set off a tornado in Texas?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Maybe we should quit programming altogether, maybe physical debt collection is a less overloaded field.&lt;/p&gt;
&lt;h2 id=&quot;can-we-do-better&quot;&gt;Can We Do Better?&lt;/h2&gt;
&lt;p&gt;I mean, we can forgo overloading altogether and completely forbid it at the language level. But truth is, overloading &lt;em&gt;is&lt;/em&gt; very convenient. Can you imagine instead of using &lt;code&gt;+&lt;/code&gt; you&apos;d have to write &lt;code&gt;+_int&lt;/code&gt;, &lt;code&gt;+_double&lt;/code&gt;, &lt;code&gt;+_short&lt;/code&gt;?..&lt;/p&gt;
&lt;p&gt;On the other hand, all the pitfalls I showed above stem from the fact that the code &lt;em&gt;silently&lt;/em&gt; changes meaning in the presence of overloaded definitions. I can imagine a language where every time an overloaded definition changes its meaning the compiler will flag it and ask for confirmation&lt;sup&gt;&lt;a href=&quot;#user-content-fn-notruntime&quot; id=&quot;user-content-fnref-notruntime&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;14&lt;/a&gt;&lt;/sup&gt;. Maybe something like a magic import that states the current type of the overloaded definition, and if the import and actual type diverge, the compiler will fail compilation until the import is fixed. At which point you can decide whether you&apos;re doing the right thing when changing types.&lt;/p&gt;
&lt;p&gt;Making this sufficiently ergonomic is left as an exercise to the reader...&lt;/p&gt;
&lt;h2 id=&quot;any-overloading-considered-evil&quot;&gt;ANY Overloading Considered Evil&lt;/h2&gt;
&lt;p&gt;Circling back to the original statement, I stand by the claim that ANY form of overloading should be considered evil. It may be necessary and even convenient, but make no mistake, where overloading exists, evil lurks not far behind.&lt;/p&gt;
&lt;p&gt;This statement goes beyond any specific languages and even programming in general&lt;sup&gt;&lt;a href=&quot;#user-content-fn-humanlangs&quot; id=&quot;user-content-fnref-humanlangs&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;15&lt;/a&gt;&lt;/sup&gt; &lt;sup&gt;&lt;a href=&quot;#user-content-fn-math&quot; id=&quot;user-content-fnref-math&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;16&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;I purposefully chose the word &quot;evil&quot; because there&apos;s something sinister about the way it strikes you. Waiting for you to feel cozy with the typechecker, lulling you into a false sense of security, only to strike you when you least expect it.&lt;/p&gt;
&lt;p&gt;Although I mostly used various Java warts to illustrate my point, this holds even without Java or those warts. Whenever you change types and your compiler lets behavior change silently and without your knowledge, it&apos;s a recipe for disaster.&lt;/p&gt;
&lt;p&gt;The consequences of overloading can be more subtle than actual exceptions. Especially as code grows larger and we rely more and more on the compiler to track the correctness of our code. Like a silent performance degradation due to a change of collection types in some unrelated area of the code&lt;sup&gt;&lt;a href=&quot;#user-content-fn-get&quot; id=&quot;user-content-fnref-get&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;17&lt;/a&gt;&lt;/sup&gt;. Or some accidental parallelism where none was present. Or improperly sorted data that you discover far away from the point of sorting. Or poorly formatted strings in your critical financial reports. Or anything really, let your imagination (paranoia?) run wild!&lt;/p&gt;
&lt;p&gt;Thank you for reading this far, and may your future not be overloaded...&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-overriding&quot;&gt;
&lt;p&gt;For our purposes here, Java-style method overriding is also a form of &quot;overloading&quot;, as it too ascribes potentially different meanings to the same name. &lt;a href=&quot;#user-content-fnref-overriding&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-better&quot;&gt;
&lt;p&gt;This is mostly in the context of statically typed languages as well, although it also holds to a degree in more dynamic languages. It&apos;s just that in statically typed languages we do expect to have more assistance from the compiler, which makes the evilness of overloading more apparent. &lt;a href=&quot;#user-content-fnref-better&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-contrived&quot;&gt;
&lt;p&gt;I know this may seem a bit contrived, but in real code this can come up naturally on its own. It&apos;s just difficult to condense into blog form. &lt;a href=&quot;#user-content-fnref-contrived&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-literalhaskell&quot;&gt;
&lt;p&gt;A notable exception would be the Haskell language with its very flexible and overloaded &lt;a href=&quot;https://www.haskell.org/onlinereport/haskell2010/haskellch6.html#x13-1360006.4.1&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;numeric&lt;/a&gt;, &lt;a href=&quot;https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/overloaded_strings.html&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;string&lt;/a&gt;, and &lt;a href=&quot;https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/literals.html&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;other&lt;/a&gt; &lt;a href=&quot;https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/overloaded_lists.html&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;literals&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-literalhaskell&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-doubleprice&quot;&gt;
&lt;p&gt;Of course one should never represent actual financial numbers with &lt;code&gt;double&lt;/code&gt;, but we&apos;ll ignore this for the sake of the example. &lt;a href=&quot;#user-content-fnref-doubleprice&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-oldjava&quot;&gt;
&lt;p&gt;You could argue that I&apos;m picking on geriatric code. The &lt;code&gt;Arrays&lt;/code&gt; class began life in Java 1.2 (circa 1998), so it&apos;s probably older than some of the readers of this blog. And yet if you take a look at &lt;code&gt;Stream&lt;/code&gt;, despite its veneer of modern respectability it has the &lt;a href=&quot;https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#sorted--&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;sorted&lt;/code&gt;&lt;/a&gt; method, which works generically for any &lt;code&gt;Stream&amp;#x3C;T&gt;&lt;/code&gt;, and so it too can potentially throw a class cast exception. &lt;a href=&quot;#user-content-fnref-oldjava&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 6&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-wartremover&quot;&gt;
&lt;p&gt;The Scala tool Wartremover has something that kind of does that, the &lt;a href=&quot;https://www.wartremover.org/doc/warts.html#any&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;Any&lt;/code&gt; wart&lt;/a&gt; (where &lt;code&gt;Any&lt;/code&gt; is the Scala equivalent of &lt;code&gt;Object&lt;/code&gt;). &lt;a href=&quot;#user-content-fnref-wartremover&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 7&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-methodoverloadingexception&quot;&gt;
&lt;p&gt;Method overloading can also exist with signatures of different arity. Although in the spirit of this post I should say something against this as well, for the moment I can&apos;t come up with a likely scenario where overloading over arity will break things silently. Reach out if you have examples of that as well. &lt;a href=&quot;#user-content-fnref-methodoverloadingexception&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 8&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-effectivejavaedition&quot;&gt;
&lt;p&gt;Or 17, depending on edition. I guess that numbering wasn&apos;t immutable... &lt;a href=&quot;#user-content-fnref-effectivejavaedition&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 9&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-depress&quot;&gt;
&lt;p&gt;So as to depress our clients with the size of their debts. &lt;a href=&quot;#user-content-fnref-depress&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 10&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-nonemptyalias&quot;&gt;
&lt;p&gt;Here and below I&apos;m using type aliases with &lt;code&gt;type NonEmptyList a = NonEmpty a&lt;/code&gt; and &lt;code&gt;type List a = [a]&lt;/code&gt;. This is not essential, I&apos;m using the aliases just to make it a bit easier to relate the Haskell code to Java code above. &lt;a href=&quot;#user-content-fnref-nonemptyalias&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 11&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-sameone&quot;&gt;
&lt;p&gt;Is it the same one we had in our enterprise cage? I can&apos;t tell them all apart anymore... &lt;a href=&quot;#user-content-fnref-sameone&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 12&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-laws&quot;&gt;
&lt;p&gt;Unlike Java though it&apos;s encouraged in Haskell for type classes to come equipped with laws. The laws help us reason about the behavior of the different implementations generically, making sure that all implementations &quot;make sense&quot;. That&apos;s great, and better than nothing. But I still think you can stumble on overloading pitfalls even in the presence of laws. &lt;a href=&quot;#user-content-fnref-laws&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 13&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-notruntime&quot;&gt;
&lt;p&gt;This can help with the statically resolved overloading cases, but won&apos;t help with all instances of runtime polymorphism (although some can be caught at compile-time, like changing the receiver type of &lt;code&gt;toString&lt;/code&gt; in a non-generic context). &lt;a href=&quot;#user-content-fnref-notruntime&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 14&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-humanlangs&quot;&gt;
&lt;p&gt;I hear that human languages are known to be notoriously overloaded. Can you think of any problems that can cause? &lt;a href=&quot;#user-content-fnref-humanlangs&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 15&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-math&quot;&gt;
&lt;p&gt;It&apos;s not uncommon for mathematicians to rely on overloading to make derivations more concise. Unfortunately for them, most of them don&apos;t even have a typechecker to begin with. &lt;a href=&quot;#user-content-fnref-math&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 16&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-get&quot;&gt;
&lt;p&gt;The runtime polymorphic &lt;code&gt;get&lt;/code&gt; method on &lt;code&gt;List&lt;/code&gt; can have wildly different performance characteristics depending on the concrete implementation. Some algorithms may become unacceptably slow when you swap constant-time indexing with linear indexing. &lt;a href=&quot;#user-content-fnref-get&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 17&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><author>Daniel Beskin</author></item><item><title>Purify Your Tests III: Lean, Mean Testing Machine</title><link>https://blog.daniel-beskin.com/2024-05-30-purify-tests-3</link><guid isPermaLink="true">https://blog.daniel-beskin.com/2024-05-30-purify-tests-3</guid><description>In the last part we saw how to make our code more declarative, and the tests more functional by introducing type parameters for the inner data flowing through our code. In this part we are going to…</description><pubDate>Thu, 30 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://blog.daniel-beskin.com/social-cards/2024-05-30-purify-tests-3.png&quot; alt=&quot;Cover image for Purify Your Tests III: Lean, Mean Testing Machine&quot; style=&quot;width: 100%; height: auto; margin-bottom: 2rem;&quot;&gt;&lt;p&gt;In the &lt;a href=&quot;https://blog.daniel-beskin.com/2024-05-20-purify-tests-2&quot;&gt;last part&lt;/a&gt; we saw how to make our code more declarative, and the tests more functional by introducing type parameters for the inner data flowing through our code. In this part we are going to make our test inputs leaner.&lt;/p&gt;
&lt;h2 id=&quot;previously&quot;&gt;Previously...&lt;/h2&gt;
&lt;p&gt;Recall that last time we ended up with the following code&lt;sup&gt;&lt;a href=&quot;#user-content-fn-github&quot; id=&quot;user-content-fnref-github&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UberService&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt;](&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fetcher&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Fetcher&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;enricher&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Enricher&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bookkeeper&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Bookkeeper&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;storage&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetchAndStore&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; fetcher.fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;enriched&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; enricher.enrich&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user, data&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bookkept&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; bookkeeper.bookkeep&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data, enriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stored&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; storage.store&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;bookkept, enriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;stored&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The key insight from the &lt;a href=&quot;https://blog.daniel-beskin.com/2024-05-20-purify-tests-2&quot;&gt;last post&lt;/a&gt; is that we can use type parameters to enforce the call order between the &lt;code&gt;Bookkeeper&lt;/code&gt; and &lt;code&gt;Storage&lt;/code&gt; instances (1). We are forced to produce a &lt;code&gt;Bookkept&lt;/code&gt; value (2) before being able to call &lt;code&gt;store&lt;/code&gt; (3), and the only way to produce a &lt;code&gt;Bookkept&lt;/code&gt; value is by calling &lt;code&gt;bookkeep&lt;/code&gt;. This makes our code more declarative, and that much more type-safe. So much so, that it enabled us to delete a test.&lt;/p&gt;
&lt;p&gt;Last time I suggested and quickly dismissed the idea of turning the inputs into type parameters as well. I (quite reasonably) claimed that the proliferation of type parameters might not be worth the potential gains. That may be the case in this specific example. But what if we imagine a slightly different scenario. One where our inputs are much more cumbersome.&lt;/p&gt;
&lt;h2 id=&quot;this-is-all-way-too-much&quot;&gt;This Is All Way Too Much&lt;/h2&gt;
&lt;p&gt;Till now, these were the classes representing the inputs to our tests:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, these inputs our quite lean, just some wrappers around strings and ints. It&apos;s not a big deal to create fake instances of them in a test. This was perfectly fine for a blog post example, but it&apos;s usually not representative of real code.&lt;/p&gt;
&lt;p&gt;Let&apos;s imagine that we had some more realistic inputs.&lt;/p&gt;
&lt;p&gt;First stop, &lt;code&gt;UserID&lt;/code&gt; is actually defined like so:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;make&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Option&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; ... &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice the private constructor (1), and the smart constructor &lt;code&gt;make&lt;/code&gt; (2). &lt;code&gt;UserID&lt;/code&gt; cannot be created willy-nilly, there are some invariants&lt;sup&gt;&lt;a href=&quot;#user-content-fn-abovezero&quot; id=&quot;user-content-fnref-abovezero&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; to be maintained, and a smart constructor to enforce them. But do you care about that in the test? Do you want to deal with creating valid instances and handling that smart constructor just to pass data around in the test?&lt;/p&gt;
&lt;p&gt;Next, the user-data is actually this monster:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;username&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;email&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Email&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;password&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Hashed&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Password&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;firstName&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;FirstName&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;lastName&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;LastName&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bio&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Location&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;birthDate&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;LocalDate&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gender&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Gender&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;profilePicture&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Option&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Media&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;coverPhoto&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Option&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Media&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;education&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Education&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;workExperience&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;WorkExperience&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;interests&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;socialMediaAccounts&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;SocialMediaAccount&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;privacy&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Privacy&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;followers&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;following&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;notifications&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Notification&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;createdAt&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Instant&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;lastLogin&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Instant&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;isVerified&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Boolean&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; ... another 20 fields...&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Not only does it have way too many fields, some of those are nested classes as well (like &lt;code&gt;Post&lt;/code&gt;). How cumbersome would it be to create fake instances of this data in your test&lt;sup&gt;&lt;a href=&quot;#user-content-fn-trap&quot; id=&quot;user-content-fnref-trap&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;? And for what, just to pass it around? Or maybe you&apos;re a well-disciplined functional programmer, and use randomly generated data a la &lt;a href=&quot;https://fsharpforfunandprofit.com/pbt/&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;property-based testing&lt;/a&gt;. That&apos;s great, but you will still have to create a gnarly generator for this purpose. The fact that you can use random data and not care might be hinting something...&lt;/p&gt;
&lt;p&gt;You can imagine that &lt;code&gt;EnrichedUserData&lt;/code&gt; is just as bad, maybe it has even more fields. I won&apos;t daunt you with an example, just add it to our overly heavy baggage.&lt;/p&gt;
&lt;p&gt;The ongoing theme here is that we are paying the price in the test for something we really don&apos;t care about.&lt;/p&gt;
&lt;p&gt;Hopefully, by now I&apos;m becoming predictable and the solution intuitive. When code &quot;doesn&apos;t care&quot; about something it might be a fertile ground to introduce a new type parameter. Encoding the code&apos;s indifference at the type-level.&lt;/p&gt;
&lt;p&gt;Every piece of data the details of which we don&apos;t care about in our flow and want to avoid faking in the tests can become a brand-new type parameter&lt;sup&gt;&lt;a href=&quot;#user-content-fn-enough&quot; id=&quot;user-content-fnref-enough&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; &lt;sup&gt;&lt;a href=&quot;#user-content-fn-initialtrigger&quot; id=&quot;user-content-fnref-initialtrigger&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Fair warning, some individuals may find the number of type parameters in the following section disturbing. Reader discretion is advised.&lt;/p&gt;
&lt;h2 id=&quot;type-parameter-galore&quot;&gt;Type Parameter Galore&lt;/h2&gt;
&lt;p&gt;Here&apos;s the new &lt;code&gt;Fetcher&lt;/code&gt; trait:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;trait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Fetcher&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note the new type parameters, &lt;code&gt;UserID&lt;/code&gt; and &lt;code&gt;UserData&lt;/code&gt;. I&apos;m breaking the FP convention of naming type parameters using single alphabet letters&lt;sup&gt;&lt;a href=&quot;#user-content-fn-dubious&quot; id=&quot;user-content-fnref-dubious&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;. The reasoning being that these are not &quot;really&quot; generic things, they are just stand-ins for the real &lt;code&gt;UserID&lt;/code&gt; and &lt;code&gt;UserData&lt;/code&gt; classes. They exist merely to facilitate testing.&lt;/p&gt;
&lt;p&gt;The real production implementation is going to set the parameters to the original classes, and otherwise remain unchanged:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ProductionFetcher&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;/*&lt;/span&gt;&lt;span&gt; ... &lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Fetcher&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just as before, the production code doesn&apos;t care about the type parameters, and can keep using the original classes. The actual benefit of using the type parameters will become apparent when we write down the tests. But first, let&apos;s parametrize the rest of the classes:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;trait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Enricher&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;enrich&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;trait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Bookkeeper&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bookkeep&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;original&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;enriched&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;trait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Precondition&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;store&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;precondition&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Precondition&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Similarly to &lt;code&gt;Fetcher&lt;/code&gt;, we are replacing every occurrence of &lt;code&gt;UserID&lt;/code&gt;, &lt;code&gt;UserData&lt;/code&gt;, and &lt;code&gt;EnrichedUserData&lt;/code&gt; with the type parameters (1, 2, 3). We then use them consistently in all the methods. Just like with &lt;code&gt;Fetcher&lt;/code&gt;, the production classes can remain unchanged by setting the type parameters to the original classes.&lt;/p&gt;
&lt;p&gt;Now for the main bit, &lt;code&gt;UberService&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UberService&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt;&lt;span&gt;](&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fetcher&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Fetcher&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;enricher&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Enricher&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bookkeeper&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Bookkeeper&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;storage&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetchAndStore&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 6&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; fetcher.fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;enriched&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; enricher.enrich&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user, data&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bookkept&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; bookkeeper.bookkeep&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data, enriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stored&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; storage.store&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;bookkept, enriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;stored&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;UberService&lt;/code&gt; now takes an impressive total of five type parameters&lt;sup&gt;&lt;a href=&quot;#user-content-fn-septi&quot; id=&quot;user-content-fnref-septi&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;7&lt;/a&gt;&lt;/sup&gt; (1). We then consistently pass these parameters around the different classes (2, 3, 4, 5). But despite this apparent mess, the actual implementation code (6) remained unchanged&lt;sup&gt;&lt;a href=&quot;#user-content-fn-naming&quot; id=&quot;user-content-fnref-naming&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;8&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Now we are ready for the payoff.&lt;/p&gt;
&lt;h2 id=&quot;lean-fakes&quot;&gt;Lean Fakes&lt;/h2&gt;
&lt;p&gt;To make things slightly less confusing&lt;sup&gt;&lt;a href=&quot;#user-content-fn-more&quot; id=&quot;user-content-fnref-more&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;9&lt;/a&gt;&lt;/sup&gt; I&apos;m going to define the following aliases for the test:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What I mean by that is that in the context of the test, the different type parameters are going to be chosen to be these simple aliases. All we have to do now is to reimplement our mocks using these parameters. To wit:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TestFetcher&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Fetcher&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;data: &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The new &lt;code&gt;Fetcher&lt;/code&gt; mock now uses the aliases &lt;code&gt;UserID&lt;/code&gt; (actually &lt;code&gt;Int&lt;/code&gt;) and &lt;code&gt;UserData&lt;/code&gt; (actually &lt;code&gt;String&lt;/code&gt;) for the type that it&apos;s going to be processing (1). And the implementation of &lt;code&gt;fetch&lt;/code&gt; (2) can now use a plain string as its output. No need to deal with the huge &quot;real&quot; &lt;code&gt;UserData&lt;/code&gt; class. Our fake data couldn&apos;t be much leaner than that.&lt;/p&gt;
&lt;p&gt;Similarly, &lt;code&gt;Enricher&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TestEnricher&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Enricher&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;enrich&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;enriched: &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt; - &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, these are just the simple aliases, not the real &quot;classes&quot;, we can use a lean string instead of some huge data class.&lt;/p&gt;
&lt;p&gt;Finally, the &lt;code&gt;Bookkeeper&lt;/code&gt; and &lt;code&gt;Storage&lt;/code&gt; mocks. Recall that they were a bit of mess before because we had to pipe various tuples of &lt;code&gt;UserData&lt;/code&gt; and &lt;code&gt;EnrichedUserData&lt;/code&gt; through them. But now that we are no longer bound to these specific types, we can choose some other way of reflecting the data that flowed through &lt;code&gt;UberService&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-oldway&quot; id=&quot;user-content-fnref-oldway&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;10&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;The new &lt;code&gt;Bookkeeper&lt;/code&gt; mock:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TestBookkeeper&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Bookkeeper&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bookkeep&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;original&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;enriched&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bookkept: &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;original&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bookkept: &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;enriched&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this new mock we choose the output of &lt;code&gt;Bookkeeper&lt;/code&gt; to be &lt;code&gt;List[String]&lt;/code&gt; (1). Since both the &lt;code&gt;UserData&lt;/code&gt; and &lt;code&gt;EnrichedUserData&lt;/code&gt; types are actually plain strings now (1), we can just place them in a list, instead of using a tuple.&lt;/p&gt;
&lt;p&gt;Accordingly, &lt;code&gt;bookkeep&lt;/code&gt; now returns a &lt;code&gt;List[String]&lt;/code&gt; (2). And we construct that list (3) in the implementation, by placing both inputs to &lt;code&gt;bookkeep&lt;/code&gt; in the result. To make it a bit more obvious when asserting, we are adding a marker &quot;bookkept&quot; prefix to that string. This is possible because everything here is actually a &lt;code&gt;String&lt;/code&gt;, we are not relying on the &lt;code&gt;toString&lt;/code&gt; representation of the real &lt;code&gt;UserData&lt;/code&gt; and &lt;code&gt;EnrichedUserData&lt;/code&gt; classes, or anything evil like that.&lt;/p&gt;
&lt;p&gt;Similarly, we modify the &lt;code&gt;Storage&lt;/code&gt; mock to use &lt;code&gt;List[String]&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TestStorage&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;store&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;bookkeepingResult&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bookkeepingResult &lt;/span&gt;&lt;span&gt;:+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;stored: &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To be consistent with the &lt;code&gt;TestBookkeeper&lt;/code&gt; we are setting the input to &lt;code&gt;TestStorage&lt;/code&gt; to be &lt;code&gt;List[String]&lt;/code&gt;, as well as the output (1).&lt;/p&gt;
&lt;p&gt;The final result from &lt;code&gt;store&lt;/code&gt; is now a compound list with both the &lt;code&gt;Bookkeeper&lt;/code&gt; result and the entry from the storage process (2). Again, we are adding a string prefix to make it easier to see what&apos;s going on when we finally assert on the data. Which we are now ready to do.&lt;/p&gt;
&lt;p&gt;Here&apos;s what the new test looks like now:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;The uber-service&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;should&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fetch the user data, enrich it, and store the results&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;service&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UberService&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;TestFetcher&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;TestEnricher&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;TestBookkeeper&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;TestStorage&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; service.fetchAndStore&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;result shouldBe &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bookkept: data: 5&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bookkept: enriched: 5 - data: 5&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;stored: enriched: 5 - data: 5&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First we are running &lt;code&gt;fethAndStore&lt;/code&gt; (1) with the ID &lt;code&gt;5&lt;/code&gt;. Note that this is a plain &lt;code&gt;Int&lt;/code&gt;. No need to try to circumvent the smart constructor of the real &lt;code&gt;UserID&lt;/code&gt; type. After running &lt;code&gt;fetchAndStore&lt;/code&gt; we have a single result which is a &lt;code&gt;List[String]&lt;/code&gt;. We can now assert on it (2) and see that we indeed have all the relevant outputs, both from &lt;code&gt;Bookkeeper&lt;/code&gt; and &lt;code&gt;Storage&lt;/code&gt;, as plain strings.&lt;/p&gt;
&lt;p&gt;And that&apos;s it. Keep in mind what we didn&apos;t have to do, we never initialize any instances of &quot;real&quot; production classes, we never had to come up with a multitude of fake values for all the numerous fields of &lt;code&gt;UserData&lt;/code&gt;. All we had to do is use some simple strings and ints, and yet this test is powerful enough to test real production code, code that can work with real, messy data.&lt;/p&gt;
&lt;p&gt;This is the power of type parameters, they blend immense flexibility with extraordinary type-safety.&lt;/p&gt;
&lt;h2 id=&quot;was-it-all-really-necessary&quot;&gt;Was It All Really Necessary?&lt;/h2&gt;
&lt;p&gt;Recall that in the &lt;a href=&quot;https://blog.daniel-beskin.com/2024-05-20-purify-tests-2&quot;&gt;last post&lt;/a&gt; we talked about &quot;non-fungible&quot; type parameters. That is how in code that uses type parameters we cannot fake parametric values into existence out of nowhere. We are forced to &quot;follow the types&quot; and call the relevant producers of each type (assuming we are not cheating&lt;sup&gt;&lt;a href=&quot;#user-content-fn-cheating&quot; id=&quot;user-content-fnref-cheating&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;11&lt;/a&gt;&lt;/sup&gt;).&lt;/p&gt;
&lt;p&gt;This allowed us to avoid testing various aspects of the code. Like the fact that we definitely called the &lt;code&gt;Bookkeeper&lt;/code&gt; before calling &lt;code&gt;Storage&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now that we have even more type parameters, can we remove more tests?&lt;/p&gt;
&lt;p&gt;If we look at the code of &lt;code&gt;fethAndStore&lt;/code&gt; again:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetchAndStore&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; fetcher.fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;enriched&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; enricher.enrich&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user, data&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bookkept&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; bookkeeper.bookkeep&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data, enriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stored&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; storage.store&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;bookkept, enriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;stored&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Is it even possible to not call the &lt;code&gt;Fetcher&lt;/code&gt; or &lt;code&gt;Enricher&lt;/code&gt;? Is it possible to pass the wrong inputs to &lt;code&gt;Bookkeeper&lt;/code&gt; and &lt;code&gt;Storage&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;If we follow the types backwards from the return type, we observe the following.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We must return a &lt;code&gt;Stored&lt;/code&gt; value (1), but this is a type parameter, so it cannot be faked. The only way to obtain it is to call &lt;code&gt;store&lt;/code&gt; (5).&lt;/li&gt;
&lt;li&gt;The only way to do that is to pass it a &lt;code&gt;Bookkept&lt;/code&gt; value&lt;sup&gt;&lt;a href=&quot;#user-content-fn-nobookkept&quot; id=&quot;user-content-fnref-nobookkept&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;12&lt;/a&gt;&lt;/sup&gt; and an &lt;code&gt;EnrichedUserData&lt;/code&gt; value. But &lt;code&gt;EnrichedUserData&lt;/code&gt; is now a type parameter, so there&apos;s no way to fake it as well.&lt;/li&gt;
&lt;li&gt;To obtain an &lt;code&gt;EnrichedUserData&lt;/code&gt; value we must call &lt;code&gt;Enricher&lt;/code&gt; (3), but to do that we must provide it with a &lt;code&gt;UserID&lt;/code&gt; and a &lt;code&gt;UserData&lt;/code&gt; instance. Both are now type parameters.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UserData&lt;/code&gt; can only be obtained from a call to &lt;code&gt;Fetcher&lt;/code&gt; (2).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UserID&lt;/code&gt; can only be obtained from the input to &lt;code&gt;fetchAndStore&lt;/code&gt; (1).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Following this chain we see that the answers to the questions above is &quot;no&quot;, we cannot mess these things up. The type-signatures force us into a certain chain of calls, that cannot be avoided.&lt;/p&gt;
&lt;p&gt;This means that we could actually remove the test we just wrote, as it doesn&apos;t seem to test anything that is not already enforced by the type-system. Our types are so declarative that the compiler can just check everything for us. It&apos;s as if the types have become the code&apos;s specification.&lt;/p&gt;
&lt;p&gt;If you&apos;re like me though, and have a slight measure of paranoia&lt;sup&gt;&lt;a href=&quot;#user-content-fn-simple&quot; id=&quot;user-content-fnref-simple&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;13&lt;/a&gt;&lt;/sup&gt;, maybe one little sanity test like the one we wrote is not that bad. Especially when the resulting test is both pure and uses very lean fake data.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Following the pains of mocking data we made our code so parametric that it no longer cares about anything, allowing us to use the leanest possible fake data in our tests. This might seem an extreme measure, but depending on how difficult it is to create the fake data, it might be worth it.&lt;/p&gt;
&lt;p&gt;Even if you don&apos;t end up using so many type parameters, the mere fact that you can do it tells us something important about the code: it&apos;s agnostic to all the data flowing through it, it&apos;s pure orchestration. Every time you see such indifference&lt;sup&gt;&lt;a href=&quot;#user-content-fn-morecommon&quot; id=&quot;user-content-fnref-morecommon&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;14&lt;/a&gt;&lt;/sup&gt; in your code you can take advantage of it to make your tests both leaner and purer.&lt;/p&gt;
&lt;p&gt;On a different note. I&apos;m not a big believer in Test Driven Development&lt;sup&gt;&lt;a href=&quot;#user-content-fn-pitchforks&quot; id=&quot;user-content-fnref-pitchforks&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;15&lt;/a&gt;&lt;/sup&gt;. I don&apos;t think that it&apos;s necessary to write tests upfront, and I&apos;m probably guilty of skipping the occasional test. But I&apos;m a firm believer that making code &lt;strong&gt;testable&lt;/strong&gt; tends to improve the overall quality of the code.&lt;/p&gt;
&lt;p&gt;If writing tests for some piece of code is painful, maybe that code can be improved to be more testable, and as a side-effect&lt;sup&gt;&lt;a href=&quot;#user-content-fn-pun&quot; id=&quot;user-content-fnref-pun&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;16&lt;/a&gt;&lt;/sup&gt; the quality of the code itself will be improved.&lt;/p&gt;
&lt;p&gt;I hope that this blog was a case in point. Our quest to make the code more easily testable made it so type-safe that it&apos;s nigh impossible to write it incorrectly. I think that&apos;s a win&lt;sup&gt;&lt;a href=&quot;#user-content-fn-costbenefit&quot; id=&quot;user-content-fnref-costbenefit&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;17&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;https://blog.daniel-beskin.com/2024-12-02-purify-tests-4&quot;&gt;next part&lt;/a&gt; we&apos;ll see how we can leverage our fake type parameters directly for the benefit of the production code, and not just for tests.&lt;/p&gt;
&lt;p&gt;See you next time!&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-github&quot;&gt;
&lt;p&gt;The full code for the examples is available on &lt;a href=&quot;https://github.com/ncreep/purify-tests-blog&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-github&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-abovezero&quot;&gt;
&lt;p&gt;Like &lt;code&gt;value &gt; 0&lt;/code&gt;. &lt;a href=&quot;#user-content-fnref-abovezero&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-trap&quot;&gt;
&lt;p&gt;Don&apos;t fall for the luring trap of an &lt;a href=&quot;https://wiki.c2.com/?ObjectMother&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;Object Mother&lt;/a&gt;, only pain and misery can come from that. &lt;a href=&quot;#user-content-fnref-trap&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-enough&quot;&gt;
&lt;p&gt;As if we don&apos;t have enough of them already... &lt;a href=&quot;#user-content-fnref-enough&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-initialtrigger&quot;&gt;
&lt;p&gt;Actually the burning desire to avoid annoying fake data in tests was the initial trigger that got me into this approach of using type parameters to help with testing. You wouldn&apos;t believe the size of the class I wanted to avoid... &lt;a href=&quot;#user-content-fnref-initialtrigger&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-dubious&quot;&gt;
&lt;p&gt;Or the dubious Java convention of naming parameters with &lt;code&gt;T&lt;/code&gt;, &lt;code&gt;U&lt;/code&gt;, and &lt;code&gt;V&lt;/code&gt;. &lt;a href=&quot;#user-content-fnref-dubious&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 6&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-septi&quot;&gt;
&lt;p&gt;Are we getting closer to the glorious septifunctor?.. &lt;a href=&quot;#user-content-fnref-septi&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 7&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-naming&quot;&gt;
&lt;p&gt;Partly due to the unconventional naming of the type parameters, we can still pretend that we are working with the same classes as before. &lt;a href=&quot;#user-content-fnref-naming&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 8&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-more&quot;&gt;
&lt;p&gt;Or more. &lt;a href=&quot;#user-content-fnref-more&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 9&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-oldway&quot;&gt;
&lt;p&gt;Though the old way with more granuraly typed tuples is still possible. &lt;a href=&quot;#user-content-fnref-oldway&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 10&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-cheating&quot;&gt;
&lt;p&gt;By &quot;cheating&quot; I mean things like subverting the type system with &lt;code&gt;null&lt;/code&gt;s, casts, and the like. See the &lt;a href=&quot;https://failex.blogspot.com/2019/07/scalazzi-safe-scala-subset-principles-2.html&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;Scalazzi&lt;/a&gt; subset of Scala. I&apos;ll assume we are not cheating for the rest of the post. &lt;a href=&quot;#user-content-fnref-cheating&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 11&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-nobookkept&quot;&gt;
&lt;p&gt;I&apos;m ignoring &lt;code&gt;Bookkept&lt;/code&gt; now because we already discussed it at length in the last installment. &lt;a href=&quot;#user-content-fnref-nobookkept&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 12&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-simple&quot;&gt;
&lt;p&gt;This is fairly simple code, but in the presence of something like &lt;code&gt;IO&lt;/code&gt; we might want, for example, to sanity test that we are not accidentally short-circuiting where we are not supposed to. One might ponder whether introducing a type parameter instead of &lt;code&gt;IO&lt;/code&gt; will enable us to skip these tests as well... &lt;a href=&quot;#user-content-fnref-simple&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 13&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-morecommon&quot;&gt;
&lt;p&gt;And it tends to be more common than you might guess. &lt;a href=&quot;#user-content-fnref-morecommon&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 14&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-pitchforks&quot;&gt;
&lt;p&gt;Prepare the pitchforks... &lt;a href=&quot;#user-content-fnref-pitchforks&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 15&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-pun&quot;&gt;
&lt;p&gt;Pun intended. &lt;a href=&quot;#user-content-fnref-pun&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 16&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-costbenefit&quot;&gt;
&lt;p&gt;Although the exact number of type parameters such that the cost/benefit of using them is sufficiently worthwhile is up for debate. &lt;a href=&quot;#user-content-fnref-costbenefit&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 17&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><author>Daniel Beskin</author></item><item><title>Purify Your Tests: 2 Parametric, 2 Declarative</title><link>https://blog.daniel-beskin.com/2024-05-20-purify-tests-2</link><guid isPermaLink="true">https://blog.daniel-beskin.com/2024-05-20-purify-tests-2</guid><description>In the last part we learned how to purify our tests using type parameters. In this and following parts we&apos;ll see some further benefits of adding type parameters this way.</description><pubDate>Mon, 20 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://blog.daniel-beskin.com/social-cards/2024-05-20-purify-tests-2.png&quot; alt=&quot;Cover image for Purify Your Tests: 2 Parametric, 2 Declarative&quot; style=&quot;width: 100%; height: auto; margin-bottom: 2rem;&quot;&gt;&lt;p&gt;In the &lt;a href=&quot;https://blog.daniel-beskin.com/2024-05-09-purify-tests&quot;&gt;last part&lt;/a&gt; we learned how to purify our tests using type parameters. In this and following parts we&apos;ll see some further benefits of adding type parameters this way.&lt;/p&gt;
&lt;p&gt;Let&apos;s dig in!&lt;/p&gt;
&lt;h2 id=&quot;previously&quot;&gt;Previously...&lt;/h2&gt;
&lt;p&gt;Recall that last time we ended up with the following code&lt;sup&gt;&lt;a href=&quot;#user-content-fn-github&quot; id=&quot;user-content-fnref-github&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UberService&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt;&lt;span&gt;](&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fetcher&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Fetcher&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;enricher&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Enricher&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bookkeeper&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Bookkeeper&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;storage&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetchAndStore&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; fetcher.fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;enriched&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; enricher.enrich&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user, data&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bookkept&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; bookkeeper.bookkeep&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data, enriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stored&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; storage.store&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;enriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;bookkept, stored&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is our &lt;code&gt;UberService&lt;/code&gt;, doing some uber-work. Note the &quot;fake&quot; type parameters (1). These are used by &lt;code&gt;Bookkeeper&lt;/code&gt; and &lt;code&gt;Storage&lt;/code&gt; (2) in place of &lt;code&gt;Unit&lt;/code&gt; that they used to return. The &lt;code&gt;fetchAndStore&lt;/code&gt; method returns these values (3), so that we can inspect them in the test (by setting the type parameters to something useful), and discard them in production (by setting them to &lt;code&gt;Unit&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;So far, what we gained is the ability to write pure, stateless mocks in our tests, and avoid mutable state altogether. Is that all though?&lt;/p&gt;
&lt;h2 id=&quot;nft-type-parameters&quot;&gt;NFT Type Parameters&lt;/h2&gt;
&lt;p&gt;There&apos;s something interesting going on with the type parameters we introduced. Unless we&apos;re cheating&lt;sup&gt;&lt;a href=&quot;#user-content-fn-cheating&quot; id=&quot;user-content-fnref-cheating&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, from the point of view of &lt;code&gt;UberService&lt;/code&gt; there&apos;s no way of knowing what &lt;code&gt;Bookkept&lt;/code&gt; and &lt;code&gt;Stored&lt;/code&gt; is going to be, and so it cannot &quot;fake&quot; these values into existence&lt;sup&gt;&lt;a href=&quot;#user-content-fn-parametricity&quot; id=&quot;user-content-fnref-parametricity&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;. &lt;code&gt;UberService&lt;/code&gt; is forced to call &lt;code&gt;Bookkeeper&lt;/code&gt; and &lt;code&gt;Storage&lt;/code&gt; in order to comply with the return type of &lt;code&gt;fetchAndStore&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This is what makes the resulting type signature so declarative:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetchAndStore&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We know for a fact that there&apos;s no way to produce these values without using the provided &lt;code&gt;Bookkeeper&lt;/code&gt; and &lt;code&gt;Storage&lt;/code&gt; instances.&lt;/p&gt;
&lt;p&gt;This property of type parameters being both &quot;non-fungible&quot; and declarative is something that we can take further advantage of.&lt;/p&gt;
&lt;h2 id=&quot;a-new-requirement&quot;&gt;A New Requirement&lt;/h2&gt;
&lt;p&gt;As sometimes happens, we just got a new requirement for our &lt;code&gt;UberService&lt;/code&gt;. Turns out that the &lt;code&gt;Bookkeeper&lt;/code&gt; service can sometimes fail by throwing an exception&lt;sup&gt;&lt;a href=&quot;#user-content-fn-morefunctional&quot; id=&quot;user-content-fnref-morefunctional&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;. The new requirement is that it&apos;s no longer allowed to store the data unless bookkeeping succeeded. Can we guarantee this is the case?&lt;/p&gt;
&lt;p&gt;The type signature of &lt;code&gt;fetchAndStore&lt;/code&gt; says nothing about the ordering of bookkeeping and storage. Both &lt;code&gt;Bookkept&lt;/code&gt; and &lt;code&gt;Stored&lt;/code&gt; are returned, and there&apos;s no way of knowing which was invoked first. We could flip the invocation order, or even run in parallel and still have the same &lt;code&gt;(Bookkept, Stored)&lt;/code&gt; result type.&lt;/p&gt;
&lt;p&gt;That&apos;s okay, not everything can be enforced by types. Can we test that the new requirement holds?&lt;/p&gt;
&lt;h2 id=&quot;bad-bad-tests&quot;&gt;Bad, Bad Tests!&lt;/h2&gt;
&lt;p&gt;We can actually check the invocation order of our mocks. But that will require us to use some form of side-effect/mutation. Like so:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TestBookkeeper&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;invocations&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ListBuffer&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Bookkeeper&lt;/span&gt;&lt;span&gt;[(&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;)]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bookkeep&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;original&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;enriched&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;invocations &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bookkeep&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;original, enriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TestStorage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;invocations&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ListBuffer&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;store&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;invocations &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;store&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In these new mocks we now take a constructor argument (1 and 3). A mutable list that will store the invocations that the test is about to perform. Then when actually invoking the &lt;code&gt;bookkeep&lt;/code&gt; and &lt;code&gt;store&lt;/code&gt; methods we add the appropriate invocation name to our list of invocations (2 and 4).&lt;/p&gt;
&lt;p&gt;In the test itself we can use the new mocks:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;The uber-service&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;should&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;invoke bookkeeping before storage&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;invocations&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ListBuffer&lt;/span&gt;&lt;span&gt;&lt;span&gt;.empty&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bookkeeper&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TestBookkeeper&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;invocations&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;storage&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TestStorage&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;invocations&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;service&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UberService&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;TestFetcher&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;TestEnricher&lt;/span&gt;&lt;span&gt;&lt;span&gt;, bookkeeper, storage&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;_&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; service.fetchAndStore&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;invocations shouldBe &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bookkeep&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;store&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This new test first initializes an empty list of invocations (1). We then pass that empty list to our two new mocks (2). After invoking &lt;code&gt;fetchAndStore&lt;/code&gt; we verify that the resulting invocations are in the correct order (3).&lt;/p&gt;
&lt;p&gt;The test works, but yet again it sucks. We just spent a &lt;a href=&quot;https://blog.daniel-beskin.com/2024-05-09-purify-tests&quot;&gt;whole blog post&lt;/a&gt; eradicating mutation from our mocks. All that for nothing? Look how quickly mutation crept back in.&lt;/p&gt;
&lt;p&gt;You might say that these concerns are just theoretical, the test works, leave it at that. And yet I won&apos;t leave it alone. Not only that I&apos;m personally offended by mutability, but in the presence of concurrency this is a flaky test waiting to torment you in your CI pipeline forever. In a concurrent setting, which is quite likely for such an esteemed &lt;code&gt;UberService&lt;/code&gt;, you might get arbitrary interleavings of &lt;code&gt;bookkeep&lt;/code&gt; and &lt;code&gt;store&lt;/code&gt;, and the test will randomly succeed or fail accordingly.&lt;/p&gt;
&lt;p&gt;Are you willing to tolerate this?&lt;/p&gt;
&lt;h2 id=&quot;be-declarative-young-padawan&quot;&gt;Be Declarative Young Padawan&lt;/h2&gt;
&lt;p&gt;Remember way back when, a couple of paragraphs ago when I lamented that not everything can be enforced by types? Well, shame on me, I shouldn&apos;t have given up on them so quickly.&lt;/p&gt;
&lt;p&gt;You see, there is a way to enforce invocation order using types. And it&apos;s one of the most elementary things that we have – functions.&lt;/p&gt;
&lt;p&gt;Suppose I have a function &lt;code&gt;f: A =&gt; B&lt;/code&gt; and a function &lt;code&gt;g: B =&gt; C&lt;/code&gt;. If I start with &lt;code&gt;A&lt;/code&gt; and end up with &lt;code&gt;C&lt;/code&gt;, there&apos;s only one order in which &lt;code&gt;f&lt;/code&gt; and &lt;code&gt;g&lt;/code&gt; can be invoked. You must first invoke &lt;code&gt;f&lt;/code&gt; to produce a &lt;code&gt;B&lt;/code&gt; value, and only then can you pass it to &lt;code&gt;g&lt;/code&gt; to produce the final &lt;code&gt;C&lt;/code&gt; value.&lt;/p&gt;
&lt;p&gt;But. This handwavy argument&lt;sup&gt;&lt;a href=&quot;#user-content-fn-parametricityagain&quot; id=&quot;user-content-fnref-parametricityagain&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; only works if &lt;code&gt;B&lt;/code&gt; is &quot;non-fungible&quot;. That is, if we have no way of obtaining &lt;code&gt;B&lt;/code&gt; without invoking &lt;code&gt;f&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;All we need to do now, is to actually declare our intentions with the types, and to make sure they are non-fungible, or, in our case, parametric.&lt;/p&gt;
&lt;p&gt;To that end, we are going to explicitly encode the relationship between &lt;code&gt;Bookkeeper&lt;/code&gt; and &lt;code&gt;Storage&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;First we&apos;ll make our storage accept a precondition&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;trait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Precondition&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;store&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;precondition&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Precondition&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now &lt;code&gt;Storage&lt;/code&gt; takes another type parameter, which stands for a precondition that should hold before we can call &lt;code&gt;store&lt;/code&gt;. We then modify &lt;code&gt;store&lt;/code&gt; to accept the &lt;code&gt;Precondition&lt;/code&gt; as an argument. That means that there is no way to call &lt;code&gt;store&lt;/code&gt; without first obtaining a &lt;code&gt;Precondition&lt;/code&gt; value. This signature declares our intentions more precisely, directly in the types. A &lt;code&gt;Precondition&lt;/code&gt; value is proof that a function producing &lt;code&gt;Precondition&lt;/code&gt; was invoked.&lt;/p&gt;
&lt;p&gt;While &lt;code&gt;Bookkeeper&lt;/code&gt; can remain the same, we&apos;ll now modify &lt;code&gt;UberService&lt;/code&gt; to take advantage of the new precondition:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UberService&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt;&lt;span&gt;](&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fetcher&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Fetcher&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;enricher&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Enricher&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bookkeeper&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Bookkeeper&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;storage&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetchAndStore&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; fetcher.fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;enriched&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; enricher.enrich&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user, data&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bookkept&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; bookkeeper.bookkeep&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data, enriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stored&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; storage.store&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;bookkept, enriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;stored &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 6&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;UberService&lt;/code&gt; still takes the same two type parameters (1), but we now declare that the precondition for calling &lt;code&gt;store&lt;/code&gt; is the presence of a &lt;code&gt;Bookkept&lt;/code&gt; value (2). For reasons that will be clear a bit later, &lt;code&gt;fetchAndStore&lt;/code&gt; no longer returns a tuple, instead it&apos;s only a &lt;code&gt;Stored&lt;/code&gt; value (3).&lt;/p&gt;
&lt;p&gt;Now for the main part. After we invoke &lt;code&gt;bookkeep&lt;/code&gt; (4) and obtain a &lt;code&gt;Bookkept&lt;/code&gt; value, we pass that value to &lt;code&gt;store&lt;/code&gt; (5). We then return the &lt;code&gt;Stored&lt;/code&gt; result (6).&lt;/p&gt;
&lt;p&gt;Note how we no longer have a choice, if we want to obtain a &lt;code&gt;Stored&lt;/code&gt; value we &lt;strong&gt;must&lt;/strong&gt; invoke &lt;code&gt;store&lt;/code&gt; after we successfully invoked &lt;code&gt;bookkeep&lt;/code&gt;. Because the precondition for &lt;code&gt;Storage&lt;/code&gt; is now the &lt;code&gt;Bookkept&lt;/code&gt; value, and because the only way to obtain the (non-fungible) &lt;code&gt;Bookkept&lt;/code&gt; value is by calling &lt;code&gt;bookkeep&lt;/code&gt;, we are &lt;strong&gt;forced&lt;/strong&gt; to observe our new ordering requirement.&lt;/p&gt;
&lt;p&gt;Stated more explicitly, we now have the following chain:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;bookkeep&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;store&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since &lt;code&gt;Bookkept&lt;/code&gt; cannot be faked into existence, the only way to obtain a &lt;code&gt;Stored&lt;/code&gt; value is to chain &lt;code&gt;bookkeep&lt;/code&gt; with &lt;code&gt;store&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;If we try to flip the order of &lt;code&gt;store&lt;/code&gt; and &lt;code&gt;bookkeep&lt;/code&gt;, we will fail to compile. If we forget to invoke &lt;code&gt;bookkeep&lt;/code&gt; &lt;sup&gt;&lt;a href=&quot;#user-content-fn-refactoring&quot; id=&quot;user-content-fnref-refactoring&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;, we will fail to compile. We really have no choice. This is also the reason why we removed &lt;code&gt;Bookkept&lt;/code&gt; from the return type of &lt;code&gt;fetchAndStore&lt;/code&gt;, we already know for sure that &lt;code&gt;bookkeep&lt;/code&gt; was called, there&apos;s no need to assert it again.&lt;/p&gt;
&lt;p&gt;That&apos;s the power of having declarative types. By thoroughly declaring our requirements in the type system, we now have the compiler to enforce our invariants. Better yet, we can say that we &quot;made an illegal state unrepresentable&lt;sup&gt;&lt;a href=&quot;#user-content-fn-minsky&quot; id=&quot;user-content-fnref-minsky&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;&quot;.&lt;/p&gt;
&lt;p&gt;All the while the production code can still remain completely oblivious to this:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ProductionStorage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;/*&lt;/span&gt;&lt;span&gt; ... &lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The production implementation doesn&apos;t care about any preconditions, and just sets it to &lt;code&gt;Unit&lt;/code&gt;. Nonetheless this cannot be abused by &lt;code&gt;UberService&lt;/code&gt; to circumvent our rules. Since it&apos;s completely parametric, it cannot know that the real implementation has such an easily forgeable&lt;sup&gt;&lt;a href=&quot;#user-content-fn-unit&quot; id=&quot;user-content-fnref-unit&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;8&lt;/a&gt;&lt;/sup&gt; precondition, and so it cannot abuse that fact.&lt;/p&gt;
&lt;p&gt;What&apos;s even better, we no longer need to write those terrible, mutable tests to check the invocation ordering requirement anymore. The compiler has our back. As they say &lt;sup&gt;&lt;a href=&quot;#user-content-fn-someone&quot; id=&quot;user-content-fnref-someone&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;9&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The best tests are the ones you never had to write.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;even-more-type-parameters&quot;&gt;Even More Type Parameters?&lt;/h2&gt;
&lt;p&gt;The rush you get from deleting tests with impunity...&lt;/p&gt;
&lt;p&gt;Can we delete more tests? Can we delete the original test we started with? Unfortunately, no, the original test is verifying that we are invoking &lt;code&gt;Bookkeeper&lt;/code&gt; and &lt;code&gt;Storage&lt;/code&gt; with the correct input. Unlike the outputs, the inputs of &lt;code&gt;UserData&lt;/code&gt; and &lt;code&gt;EnrichedUserData&lt;/code&gt; can be faked into existence, as they are concrete types.&lt;/p&gt;
&lt;p&gt;Seeing the success we had so far with using fake type parameters, maybe we can apply this to the inputs as well. Indeed we can, but the proliferation of type parameters might not be worth it. In the next part I will find some justification to fake the inputs as well. But for now, we&apos;ll keep them as is and instead adapt the existing test to the new type signatures. This can be instructive.&lt;/p&gt;
&lt;h2 id=&quot;every-day-im-refactoring&quot;&gt;Every Day I&apos;m Refactoring...&lt;/h2&gt;
&lt;p&gt;Recall that we no longer have &lt;code&gt;Bookkept&lt;/code&gt; as the output of &lt;code&gt;fetchAndStore&lt;/code&gt;. Previously we used the &lt;code&gt;Bookkept&lt;/code&gt; parameter as the vehicle to carry data about the invocation of &lt;code&gt;bookkeep&lt;/code&gt;. How can we obtain this information now?&lt;/p&gt;
&lt;p&gt;Swimmingly, it turns out.&lt;/p&gt;
&lt;p&gt;On the one hand &lt;code&gt;Bookkept&lt;/code&gt; is being piped into &lt;code&gt;store&lt;/code&gt;, on the other, type parameters are so flexible we can put anything we want into them. The solution here would be to add the info from &lt;code&gt;Bookkept&lt;/code&gt; directly to the &lt;code&gt;Stored&lt;/code&gt; result.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Bookkeeper&lt;/code&gt; mock remains exactly as before, it just produces its inputs as the output:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TestBookkeeper&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Bookkeeper&lt;/span&gt;&lt;span&gt;[(&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;)]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bookkeep&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;original&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;enriched&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;original, enriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we know that this type &lt;code&gt;(UserData, EnrichedUserData)&lt;/code&gt; is going to be fed to our &lt;code&gt;Storage&lt;/code&gt; mock. Let&apos;s use it:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TestStorage&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;)]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;store&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bookkeepingResult&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;bookkeepingResult, data&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This mock looks a bit scary now, but it&apos;s still essentially the identity function.&lt;/p&gt;
&lt;p&gt;We align the &lt;code&gt;Precondition&lt;/code&gt; type parameter to be the output of &lt;code&gt;Bookkeeper&lt;/code&gt;: &lt;code&gt;(UserData, EnrichedUserData)&lt;/code&gt; (1). The output is now modified to be both the original output of the &lt;code&gt;Storage&lt;/code&gt; mock (&lt;code&gt;EnrichedUserData&lt;/code&gt;) and a tuple of &lt;code&gt;(UserData, EnrichedUserData)&lt;/code&gt; (2). This way we have access both to the data from &lt;code&gt;Bookkeeper&lt;/code&gt; and the data from &lt;code&gt;Storage&lt;/code&gt; in a single output.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;store&lt;/code&gt; implementation, we get the output from &lt;code&gt;Bookkeeper&lt;/code&gt; (3), which we then produce as part of the output of &lt;code&gt;store&lt;/code&gt; (4).&lt;/p&gt;
&lt;p&gt;With the flexibility of type parameters we managed to pipe all the data we need to be able to test the &lt;code&gt;fetchAndStore&lt;/code&gt; flow. And now the test remains exactly the same as before:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;The uber-service&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;should&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fetch the user data, enrich it, and store the results&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;service&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UberService&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;TestFetcher&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;TestEnricher&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;TestBookkeeper&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;TestStorage&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;expectedUserData&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;data: 5&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;expectedEnriched&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;enriched: 5 - data: 5&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;bookkeeperResult, storageResult&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; service.fetchAndStore&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bookkeeperResult shouldBe &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;expectedUserData, expectedEnriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;storageResult shouldBe expectedEnriched&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output of &lt;code&gt;fetchAndStore&lt;/code&gt; (1) has the same type and meaning as the output when &lt;code&gt;Bookkept&lt;/code&gt; was part of the signature. The only difference is that now it comes through the &lt;code&gt;store&lt;/code&gt; call. By plumbing the type parameters correctly we managed to reproduce the old functionality&lt;sup&gt;&lt;a href=&quot;#user-content-fn-paranoid&quot; id=&quot;user-content-fnref-paranoid&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;10&lt;/a&gt;&lt;/sup&gt; .&lt;/p&gt;
&lt;h2 id=&quot;a-silver-bullet&quot;&gt;A Silver Bullet?&lt;/h2&gt;
&lt;p&gt;This is all great, unfortunately, it&apos;s not a silver bullet for all possible requirements we might get. As it stands, for example, there is no way to prevent the code from invoking &lt;code&gt;bookkeep&lt;/code&gt; more than once. We are forcing the code to invoke &lt;code&gt;bookkeep&lt;/code&gt; &lt;em&gt;at least&lt;/em&gt; once, but it can accidentally (or on purpose) invoke it more than once.&lt;/p&gt;
&lt;p&gt;I&apos;m not aware of a way to use Scala&apos;s current type system to enforce such an invariant&lt;sup&gt;&lt;a href=&quot;#user-content-fn-caps&quot; id=&quot;user-content-fnref-caps&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;11&lt;/a&gt;&lt;/sup&gt;. Maybe one day &lt;a href=&quot;https://en.wikipedia.org/wiki/Substructural_type_system&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;substructural types&lt;/a&gt; will enter the mainstream, and will let us enforce such basic invariants. One can only hope...&lt;/p&gt;
&lt;p&gt;For now, we&apos;ll have to make due with the unpleasant mutable mocks if we want to check for multiple invocations. If you come up with a way to avoid them in this scenario as well, I&apos;ll be glad to hear about it.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Type parameters are great, they are flexible, and enable us to enforce some non-trivial invariants. By making our code more declarative, we managed to promote a requirement from a runtime test into a compile-time verified fact&lt;sup&gt;&lt;a href=&quot;#user-content-fn-talk&quot; id=&quot;user-content-fnref-talk&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;12&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;More generally, what was it about our code that enabled us to apply these powerful techniques?&lt;/p&gt;
&lt;p&gt;I would posit that a lot of the well-factored, testable code that we write tends to end up turning into glue code that is ignorant of the specific context it is being used in. It basically just orchestrates other code (like &lt;code&gt;UberService&lt;/code&gt;), instead of doing much on its own. Type parameters lets us promote this ignorance to the type-level where we can reap the benefits that we just saw: declarative, type-safe code and pure tests.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;https://blog.daniel-beskin.com/2024-05-30-purify-tests-3&quot;&gt;next part&lt;/a&gt; we&apos;ll continue reaping the benefits of type parameters and handle those pesky inputs.&lt;/p&gt;
&lt;p&gt;Till next time!&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-github&quot;&gt;
&lt;p&gt;The full code for the examples is available on &lt;a href=&quot;https://github.com/ncreep/purify-tests-blog&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-github&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-cheating&quot;&gt;
&lt;p&gt;By &quot;cheating&quot; I mean things like subverting the type system with &lt;code&gt;null&lt;/code&gt;s, casts, and the like. See the &lt;a href=&quot;https://failex.blogspot.com/2019/07/scalazzi-safe-scala-subset-principles-2.html&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;Scalazzi&lt;/a&gt; subset of Scala. I&apos;ll assume we are not cheating for the rest of the post. &lt;a href=&quot;#user-content-fnref-cheating&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-parametricity&quot;&gt;
&lt;p&gt;This is a loose application of &lt;a href=&quot;https://en.wikipedia.org/wiki/Parametricity&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&quot;parametricity&quot;&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-parametricity&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-morefunctional&quot;&gt;
&lt;p&gt;We could use a more functional approach to error handling, but the same point I&apos;m about to make will still stand. &lt;a href=&quot;#user-content-fnref-morefunctional&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-parametricityagain&quot;&gt;
&lt;p&gt;This is yet another loose application of &lt;a href=&quot;https://en.wikipedia.org/wiki/Parametricity&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&quot;parametricity&quot;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-parametricityagain&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-refactoring&quot;&gt;
&lt;p&gt;E.g., after an accidental refactoring. &lt;a href=&quot;#user-content-fnref-refactoring&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 6&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-minsky&quot;&gt;
&lt;p&gt;As per &lt;a href=&quot;http://lambda-the-ultimate.org/node/2216#comment-31690&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;Yaron Minsky&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-minsky&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 7&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-unit&quot;&gt;
&lt;p&gt;I mean, it&apos;s not that difficult to obtain a value of type &lt;code&gt;Unit&lt;/code&gt;. &lt;a href=&quot;#user-content-fnref-unit&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 8&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-someone&quot;&gt;
&lt;p&gt;I&apos;m sure someone, somewhere said that. &lt;a href=&quot;#user-content-fnref-someone&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 9&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-paranoid&quot;&gt;
&lt;p&gt;If we are being paranoid, it&apos;s true that since we are dealing with concrete types in the test, the &lt;code&gt;Storage&lt;/code&gt; mock can now fake the output of &lt;code&gt;Bookkeeper&lt;/code&gt;. If indeed we submit to the paranoia, we can easily make the mock &quot;ignorant&quot; of the input, and keep &lt;code&gt;Precondition&lt;/code&gt; as a type-parameter, and just pipe that to the output. This will prevent &lt;code&gt;TestStorage&lt;/code&gt; from messing around with the data flowing through it, just like it did in &lt;code&gt;UberService&lt;/code&gt;. Incidentally, this will make the mock more flexible and reusable in other contexts as well. I leave this as an exercise to the curious reader. &lt;a href=&quot;#user-content-fnref-paranoid&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 10&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-caps&quot;&gt;
&lt;p&gt;Maybe something with the still in research &lt;a href=&quot;https://docs.scala-lang.org/scala3/reference/experimental/cc.html&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;capture checking&lt;/a&gt;? Reach out if you have any suggestion on how this can be achieved. &lt;a href=&quot;#user-content-fnref-caps&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 11&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-talk&quot;&gt;
&lt;p&gt;For a more involved example along these lines, see the last part of my &quot;Make Illegal States Unrepresentable&quot; &lt;a href=&quot;https://www.youtube.com/watch?v=PSh7JUfDstE&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;talk&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-talk&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 12&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><author>Daniel Beskin</author></item><item><title>Purify Your Tests</title><link>https://blog.daniel-beskin.com/2024-05-09-purify-tests</link><guid isPermaLink="true">https://blog.daniel-beskin.com/2024-05-09-purify-tests</guid><description>Here&apos;s a fun little trick to purify your tests. When I say &quot;purify&quot; I mean it in the pure functional programming sense of making the tests side-effect-free. But more specifically, I&apos;m going to show…</description><pubDate>Thu, 09 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://blog.daniel-beskin.com/social-cards/2024-05-09-purify-tests.png&quot; alt=&quot;Cover image for Purify Your Tests&quot; style=&quot;width: 100%; height: auto; margin-bottom: 2rem;&quot;&gt;&lt;p&gt;Here&apos;s a fun little trick to purify your tests. When I say &quot;purify&quot; I mean it in the pure functional programming sense of making the tests side-effect-free. But more specifically, I&apos;m going to show you a way to avoid mutable mocks.&lt;/p&gt;
&lt;h2 id=&quot;testing-side-effects&quot;&gt;Testing Side-Effects&lt;/h2&gt;
&lt;p&gt;To make this concrete, we&apos;re going to use an example. Although this is Scala, the technique I&apos;m about to describe is applicable in any statically-typed language with generics/parametric polymorphism, Java&lt;sup&gt;&lt;a href=&quot;#user-content-fn-java&quot; id=&quot;user-content-fnref-java&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; to Haskell included.&lt;/p&gt;
&lt;p&gt;So suppose you&apos;re writing your very cool &lt;code&gt;UberService&lt;/code&gt; &lt;sup&gt;&lt;a href=&quot;#user-content-fn-fullcode&quot; id=&quot;user-content-fnref-fullcode&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UberService&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fetcher&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Fetcher&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;enricher&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Enricher&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bookkeeper&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Bookkeeper&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;storage&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetchAndStore&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; fetcher.fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;enriched&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; enricher.enrich&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user, data&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bookkeeper.bookkeep&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data, enriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;storage.store&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;enriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;UberService&lt;/code&gt; is the pinnacle of engineering:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It has one method &lt;code&gt;fetchAndStore&lt;/code&gt; (1).&lt;/li&gt;
&lt;li&gt;As the name states, it does some data-fetching (2).&lt;/li&gt;
&lt;li&gt;It then enriches the data (3).&lt;/li&gt;
&lt;li&gt;Since this is an important process we do some bookkeeping (4) about the fetched/enriched data.&lt;/li&gt;
&lt;li&gt;Lastly, we store the enriched result into some storage (5).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And because this is the pinnacle of engineering, we want &lt;code&gt;UberService&lt;/code&gt; to be testable. To that end it doesn&apos;t actually do anything on its own, instead it only orchestrates the &lt;code&gt;Fetcher&lt;/code&gt;, &lt;code&gt;Enricher&lt;/code&gt;, &lt;code&gt;Bookkeeper&lt;/code&gt;, and &lt;code&gt;Storage&lt;/code&gt; instances. Since they are all abstract traits, we can mock them out in tests, and verify that &lt;code&gt;UberService&lt;/code&gt; actually does what&apos;s written on the tin.&lt;/p&gt;
&lt;p&gt;First we need to set up some mocks&lt;sup&gt;&lt;a href=&quot;#user-content-fn-mockinglibs&quot; id=&quot;user-content-fnref-mockinglibs&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; &lt;sup&gt;&lt;a href=&quot;#user-content-fn-doubles&quot; id=&quot;user-content-fnref-doubles&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Here are the mocks for the &lt;code&gt;Fetcher&lt;/code&gt; and &lt;code&gt;Enricher&lt;/code&gt; traits. These are easy, they are just pure functions, on every input they return a deterministic output:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TestFetcher&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Fetcher&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;data: &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;user.value&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TestEnricher&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Enricher&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;enrich&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;enriched: &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;user.value&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; - &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;data.value&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next we need to mock the &lt;code&gt;Bookkeeper&lt;/code&gt; trait. This one&apos;s trickier, since its functionality is a pure side-effect, all we &quot;return&quot; from &lt;code&gt;bookkeep&lt;/code&gt; is &lt;code&gt;Unit&lt;/code&gt;, which doesn&apos;t provide us any new information within the context of the test.&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TestBookkeeper&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Bookkeeper&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bookkept&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[(&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;&lt;span&gt;)]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;.empty &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bookkeep&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;original&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;enriched&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bookkept &lt;/span&gt;&lt;span&gt;::=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;original, enriched&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As a result, to be able to test proper usage of &lt;code&gt;Bookkeeper&lt;/code&gt; we have to use some mutable state:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In (1) we initialize a &lt;code&gt;var&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-doublelgasp&quot; id=&quot;user-content-fnref-doublelgasp&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; to hold the data that will be passed by &lt;code&gt;UberService&lt;/code&gt; to our mock.&lt;/li&gt;
&lt;li&gt;In (2) we add the data that was passed to the &lt;code&gt;bookkeep&lt;/code&gt; method.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And similarly for &lt;code&gt;Storage&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TestStorage&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stored&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;.empty&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;store&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;stored &lt;/span&gt;&lt;span&gt;::=&lt;/span&gt;&lt;span&gt; data&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enough mocks, let&apos;s get to business. Now we can write a test for &lt;code&gt;UberService&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;The uber-service&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;should&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fetch the user data, enrich it, and store the results&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bookkeeper&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TestBookkeeper&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;storage&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TestStorage&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;service&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UberService&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;TestFetcher&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;TestEnricher&lt;/span&gt;&lt;span&gt;&lt;span&gt;, bookkeeper, storage&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;expectedUserData&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;data: 5&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;expectedEnriched&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;enriched: 5 - data: 5&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;service.fetchAndStore&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bookkeeper.bookkept shouldBe &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;expectedUserData, expectedEnriched&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;storage.stored shouldBe &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;expectedEnriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The test is pretty-straightforward:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We initialize some mocks (1).&lt;/li&gt;
&lt;li&gt;Create a new a &lt;code&gt;UberService&lt;/code&gt; instance (2) with these mocks.&lt;/li&gt;
&lt;li&gt;Then execute the &lt;code&gt;fetchAndStore&lt;/code&gt; method (3), which is the thing actually being tested.&lt;/li&gt;
&lt;li&gt;And we assert the contents of our mocks, making sure they got the right data passed into them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By doing all that we verified that &lt;code&gt;UberService&lt;/code&gt; indeed performs the orchestration expected of it.&lt;/p&gt;
&lt;p&gt;But this kind of sucks...&lt;/p&gt;
&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;Bookkeeper&lt;/code&gt; and &lt;code&gt;Storage&lt;/code&gt; mocks are annoying. Writing mutable mocks is tedious and error-prone&lt;sup&gt;&lt;a href=&quot;#user-content-fn-okaymocks&quot; id=&quot;user-content-fnref-okaymocks&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;. Luckily for us &lt;code&gt;UberService&lt;/code&gt; is completely synchronous, but what would happen to that poor &lt;code&gt;var&lt;/code&gt; if we add some concurrency to the game? Not to mention race conditions and the like.&lt;/p&gt;
&lt;p&gt;Needing to inspect the innards of our mocks to be able to write the test is a definite code smell.&lt;/p&gt;
&lt;p&gt;To contrast, the mocks for &lt;code&gt;Fetcher&lt;/code&gt; and &lt;code&gt;Enricher&lt;/code&gt; are much simpler, they can be modeled by pure functions, given some input they produce some output. We do not have to deal with mutable state to see that they are used properly by &lt;code&gt;UberService&lt;/code&gt;. Their outputs can be used as indication that the functionality of &lt;code&gt;Fetcher&lt;/code&gt; and &lt;code&gt;Enricher&lt;/code&gt; was employed correctly.&lt;/p&gt;
&lt;p&gt;It all boils down to a single source of problems: we are trying to test side-effects. Since side-effects are not observable without other side-effects, we have to resort to using mutation to catch them.&lt;/p&gt;
&lt;p&gt;But what can we do, &lt;code&gt;Bookkeeper&lt;/code&gt; and &lt;code&gt;Storage&lt;/code&gt; are side-effectful components. Do we have any other choice?&lt;/p&gt;
&lt;h2 id=&quot;the-non-solutions&quot;&gt;The Non-Solutions&lt;/h2&gt;
&lt;p&gt;What makes &lt;code&gt;Fetcher&lt;/code&gt; and &lt;code&gt;Enricher&lt;/code&gt; nice to work with is that they produce outputs when called, while &lt;code&gt;Bookkeeper&lt;/code&gt; and &lt;code&gt;Storage&lt;/code&gt; only return &lt;code&gt;Unit&lt;/code&gt;. Well, what about adding some outputs to them. But what should we return? Here&apos;s one attempt:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;trait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Bookkeeper&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bookkeep&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;original&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;enriched&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;sealed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;trait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this new edition of &lt;code&gt;Bookkeeper&lt;/code&gt; instead of returning &lt;code&gt;Unit&lt;/code&gt; we are returning a new type called &lt;code&gt;Bookkept&lt;/code&gt; (1). Which is defined as a singleton (2).&lt;/p&gt;
&lt;p&gt;Great, we now have an output, just like &lt;code&gt;Fetcher&lt;/code&gt;. Can we get rid of the unpleasant mocks? Nope.&lt;/p&gt;
&lt;p&gt;Since &lt;code&gt;Bookkept&lt;/code&gt; is a singleton with no information beyond its existence, that is to say, isomorphic to &lt;code&gt;Unit&lt;/code&gt;, we cannot use it to glean any information on how &lt;code&gt;Bookkeeper&lt;/code&gt; was invoked. This is a dead-end.&lt;/p&gt;
&lt;p&gt;Okay, so let&apos;s add some information to the output:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;trait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Bookkeeper&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bookkeep&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;original&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;enriched&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UberService&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;/*&lt;/span&gt;&lt;span&gt; same as before &lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetchAndStore&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; same as before&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bookkept&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; bookkeeper.bookkept&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data, enriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;storage.store&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;enriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bookkept &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this version of &lt;code&gt;Bookkeeper&lt;/code&gt; we are returning the data that we need for the test (1), a tuple of &lt;code&gt;(UserData, EnrichedUserData)&lt;/code&gt;. This should give us everything we need. To be able to use that data, we have to hold on to it in &lt;code&gt;fetchAndStore&lt;/code&gt; (2), and then return it as the result of &lt;code&gt;fetchAndStore&lt;/code&gt; (3).&lt;/p&gt;
&lt;p&gt;This works, but feels kind of iffy. Why should the real production code know about the specific data that is needed for the test? And what if we have different kinds of tests that care about different pieces of data? Do we have to accommodate them all in our real code?&lt;/p&gt;
&lt;p&gt;The resulting code is both too rigid and concerns itself with something it shouldn&apos;t care about. How can we make it both more flexible and more ignorant
&lt;sup&gt;&lt;a href=&quot;#user-content-fn-ignorance&quot; id=&quot;user-content-fnref-ignorance&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;?&lt;/p&gt;
&lt;h2 id=&quot;the-solution&quot;&gt;The Solution&lt;/h2&gt;
&lt;p&gt;When some code requires both flexibility and ignorance it is very likely that the solution is to add a type parameter. Let me demonstrate:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;trait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Bookkeeper&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bookkeep&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;original&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;enriched&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now &lt;code&gt;Bookkeeper&lt;/code&gt; takes a type-parameter (1), and returns a value of that type as a result. This gives us maximum flexibility. In concrete implementations we can choose &lt;code&gt;A&lt;/code&gt; to be whatever we want, it can be &lt;code&gt;Unit&lt;/code&gt; for real production code, and it can be &lt;code&gt;(UserData, EnrichedUserData)&lt;/code&gt; for test code. And the best part of it is that &lt;code&gt;UberService&lt;/code&gt; doesn&apos;t care, it&apos;s ignorant of the concrete choice of &lt;code&gt;A&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UberService&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt;&lt;span&gt;](&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fetcher&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Fetcher&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;enricher&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Enricher&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bookkeeper&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Bookkeeper&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;storage&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetchAndStore&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; fetcher.fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;enriched&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; enricher.enrich&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user, data&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bookkept&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; bookkeeper.bookkeep&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data, enriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;storage.store&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;enriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bookkept &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code is very similar to the first attempt of returning the &lt;code&gt;Bookkept&lt;/code&gt; value. Unlike last time though, &lt;code&gt;Bookkept&lt;/code&gt; is now a type-parameter (1), so &lt;code&gt;UberService&lt;/code&gt; doesn&apos;t &quot;know&quot; what it&apos;s going to be, and doesn&apos;t care. The only thing we know is that the type parameter of the input &lt;code&gt;Bookkeeper&lt;/code&gt; (2) is going to be consistent with the output type of &lt;code&gt;fetchAndStore&lt;/code&gt; (3). All it does is pass it along.&lt;/p&gt;
&lt;p&gt;Because the code is now completely generic (or ignorant) we can accommodate both the real use-case (&lt;code&gt;Unit&lt;/code&gt;) and the test use-case (&lt;code&gt;(UserData, EnrichedUserData)&lt;/code&gt;), without further modifications to &lt;code&gt;UberService&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To wit, here&apos;s what the mock for &lt;code&gt;Bookkeeper&lt;/code&gt; will look like now:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TestBookkeeper&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Bookkeeper&lt;/span&gt;&lt;span&gt;[(&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;)]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bookkeep&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;original&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;enriched&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;original, enriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No more mutable state, we choose the type we need for the test, and the resulting mock is basically a pure function&lt;sup&gt;&lt;a href=&quot;#user-content-fn-identity&quot; id=&quot;user-content-fnref-identity&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;8&lt;/a&gt;&lt;/sup&gt;, just like the mocks for &lt;code&gt;Fetcher&lt;/code&gt; and &lt;code&gt;Enricher&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The test itself can easily check that &lt;code&gt;Bookkeeper&lt;/code&gt; was called with the correct input:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;The uber-service&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;should&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fetch the user data, enrich it, and store the results&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;service&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UberService&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;TestFetcher&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;TestEnricher&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;TestBookkeeper&lt;/span&gt;&lt;span&gt;&lt;span&gt;, storage&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bookkeeperResult&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; service.fetchAndStore&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bookkeeperResult shouldBe &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;expectedUserData, expectedEnriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We call &lt;code&gt;fetchAndStore&lt;/code&gt; as before (1). But instead of &lt;code&gt;Unit&lt;/code&gt; we have an actual result. Since we chose the type parameter of &lt;code&gt;Bookkeeper&lt;/code&gt; to be &lt;code&gt;(UserData, EnrichedUserData)&lt;/code&gt;, this is exactly the data we had before in our mock, and we can assert on it as before (2).&lt;/p&gt;
&lt;p&gt;What&apos;s more, it&apos;s not possible for &lt;code&gt;UberService&lt;/code&gt; to &quot;lie&quot; to us with that &lt;code&gt;Bookkeeper&lt;/code&gt; output. From the point of view of &lt;code&gt;UberService&lt;/code&gt; there&apos;s no way to produce a &lt;code&gt;Bookkept&lt;/code&gt; value without calling &lt;code&gt;Bookkeeper&lt;/code&gt;, unless you&apos;re cheating&lt;sup&gt;&lt;a href=&quot;#user-content-fn-cheating&quot; id=&quot;user-content-fnref-cheating&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;9&lt;/a&gt;&lt;/sup&gt;, parametric values cannot be faked into existence&lt;sup&gt;&lt;a href=&quot;#user-content-fn-free&quot; id=&quot;user-content-fnref-free&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;10&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;While our test is becoming more civilized, the production code knows nothing about it. The production implementation can remain the same by setting &lt;code&gt;A = Unit&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ProductionBookkeeper&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;/*&lt;/span&gt;&lt;span&gt; ... &lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Bookkeeper&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;rinse-and-repeat&quot;&gt;Rinse and Repeat&lt;/h2&gt;
&lt;p&gt;We can repeat the same trick with &lt;code&gt;Storage&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Add a type-parameter:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;trait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;store&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Propagate the new type through &lt;code&gt;UberService&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UberService&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt;](&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fetcher&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Fetcher&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;enricher&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Enricher&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bookkeeper&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Bookkeeper&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;storage&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetchAndStore&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; fetcher.fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;enriched&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; enricher.enrich&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user, data&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bookkept&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; bookkeeper.bookkeep&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data, enriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stored&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; storage.store&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;enriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;bookkept, stored&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a mock that&apos;s a pure function:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TestStorage&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Storage&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;store&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; data&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And use it in our test:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;The uber-service&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;should&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fetch the user data, enrich it, and store the results&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;service&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UberService&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;TestFetcher&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;TestEnricher&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;TestBookkeeper&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;TestStorage&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;expectedUserData&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UserData&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;data: 5&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;expectedEnriched&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EnrichedUserData&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;enriched: 5 - data: 5&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;bookkeeperResult, storageResult&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; service.fetchAndStore&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bookkeeperResult shouldBe &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;expectedUserData, expectedEnriched&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;storageResult shouldBe expectedEnriched&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Look at this lovely test! It&apos;s now completely pure. No more side-effects or mutable state. We are testing using pure-functions. The best kinds of test.&lt;/p&gt;
&lt;p&gt;This test has been officially purified.&lt;/p&gt;
&lt;div style=&quot;text-align: left; width: 15%;&quot;&gt;
&lt;img alt=&quot;pure-test&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;3000&quot; height=&quot;2815&quot; src=&quot;https://blog.daniel-beskin.com/_astro/pure-test.CKRZOR3M_Z24uFL0.webp&quot;&gt;
&lt;/div&gt;
&lt;h2 id=&quot;complications&quot;&gt;Complications?&lt;/h2&gt;
&lt;p&gt;The eagle-eyed readers amongst you might&apos;ve noticed the potential for proliferation of type parameters in our code. For every &lt;code&gt;Unit&lt;/code&gt;-returning method we now have to create a fake type-parameter. Is it worth it?&lt;/p&gt;
&lt;p&gt;Apart from the benefit of (much) nicer tests, I would argue that the resulting type-signature is actually more helpful:&lt;/p&gt;
&lt;pre data-language=&quot;scala&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetchAndStore&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt; vs.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetchAndStore&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UserID&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Bookkept&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Stored&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first signature describes an opaque side-effect, while the second signatures is more declarative, stating that the result of &lt;code&gt;fetchAndStore&lt;/code&gt; is something being bookkept and stored. I think that&apos;s a win.&lt;/p&gt;
&lt;p&gt;In the next part we&apos;ll see more advantages of being declarative this way in detail.&lt;/p&gt;
&lt;h2 id=&quot;but-im-already-doing-pure-fp&quot;&gt;But I&apos;m Already Doing Pure FP&lt;/h2&gt;
&lt;p&gt;You might also object that you&apos;re already doing pure FP with ZIO/Cats Effect/Finally Tagless. And that&apos;s great. But every time you see something like &lt;code&gt;IO[Unit]&lt;/code&gt;, the same considerations as above apply. You&apos;ll need to test a side-effect, and resort to some sort of mutability&lt;sup&gt;&lt;a href=&quot;#user-content-fn-future&quot; id=&quot;user-content-fnref-future&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;11&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Unless you&apos;re doing Finally Tagless in which case in your tests you can replace &lt;code&gt;F[Unit]&lt;/code&gt; with something like &lt;code&gt;Writer[Unit]&lt;/code&gt; and still have a pure test. Though I still think that adding a type-parameter might be worthwhile.&lt;/p&gt;
&lt;p&gt;In any case, the general lesson is still:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Friends, don&apos;t let friends use &lt;code&gt;Unit&lt;/code&gt; as a return value.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;We&apos;ve seen how we can mechanically turn our side-effecting, mutable tests into nice pure functions, just by adding some type-parameters. The results are nicer, more declarative tests.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;https://blog.daniel-beskin.com/2024-05-20-purify-tests-2&quot;&gt;next part&lt;/a&gt;, in the spirit of &lt;a href=&quot;https://www.youtube.com/watch?v=BHjIl81HgfE&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&quot;one &apos;simple&apos; design change, a panoply of outcomes&quot;&lt;/a&gt;, we are going to take a look at some other benefits of parameterizing our code this way.&lt;/p&gt;
&lt;p&gt;Happy purifying!&lt;/p&gt;
&lt;div class=&quot;mx-auto w-50&quot;&gt;
&lt;a href=&quot;https://www.lambdatest.com/newsletter/&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;506&quot; height=&quot;226&quot; src=&quot;https://blog.daniel-beskin.com/_astro/codingjag.CpxOR38Y_Z2csq2Y.webp&quot;&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-java&quot;&gt;
&lt;p&gt;Gasp... &lt;a href=&quot;#user-content-fnref-java&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-fullcode&quot;&gt;
&lt;p&gt;The full code for the examples is available on &lt;a href=&quot;https://github.com/ncreep/purify-tests-blog&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-fullcode&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-mockinglibs&quot;&gt;
&lt;p&gt;You could also use a mocking library for this purpose, but I personally prefer to avoid them. &lt;a href=&quot;#user-content-fnref-mockinglibs&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-doubles&quot;&gt;
&lt;p&gt;Don&apos;t catch me on the terminology here. Mocks, spies, stunt doubles, whatever kids these days call them, for our purposes here they are all the same. &lt;a href=&quot;#user-content-fnref-doubles&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-doublelgasp&quot;&gt;
&lt;p&gt;Double gasp... &lt;a href=&quot;#user-content-fnref-doublelgasp&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-okaymocks&quot;&gt;
&lt;p&gt;Okay, using a mocking library would make this less tedious. It can still be error-prone. And in any case I have no incentive to make mocking easy, I want people to feel the pain of mocking, so that they gravitate to code that doesn&apos;t rely on it that much. Arguably, such code would be of better quality compared to code that requires many mocks to test. &lt;a href=&quot;#user-content-fnref-okaymocks&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 6&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-ignorance&quot;&gt;
&lt;p&gt;I mean this in the Orwellian sense of &quot;ignorance is strength&quot;. And see &lt;a href=&quot;https://www.youtube.com/watch?v=QDaIC6OD6sU&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;this&lt;/a&gt; talk for more. &lt;a href=&quot;#user-content-fnref-ignorance&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 7&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-identity&quot;&gt;
&lt;p&gt;Equivalent to the identity function. &lt;a href=&quot;#user-content-fnref-identity&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 8&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-cheating&quot;&gt;
&lt;p&gt;By &quot;cheating&quot; I mean things like subverting the type-system with &lt;code&gt;null&lt;/code&gt;s, casts, and the like. See the &lt;a href=&quot;https://failex.blogspot.com/2019/07/scalazzi-safe-scala-subset-principles-2.html&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;Scalazzi&lt;/a&gt; subset of Scala. &lt;a href=&quot;#user-content-fnref-cheating&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 9&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-free&quot;&gt;
&lt;p&gt;This is a loose application of &lt;a href=&quot;https://en.wikipedia.org/wiki/Parametricity&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;&quot;parametricity&quot;&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-free&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 10&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-future&quot;&gt;
&lt;p&gt;The same reasoning applies to non-pure container types, like &lt;code&gt;Future&lt;/code&gt;. &lt;a href=&quot;#user-content-fnref-future&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 11&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><author>Daniel Beskin</author></item><item><title>When Are Functions Lazy Enough for Lists</title><link>https://blog.daniel-beskin.com/2024-05-02-lazy-enough</link><guid isPermaLink="true">https://blog.daniel-beskin.com/2024-05-02-lazy-enough</guid><description>Recently, I&apos;ve been TA-ing on an introduction to functional programming course, using Haskell. This is a very enlightening experience, as it once again proves the notion that the best way to learn…</description><pubDate>Thu, 02 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://blog.daniel-beskin.com/social-cards/2024-05-02-lazy-enough.png&quot; alt=&quot;Cover image for When Are Functions Lazy Enough for Lists&quot; style=&quot;width: 100%; height: auto; margin-bottom: 2rem;&quot;&gt;&lt;p&gt;Recently, I&apos;ve been TA-ing on an introduction to functional programming course, using Haskell. This is a very enlightening experience, as it once again proves the notion that the best way to learn something is to teach it.&lt;/p&gt;
&lt;p&gt;Since Haskell is rife with topics that I feel I understand but are still tricky to actually explain&lt;sup&gt;&lt;a href=&quot;#user-content-fn-foldr&quot; id=&quot;user-content-fnref-foldr&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, trying to answer student question can sometimes prove more challenging than expected. This post is my attempt to answer one such question that I got the other day.&lt;/p&gt;
&lt;p&gt;Disclaimer: this post is aimed at Haskell beginners or teachers.&lt;/p&gt;
&lt;p&gt;As part of their homework, the students had to reimplement some basic list functions on their own. Part of the requirements were that when possible a function accepting a list should seamlessly work for both finite and infinite lists. Given that Haskell is lazy by default, this tends to happen magically on its own, without any extra effort. But trying to understand when this works or not is another matter.&lt;/p&gt;
&lt;p&gt;Here I&apos;ll try to add some intuition for the following question:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When is a function that operates on lists is &quot;lazy enough&quot; to handle infinite lists?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The answer I&apos;m going to propose is that&lt;sup&gt;&lt;a href=&quot;#user-content-fn-notformal&quot; id=&quot;user-content-fnref-notformal&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A function on lists is &quot;lazy enough&lt;sup&gt;&lt;a href=&quot;#user-content-fn-productivity&quot; id=&quot;user-content-fnref-productivity&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&quot; when for all inputs it uses a finite chunk of the input to produce a finite chunk of the output.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The answer might be even more confusing, so let&apos;s check out some examples to get intuition.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;head&lt;/code&gt; function&lt;sup&gt;&lt;a href=&quot;#user-content-fn-unsafe&quot; id=&quot;user-content-fnref-unsafe&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;head &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;x &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; _&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; x&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;head &lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; error &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;head on an empty list&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;head&lt;/code&gt; takes in a list, a potentially infinite list, but it only looks at the head of the list (a finite chunk) and ignores the tail (a potentially infinite chunk of data). So &lt;code&gt;head&lt;/code&gt; is &quot;lazy enough&quot; to handle infinite lists:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;head &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What about &lt;code&gt;tail&lt;/code&gt;?&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;tail &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_ &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; xs&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; xs&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;tail &lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; error &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tail on an empty list&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To produce a finite chunk of the tail, we only need to skip the head (a finite chunk) and then produce the (finite) chunk of the tail of the list.
So this works:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;head &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; tail &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Similarly functions like &lt;code&gt;take n&lt;/code&gt;, only consume a finite chunk of elements (&lt;code&gt;n&lt;/code&gt;) to produce a finite chunk of elements (a list of &lt;code&gt;n&lt;/code&gt; elements).&lt;/p&gt;
&lt;p&gt;What are examples that are not lazy enough?&lt;/p&gt;
&lt;p&gt;Take &lt;code&gt;length&lt;/code&gt; for example:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;length &lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;length &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_ &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; xs&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; length xs&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, to produce a finite chunk of the result (a single number), we must consume the whole list (a potentially infinite chunk of data). So there&apos;s no way this can work for infinite lists.&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;length &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; never terminates&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The same goes for &lt;code&gt;reverse&lt;/code&gt;: the first element of the result of &lt;code&gt;reverse&lt;/code&gt; is the last element of the input list. To produce a finite chunk of the result (the last element of the input), we must consume the whole, potentially infinite, list. And so &lt;code&gt;reverse&lt;/code&gt; does not work for infinite lists.&lt;/p&gt;
&lt;p&gt;An important thing to note is that different implementations of the same function can behave differently with respect to laziness depending on the implementation details. Consider the following implementation of &lt;code&gt;head&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;badHead xs &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; length xs &lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt; head&apos; xs&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; error &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;head on empty list&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;head&apos; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;x &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; _&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; x&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This implementation tries to be &quot;smart&quot; about checking the error condition. But now in order to produce a finite chunk (the head of the list) we must consume the whole (potentially infinite) list to check its length. The new implementation of &lt;code&gt;head&lt;/code&gt; no longer works for infinite lists:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;badHead &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; never terminates&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let&apos;s take a look at something more complicated. Figuring out when the functions above are lazy enough or not is probably not that difficult. Let&apos;s analyze a more challenging function: &lt;code&gt;partialSums&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Given a list of numbers, &lt;code&gt;partialSums&lt;/code&gt; creates a new list of sums of the numbers up to the current element:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;partialSums &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;6&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;15&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And it should also work for infinite lists:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;take &lt;/span&gt;&lt;span&gt;6&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; partialSums &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;6&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;15&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can implement this function using explicit recursion:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;partialSums &lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;partialSums &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;x &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; xs&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;x &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;partialSums xs&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why does this work for infinite lists? Let&apos;s say we want the first element of &lt;code&gt;partialSums [1..]&lt;/code&gt;. We can follow the equations in this definition&lt;sup&gt;&lt;a href=&quot;#user-content-fn-equational&quot; id=&quot;user-content-fnref-equational&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;partialSums &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; pattern matching&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;partialSums &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; by definition&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;partialSums &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Assuming that &lt;code&gt;(:)&lt;/code&gt; is lazy (it is), we can access the first element &lt;code&gt;0&lt;/code&gt; without accessing the rest of the (infinite) result.&lt;/p&gt;
&lt;p&gt;To be able to continue evaluation, recall the definition of &lt;code&gt;map&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;map _ &lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;map f &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;x &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; xs&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; f x &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; map f xs&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;map&lt;/code&gt; itself is lazy, to get the first element of its result, we only need to evaluate &lt;code&gt;f&lt;/code&gt; on the head of the result. No need to go over the possibly infinite tail.&lt;/p&gt;
&lt;p&gt;Let&apos;s continue evaluation and grab the next element:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;partialSums &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; evaluating the first step of `partialSums` so that we can pattern match on the result&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;partialSums &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;])))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; by definition of `map`&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;partialSums &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;])))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; reduce arithmetic&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;partialSums &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;])))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice how pattern matching forced us to evaluate the next step of &lt;code&gt;partialSums&lt;/code&gt;, we can&apos;t choose the correct branch of &lt;code&gt;map&lt;/code&gt; without knowing whether the list is empty or not. But we don&apos;t need anything beyond that, we do not evaluate the whole list, just the existence of the first head/tail pair. More generally, pattern matching is one way to gradually force the evaluation of lists.&lt;/p&gt;
&lt;p&gt;The next element &lt;code&gt;1&lt;/code&gt; is available for grabbing, while the rest of the tail is not evaluated by &lt;code&gt;(:)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Just for fun, we&apos;ll repeat this once again for the third element:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;partialSums &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;])))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; evaluate `partialSums`&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;partialSums &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;])))))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; evaluate the second `map`&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;partialSums &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;])))))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; reduce arithmetic&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;partialSums &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;])))))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; apply first `map`&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;partialSums &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;]))))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; reduce arithmetic&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;partialSums &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;]))))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now &lt;code&gt;3&lt;/code&gt; is available to us using a finite amount of elements from the input list. We can go on like this, every time consuming just one element from the input list to produce one element from the output. Since &lt;code&gt;(:)&lt;/code&gt; and &lt;code&gt;map&lt;/code&gt; are both lazy, we are never forced to evaluate the whole tail, just a finite chunk of it.&lt;/p&gt;
&lt;p&gt;We can also rewrite &lt;code&gt;partialSums&lt;/code&gt; in terms of &lt;code&gt;foldr&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;partialSums &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; foldr aux &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt; aux x res &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;x &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; res&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a mechanical rewriting of the recursive solution&lt;sup&gt;&lt;a href=&quot;#user-content-fn-sparemyself&quot; id=&quot;user-content-fnref-sparemyself&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;. Since &lt;code&gt;foldr&lt;/code&gt; is implemented as the same recursion pattern that we wrote manually before, this works just like the recursive version. Therefore, it still works for infinite lists.&lt;/p&gt;
&lt;p&gt;We can prove it using the same logic, we just have to apply &lt;code&gt;foldr&lt;/code&gt; first. Recall that &lt;code&gt;foldr&lt;/code&gt; is defined as:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;foldr _ z &lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; z&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;foldr f z &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;x &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; xs&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; f x &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;foldr f z xs&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can evaluate the new &lt;code&gt;partialSums&lt;/code&gt; again:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;partialSums &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; by definition `partialSums`&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;foldr aux &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; pattern matching in `foldr`&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;foldr aux &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; by definition of `foldr`&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aux &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;foldr aux &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; apply the same logic recursively&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aux &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;aux &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;foldr aux &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;]))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aux &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;aux &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;aux &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;foldr aux &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;])))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; expand the `aux` calls&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aux &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;aux &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;foldr aux &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;]))))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;aux &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;foldr aux &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;])))))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;foldr aux &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;]))))))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From here this looks pretty much the same as in the recursive version, and is lazy for the same reasons.&lt;/p&gt;
&lt;p&gt;Now suppose that we had a non-lazy version of &lt;code&gt;map&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;badMap f xs &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; length xs &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; f &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;head xs&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; badMap f &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;tail xs&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is logically the same thing as the regular &lt;code&gt;map&lt;/code&gt;, but it is no longer lazy, since it calls &lt;code&gt;length&lt;/code&gt; which forces the evaluation of the whole list.&lt;/p&gt;
&lt;p&gt;If we use &lt;code&gt;badMap&lt;/code&gt; to implement &lt;code&gt;partialSums&lt;/code&gt;:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;badPartialSums &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; foldr aux &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt; aux x res &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; badMap &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;x &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; res&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result will no longer be sufficiently lazy, and won&apos;t work for infinite lists:&lt;/p&gt;
&lt;pre data-language=&quot;haskell&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span&gt;take &lt;/span&gt;&lt;span&gt;6&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; badPartialSums &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;..&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; never terminates&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If our function calls a &quot;not lazy enough&quot; function, then it stops being &quot;lazy enough&quot;.&lt;/p&gt;
&lt;p&gt;Hopefully now the statement we started with makes more sense:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A function on lists is &quot;lazy enough&quot; when for all inputs it uses a finite chunk of the input to produce a finite chunk of the output.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Thanks for reading this far. I hope this helped you gain some intuition about laziness and infinite lists.
Reach out if you have any further questions or comments.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-foldr&quot;&gt;
&lt;p&gt;&lt;code&gt;foldr&lt;/code&gt; anyone? &lt;a href=&quot;#user-content-fnref-foldr&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-notformal&quot;&gt;
&lt;p&gt;This is not a formal definition, but good enough for our purposes. I&apos;m not trying to be precise and fully explain laziness in Haskell, but just to generate enough intuition for the students&apos; homework. &lt;a href=&quot;#user-content-fnref-notformal&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-productivity&quot;&gt;
&lt;p&gt;As was noted in the &lt;a href=&quot;https://www.reddit.com/r/haskell/comments/1cig4gq/when_are_functions_lazy_enough_for_lists/&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;Reddit discussion&lt;/a&gt;, I&apos;m actually overloading the word &quot;lazy&quot;, when what I actually mean by &quot;lazy enough&quot; is &quot;&lt;a href=&quot;https://www.win.tue.nl/~hzantema/pcs.pdf&quot; rel=&quot;noreferrer noopener&quot; target=&quot;_blank&quot;&gt;productive&lt;/a&gt;&quot;. Since we&apos;re using Haskell, laziness is entangled with productivity anyways. I&apos;m still on the fence on whether it&apos;s worthwhile introducing this new terminology while in the context of this particular homework set. &lt;a href=&quot;#user-content-fnref-productivity&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-unsafe&quot;&gt;
&lt;p&gt;We&apos;ll ignore for the moment that &lt;code&gt;head&lt;/code&gt; and &lt;code&gt;tail&lt;/code&gt; are not safe and should be banished from our lives. I don&apos;t want to bring in &lt;code&gt;Maybe&lt;/code&gt;, which might be distracting from the point. &lt;a href=&quot;#user-content-fnref-unsafe&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-equational&quot;&gt;
&lt;p&gt;This process of moving through equations is called &quot;equational reasoning&quot;. A very useful tool when trying to understand Haskell code. &lt;a href=&quot;#user-content-fnref-equational&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-sparemyself&quot;&gt;
&lt;p&gt;I&apos;ll spare myself from explaining the workings of &lt;code&gt;foldr&lt;/code&gt; in this post. &lt;a href=&quot;#user-content-fnref-sparemyself&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 6&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><author>Daniel Beskin</author></item></channel></rss>