<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"
    xmlns:dc="http://purl.org/dc/elements/1.1/">
    <channel>
        <title>Chris Grounds</title>
        <link>https://chrisgrounds.github.io</link>
        <description><![CDATA[Software, types, functional programming, and the ideas around them.]]></description>
        <atom:link href="https://chrisgrounds.github.io/feed.xml" rel="self"
                   type="application/rss+xml" />
        <lastBuildDate>Thu, 08 Oct 2026 00:00:00 UT</lastBuildDate>
        <item>
    <title>Programming as Discovery</title>
    <link>https://chrisgrounds.github.io/2026/10/08/programming-as-discovery.html</link>
    <description><![CDATA[<p>I want to talk about two views on the purpose of programming, which I’ll call <em>programming-as-discovery</em> and <em>programming-as-output</em>. These are not exhaustive views but they are two views that have been on my mind a lot lately, especially with the rise of LLM-assisted and vibe-coding.</p>
<p><em>programming-as-discovery</em> is the view that the purpose of programming is not necessarily to produce code as an output. Rather, the desired end of programming is the building of understanding, a shared language, a coherent mental model, intuition about a system (which leads to ability to debug, insight into performance, etc), hypothesis formation, new techniques and insights to doing things better, amongst many other similar ends. And yes, code often is a vital end in the task of the programmer. Code is the proof to our thinking, validates our understanding, and provides many goods to real users (if done well). I take this to be what engineers like Ryan Fleury, Casey Muratori, and others think, along with what I take to be the real meaning of the original agile manifesto.</p>
<p><em>programming-as-output</em> is the view that the purpose of programming is that of producing code-artefacts to, ideally, satisfy some user need. Although more commonly I have seen the code-artefact itself appear to be the end goal. I wouldn’t necessarily say there are any specific proponents of this view, but I see it becoming a more common implicit view recently due to the advances in LLM models and harnesses, which have allowed engineers to jump straight to the sole end goal of producing code.</p>
<p>I don’t want to say that either view is correct. They are both useful views depending on what our purposes are. Practicing <em>programming-as-output</em> can be very useful when used <em>as part</em> of a hypothesis-formation/testing flow; for example when creating throw-away prototype code, we don’t care about building understanding during this specific activity of programming, we just want to test a theory and then throw it away. Another example can be found in the recent Bun rewrite into Rust. It was completely an LLM-generated rewrite into Rust from Zig. Based on what I’ve read I think it’s fair to say that during the course of this rewrite there was minimal development of understanding, shared language, clear intuition and mental models, etc. The end sole end goal was simply to replace Zig with Rust for an already well-understood domain. And it must be said that LLMs are remarkably good at porting from one language to another.</p>
<p>But I do worry that we, as an industry, are forgetting the value in <em>programming-as-discovery</em>, and I am worried that even if we proclaim to still care about those particular ends associated with <em>programming-as-discovery</em> we will in practice jettison them in the name of “productivity”.</p>
<p>So I do want to argue that the increasing use of and reliance on LLMs in coding makes <em>programming-as-discovery</em> very hard to do. I think LLMs, especially as used indiscriminately today, are incredibly harmful to <em>programming-as-discovery</em>. In my experience, they lead to railroaded solutions which are not deeply thought through, a lack of shared understanding, a lack of a coherent and complete mental model, a loss of familarity with the code and domain, and a general de-skilling of engineers. I have had conversations with very talented engineers who now feel like they are becoming much less intelligent and able to think independently. (There is certainly an argument to be made around human-flourishing and excellence, but that’s not in scope here.)</p>
<p>My worry is amplified by some recently examples; for example, a SpaceX/Cursor employee wrote about how she is shipping 1,500 PRs a month. The API token cost of the LLM usage was estimated to be several million dollars a month. I can’t imagine this is sustainable economically, but regardless I cannot believe this is remotely sustainable from a product or user-perspective. And let’s not forget, as Fleury <a href="https://www.dgtlgrove.com/p/programmers-are-users-bad-performance">wrote here</a>, programmers are also users. And we see this drive more and more with the so-called “software factories”. What more vivid a picture can we see with the word “factory”? What is a factory, after all, other than something which produces standardised items at industrial scale? There’s no room for <em>programming-as-discovery</em> here, the factory must grow and must churn out more and more output. We just want code, we don’t need to understand, to think, to model, to share, etc. The users will accept change at a million miles an hour and they will like it.</p>
<p>And this leads us to the next question when we consider <em>programming-as-output</em> running in an LLM loop. How fast is too fast? If the end goal is just to ship more and more code, then surely there isn’t a “too fast”. But if we take the end goal of programming to be the multi-faceted <em>programming-as-discovery</em>, then “too fast” is just when we begin to lose our ability to meet the ends of <em>programming-as-discovery</em>. And I see this to be a common occurance in engineering discourse; engineers moving too fast they no longer understand the systems they are working on, are able to build and hold a mental model of it, and are able to improve and refine it (without blindly trusting the output of an LLM).</p>
<p>Which brings me back to the agile manifesto. Why does it care about “responding to change over following a plan”? Well, the answer is easy to see if we think of programming through the lens of <em>programming-as-discovery</em>. We should prioritise change because discovery will lead us to many changes as our understanding grows, shifts, and changes. Of course, LLMs can make refactoring extremely easy to do, but the challenge is that knowing what and how to refactor becomes very hard when we lose deep understanding of the code.</p>
<p>And so as far as I can see this just cannot be a good thing to lose. We mustn’t mistake one end of programming - namely, code - to be the whole end of it. Sometimes this is fine, but sometimes it’s not, and I do worry the software industry is shifting too much to the side that says it’s always fine.</p>]]></description>
    <pubDate>true</pubDate>
    <guid>https://chrisgrounds.github.io/2026/10/08/programming-as-discovery.html</guid>
    <dc:creator>Chris Grounds</dc:creator>
</item>
<item>
    <title>Verifying Writing</title>
    <link>https://chrisgrounds.github.io/2026/09/14/verifying-writing.html</link>
    <description><![CDATA[<p>After reading <a href="https://bcantrill.dtrace.org/2026/09/05/the-revolt-of-the-reader/">Bryan Cantrill’s blog on reading</a> and then reading the linked <a href="https://writethatblog.substack.com/p/dev-reaction-to-ai-blog-posts">Cynthia Dunlop report on AI-written blog posts</a> I’ve instituted a new policy on my blog, which is to add a pangram verified link to my blogs going forward to verify that my writing is not AI-assisted or AI-generated.</p>
<p>I think this is important because as writers, we are demanding time and attention from our readers. As such we owe them an obligation that we are not wasting their time and attention. It’s clear that no-one likes AI-writing, so much so that Cynthia found that,</p>
<blockquote>
<p>if readers think your article is AI-assisted or AI-authored, most will immediately leave (78%), avoid you forever (71%), and try to downvote you if they can (57%). 17% try to finish but lose interest, and around 15% will only continue if the underlying insights seem authentic.</p>
</blockquote>
<p>But beyond just not using AI for my writing, I think it might be useful and more respectful of my readers’ time if I can say at the outset that they are not needing to be on guard for AI-written content in what follows. Because one of the other findings from Cynthia’s report was that “73% [of those using LLMs] did not disclose that they used LLMs to write” their content.</p>
<p>This isn’t surprising in the slightest, and perhaps the only way to counter the rise of AI-written content is for human-written content to itself highlight that it is human-written. That will then put the spotlight on content that is not verifying itself as human-written. This may be a stop-gap until browsers add automatic AI-detection. We’ve already seen Claude add watermarks to Claude-output, so it can’t be long until the verification and detection of AI-written content is standardised across devices and software.</p>
<p>Until then, I’ll try to provide verification of my own writing on this blog.</p>]]></description>
    <pubDate>true</pubDate>
    <guid>https://chrisgrounds.github.io/2026/09/14/verifying-writing.html</guid>
    <dc:creator>Chris Grounds</dc:creator>
</item>
<item>
    <title>The Code Smell of LLM Defensive Coding</title>
    <link>https://chrisgrounds.github.io/2026/09/03/defensive-coding.html</link>
    <description><![CDATA[<p>LLMs are renowned for writing defensive code. And often that defensive code is genuinely pointless, but there is a lot of it that, in my view, is a sign your types are not constraining enough. For example, suppose we want to store a 1-10 grade value on a student type, which is updatable. Asking GPT it created the following,</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> Student <span class="op">{</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>  grade<span class="op">:</span> <span class="dt">u8</span><span class="op">,</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> update_grade(student<span class="op">:</span> <span class="op">&amp;</span><span class="kw">mut</span> Student<span class="op">,</span> new_grade<span class="op">:</span> <span class="dt">u8</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">,</span> <span class="dt">String</span><span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> <span class="op">!</span>(<span class="dv">1</span><span class="op">..=</span><span class="dv">10</span>)<span class="op">.</span>contains(<span class="op">&amp;</span>new_grade) <span class="op">{</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> <span class="cn">Err</span>(<span class="st">&quot;Grade must be between 1 and 10&quot;</span><span class="op">.</span>to_string())<span class="op">;</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>  student<span class="op">.</span>grade <span class="op">=</span> new_grade<span class="op">;</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a>  <span class="cn">Ok</span>(())</span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>Taken in isolation this is fairly sensible code, but the invariant of <em>between-1-and-10</em> is only enforced in the <code>update</code> function, so nowhere else in the code would know about it, and the defensive guard makes the function fallible - although nicely in Rust we turn that failure into a value in its own right rather than throw an exception. Nonetheless, defensive code takes what should be straight-forward code we can reason about and complicates it. And of course the annoying aspect of LLM-written defensive code is that it’s often ubiqutous, where most functions across the codebase end up with some form of defensive code. Overall, it leads to far too much code being written and I think is an aspect of why LLMs write so much code overall.</p>
<p>So how do we do better? I think the core problem here is that the type is wrong. We’re encoding the student’s grade with 256 possible values in the <code>u8</code>. But we don’t actually want 256 values, which is why the LLM has attempted to guard against the other 246 values we don’t want. It’s correctly trying to enforce our invariant because our types don’t express it.</p>
<p>There are a couple of different approaches we could take to fix that. The first is simply specify our possible values in a sum-type,</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> Grade <span class="op">{</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>  One<span class="op">,</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>  Two<span class="op">,</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>  <span class="op">...,</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>  Ten<span class="op">,</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> update_grade(student<span class="op">:</span> <span class="op">&amp;</span><span class="kw">mut</span> Student<span class="op">,</span> new_grade<span class="op">:</span> Grade) <span class="op">{</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>  student<span class="op">.</span>grade <span class="op">=</span> new_grade<span class="op">;</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>Now there is no possible way we can accidentally give the student a grade of 200 because it doesn’t exist as an option. There’s just no defensive code to write. However, one downside of this approach comes from wanting to extend the <code>Grade</code> type - if, for example, you decide you actually want 20 grades instead of 10. Additionally you would also need to implement functions that you probably take for granted with number types such as addition and subtraction.</p>
<p>The middle-ground approach is to instead wrap the <code>u8</code> in a newtype smart-constructor,</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> Grade(<span class="dt">u8</span>)<span class="op">;</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> OutOfBounds<span class="op">;</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="kw">impl</span> Grade <span class="op">{</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">fn</span> try_new(grade<span class="op">:</span> <span class="dt">u8</span>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span><span class="dt">Self</span><span class="op">,</span> OutOfBounds<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="op">!</span>(<span class="dv">1</span><span class="op">..=</span><span class="dv">10</span>)<span class="op">.</span>contains(<span class="op">&amp;</span>grade) <span class="op">{</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span> <span class="cn">Err</span>(OutOfBounds)<span class="op">;</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>    <span class="cn">Ok</span>(<span class="dt">Self</span>(grade))</span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>This forces our invariant into the constructor on the newtype and gives us a custom error value to clearly indicate what has gone wrong. With this knowledge an LLM would be more likely to resist the implementation of so much defensive code.</p>
<p>All in all, I think LLM-written defensive code is often a symptom of under-constrained types. We can do a better job and achieve better outcomes from LLM-written code by constraining our types to make sure we’re encoding what we actually care about.</p>]]></description>
    <pubDate>true</pubDate>
    <guid>https://chrisgrounds.github.io/2026/09/03/defensive-coding.html</guid>
    <dc:creator>Chris Grounds</dc:creator>
</item>
<item>
    <title>Rust on the Frontend at Switch2</title>
    <link>https://chrisgrounds.github.io/2026/08/14/rust-on-the-frontend-at-switch2.html</link>
    <description><![CDATA[<p><em>This blog was originally published on the <a href="https://tech.switch2.co.uk/">Switch2 technical blog</a></em></p>
<p>It’s probably fair to say that when people think of Rust they typically think of it as a systems or backend programming language, and this perception has not been entirely without merit. The Rust web offering has certainly been the least well-known and least mature part of the ecosystem. However, that’s not to say it is a poor part of the ecosystem. <a href="https://leptos.dev/">Leptos</a> and <a href="https://dioxuslabs.com/">Dioxus</a>, for example, are two very well-developed web frameworks. Dioxus, which this blog is built with, is a fully-fledged cross-platform framework that can target web, desktop, and apps from a single codebase. Leptos is a very solid and, by now mature, web framework that has similar ergonomics to JS frameworks like Solid.js. We have built - and are in active development of - three major enterprise-grade web applications, both customer and internal facing, with Leptos and found very few teething issues. Instead, the API is stable and has supported everything we’ve wanted and needed to do, with good performance and good developer ergonomics.</p>
<p>In this blog, I’d like to explain how we ended up with Rust on the frontend and what our process was. But before jumping there, a little history. Switch2, around 4 years ago, was mainly using JavaScript (JS) isomorphically - Node.js on the backend and JS React on the frontend. We soon pivoted all new code to be written in TypeScript (TS). Compared to untyped JS, TS improved safety considerably, but we found it remained quite a poor language for domain modelling and creating performant software. We benchmarked Rust and TS on an AWS Lambda function and found the Rust equivalent to be quicker and use significantly less RAM, which gave us not only a performance increase on our backend processing but also a cost-reduction - as AWS Lambda is billed by compute time <em>and</em> memory usage. We soon found Rust also provided excellent support for domain modelling via its native support for sum-types and product-types, something which TS somewhat makes ergonomically difficult, and we decided to write all new backend software in Rust.</p>
<p>We then started to build out a couple of small, new applications using TS + Next.js and found one major point of friction around type-maintenance. The problem comes down to using two typed-languages across a serialization-boundary. On the backend we might have a type like,</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> AccountBalance <span class="op">{</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>  balance<span class="op">:</span> Money<span class="op">,</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>which we want to send over a TCP connection, and so on the frontend we need to duplicate the type in some way. Since TypeScript doesn’t natively support newtype constructs due to its structural typing, the best we can do is a half-hearted attempt at duplicating it,</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode typescript"><code class="sourceCode typescript"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">interface</span> AccountBalance {</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>  balance<span class="op">:</span> <span class="dt">number</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p>Note that not only are we having to duplicate our types - and so increase maintenance effort - we are also losing information as we deserialize into a weaker and less expressive language.</p>
<p>Initially, in order to reduce the maintenance effort of having to duplicate types, we built a tool called <code>binding</code>, which translated Rust types into TS types via the <a href="https://github.com/aleph-alpha/ts-rs">ts-rs</a> Rust library. This approach worked well in saving us from having to create matching TS types by hand, but still left us with duplicate type information in the codebase - and more importantly, left us dealing with a less expressive type-system on the frontend.</p>
<p>It was at this point we decided to investigate using Leptos for our web applications. At this point we had already started building a new web platform in TS/Next.js, and so decided to rewrite it into Leptos. The mechanical processing power of LLMs was a great advantage here as it translated Next.js concepts and syntax over to Leptos. Within around a day we had a fully-functioning feature-parity website. Obviously, the Leptos codebase had far fewer types as it could make use of the domain types in Rust. The only changes to those types that we needed were to make them <code>serde::Serialize</code> and <code>serde::Deserialize</code> so we could send them over a TCP connection. We considered this experiment to be a success and decided to do all future web-development in Rust.</p>
<p>In terms of web performance, we have trivially managed to score 100/100 on the Google Lighthouse score for the Leptos web applications and have found Rust-produced wasm to be more than fast enough for our use-cases. The worry with wasm DOM manipulations is that the wasm code incurs a small cost every manipulation. However, Leptos is a signal-based reactive model which leads to only very small islands of interactivity being modified in the DOM. The main issue, performance-wise, we had was around wasm bundle load sizes. We dramatically lowered load sizes by compressing and optimizing the wasm bundle. In any case, that Leptos performs well shouldn’t be a huge surprise given that Leptos <a href="https://krausest.github.io/js-framework-benchmark/current.html">scores higher</a> than many JS frameworks like Vue and Reflex.</p>
<p>Additionally, we no longer have to write so much defensive code to protect against <code>null</code> or <code>undefined</code>, which is of course one of the pitfalls of JS/TS, and we can leverage the power of a more expressive type-system, thereby bringing our frontend code up to the same standards we hold our backend code in. Moreover, we’ve also found LLM models to be more than equipped to write Leptos code and we’ve not found the up-skilling journey for our engineers to be too difficult or onerous. The framework is familiar to anyone coming from a React background, as all of us were.</p>
<p>However, it hasn’t been completely smooth sailing as there have been a couple of gotchas that we’ve had to learn about; for example, in deeply nested views we’ve sometimes needed to use type-erasure to ensure the compiler doesn’t hit the recursion limit, similar to JS SSR-frameworks we’ve encountered hydration errors, and in order to lazy load routes we’ve had a couple of small issues with wasm-split in relation to hot-reloading and some caching behaviour.</p>
<p>In conclusion, this has been a very interesting and successful experiment. We’ve managed to reduce duplication at the type-level, provide a more consistent developer experience, and ensure the same type-level guarantees across frontend and backend - without any change to the user experience.</p>]]></description>
    <pubDate>true</pubDate>
    <guid>https://chrisgrounds.github.io/2026/08/14/rust-on-the-frontend-at-switch2.html</guid>
    <dc:creator>Chris Grounds</dc:creator>
</item>
<item>
    <title>Impossible Tests</title>
    <link>https://chrisgrounds.github.io/2026/07/18/impossible-tests.html</link>
    <description><![CDATA[<p>The best test is the one you can’t write. It’s not one which checks some invariant or ensures behaviour works as expected, it’s one which is literally impossible to write.</p>
<p>Suppose for some blogging software we have a requirement that a blog must have <em>one or more</em> authors. That makes sense, a blog can’t spontaneously come into existence, it must have an <code>author</code> and sometimes it may have more than one. We could represent a blog’s authors as a list or vector and then write a test to ensure it’s never empty. Maybe downstream code has to occasionally check the list of authors is never empty and if it is handles it appropriately. Maybe we panic, maybe we throw, whatever we do we have to handle it - and our tests have to care about this. We might even represent authors as a newtype <code>Authors(Vec&lt;Author&gt;)</code> with a smart-constructor that checks at construction-time that the list is not empty - now we only ever have to check in one place the list is not empty. This is good encapsulation and we only need write one set of tests to check the invariant.</p>
<p>But we are still writing tests. And the tests we are writing are really a reflection of the fact we haven’t properly encoded the invariant in the <em>type</em>-system. Fortunately, in this instance, we can leverage the power of types. Instead of a list of <code>author</code> or a newtype-wrapper around that list, we can encode the fact that the list cannot be empty into the <em>type</em> itself. We might call this the <code>Nonempty</code> type, and here’s one way we can define and construct it in Haskell and Rust:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Nonempty</span> a <span class="ot">=</span> <span class="dt">Nonempty</span> {</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="ot">  head ::</span> a,</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="ot">  rest ::</span> [a]</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<div class="sourceCode" id="cb2"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> Nonempty<span class="op">&lt;</span>T<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>  head<span class="op">:</span> T<span class="op">,</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>  rest<span class="op">:</span> <span class="dt">Vec</span><span class="op">&lt;</span>T<span class="op">&gt;</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>In order to create a <code>Nonempty Author</code> we literally cannot give it an empty list, as encoded in the very definition of this type is the fact we must pass an instantiated value to <code>head</code>. By virtue of this fact, we have proven that we do not (and cannot) have an empty list of authors. Even if we pass an empty list to <code>rest</code>, refusing to supply a value to <code>head</code> will produce a type-error.</p>
<p>Any tests here are not only redundant, they are impossible to construct in the first place - impossible because it really is impossible to create the negative, red test-case. And these are the best kinds of tests, in my view. You can’t write them because the thing to test is impossible to construct. Now, to be clear this is not to say we don’t need tests. Not everything can be encoded in this way. But I would argue the default way to program should not be to write tests, but to write code that doesn’t need tests, safe in the knowledge we can always fallback to tests if our type-system is not expressive enough.</p>]]></description>
    <pubDate>true</pubDate>
    <guid>https://chrisgrounds.github.io/2026/07/18/impossible-tests.html</guid>
    <dc:creator>Chris Grounds</dc:creator>
</item>
<item>
    <title>Why the Funny Names?</title>
    <link>https://chrisgrounds.github.io/2025/08/13/why-the-funny-names.html</link>
    <description><![CDATA[<p>Monads, monoids, semigroups, functors; functional programmers love to make use of this type of vocabulary to describe software, and at first glance it can seem intimidating and abstruse. Why do we need to know, as the common joke goes, that a monad is just a monoid in the category of endofunctors? And what value is category theory and related mathematics to the field of software engineering anyway? An occasional complaint I’ve seen is that this type of language is too difficult and obscure to learn and we should just stick to more “ordinary” names and concepts.</p>
<p>I think there are a couple different things to say to this challenge. Firstly, it should be noted that what we consider ordinary is really just a historical accident, and it could well have been that the programming and software engineering community had settled on categorical concepts from the very beginning. In which case, junior software engineers would learn from the start of their careers what it means to compose two monads together via a monad transformer. Instead the industry went down a different avenue and things like monads appear to be big and scary concepts.</p>
<p>Secondly, I would agree that these concepts can be challenging to learn. But generally the best things are. And moreover the reason for the challenge is that these concepts are universal. They apply across all languages (in fact, they apply across all of mathematics, but that’s not of too much concern for the programmer interested in building composable software). For example, a functor in Haskell is a functor in Java is a functor in Javascript is a functor in Go, and on and on. These concepts cross all language barriers, and yes they can be daunting at first, but they are learn-once and apply everywhere concepts. What does Haskell’s »=, Rust’s and_then, Scala’s flatMap, and Javascript’s then have in common? They might seem completely unrelated, but they are essentially the same function: they all compose monads. Once you’re familiar with these concepts you can jump from language to language like jumping from car to car once you’ve learnt how to drive.</p>
<p>Thirdly, and related to the second point these concepts provide a shared-basis for mutual understanding. You’ll notice that each language I listed above has different names for the same idea. Rather than re-invent new words every time we create a new language I’d think it much better to reuse shared concepts: and there really is no better language to use for shared concepts than mathematics. And category theory is the language of composition, and is really by extension the language of software engineering (a field so intimately concerned about how to compose). Of those questioning the use of categorical language and vocabulary I would really have to ask: why would we not? As a domain driven designer would say, our domain is software itself and our ubiquitous language is the language of mathematics.</p>
<p>Fourthly, another benefit of using these mathematical terms and concepts is that they often have a long history and etymology, which can be helpful in aiding and deepening understanding. The word “algebra” comes from the Arabic root “al-jabr” which means something like “to reunite/reunion.” So an algebra is something which reunites things. Similarly “catamorphism” comes from the Greek “kata” meaning something like “downwards” and “morph” meaning “form/shape”, i.e. “down + shape”. The root “kata” here is the one found in English words like “catastrophe”. So a catamorphism is what most languages call the reduce or fold functions, as they are taking a structure or shape and turning it into a single (downward movement) value. The reverse of a catamorphism is called an “anamorphism.” The root here is “ana” meaning “upward”. So the inverse of a catamorphism takes a single value and turns it upward into a shape/form, or unfold. Putting these three concepts together we’ve just rediscovered what are called “recursion schemes.” A catamorphism takes an algebra (a definition of how to reunite things) and performs the downward movement to a single form, and an anamorphism does the inverse. Of course, etymology is not enough by far and these concepts can take a bit of work to understand. But the trade-off is that like learning to ride a bike once we know them we know them and can apply them to all languages.</p>
<p>So all in all I think there are very good reasons for using the funny names that functional programmers are so fond of. We shouldn’t reinvent the linguistic wheel but should build upon the solid foundations we find at our disposal. And let’s not forget that naming is a very hard problem - a problem that is solved in this instance.</p>]]></description>
    <pubDate>true</pubDate>
    <guid>https://chrisgrounds.github.io/2025/08/13/why-the-funny-names.html</guid>
    <dc:creator>Chris Grounds</dc:creator>
</item>
<item>
    <title>Coding without implementation</title>
    <link>https://chrisgrounds.github.io/2025/05/31/coding-without-implementation.html</link>
    <description><![CDATA[<p>Ever since discovering the joy that is Haskell I have wondered how long it would be until we, as programmers and software engineers, would do away with hand-coded implementations. The Haskell type-system and general language design made it apparent to me that implementing functions by hand would one day become a relic of a bygone era. Instead, we would just think about <em>types</em>. Well, perhaps anyway. I think we’ve seen a glimpse of this future with the development of LLMs, where AI can generate most basic and trivial function definitions. However, I feel that most people are not taking advantage of the power here because they are using the wrong languages and type-systems.</p>
<p>Let me give an example.</p>
<h2 id="hey-google-what-is-this-function">Hey Google, what is this function?<a href="#hey-google-what-is-this-function" class="heading-anchor">#</a></h2>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">forall</span> a<span class="op">.</span> a <span class="ot">-&gt;</span> a</span></code></pre></div>
<p>Let’s ask gpt4.5 what function could instantiate this type,</p>
<pre><code>id :: a -&gt; a
id x = x

Because of its polymorphic type (forall a.), it cannot depend on any specifics 
of the argument—meaning it literally cannot do anything else other than 
return the argument itself.</code></pre>
<p>The fact that the identity function is polymorphic is actually highly-constraining on it. Now what about:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">forall</span> a b<span class="op">.</span> a <span class="ot">-&gt;</span> b <span class="ot">-&gt;</span> a</span></code></pre></div>
<p>Once again the LLM comes to our rescue with the correct answer:</p>
<pre><code>This function is known as const. Its definition is:

const :: a -&gt; b -&gt; a
const x _ = x

It takes two arguments of potentially different types, 
and returns the first one, completely ignoring the second.</code></pre>
<p>So here we have given our LLM two type signatures and asked it to <em>derive</em> the implementation. That’s right - the implementations here are derived. In fact there is a long research history here going back to at least 1989 (and no doubt much before) looking at how we can derive implementation <em>and behaviour</em> from types.[1]</p>
<p>These, you might think, are not useful examples of “real” software problems. I would disagree but here’s another more concrete example: what function implementation satisfies:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="dt">User</span> <span class="ot">-&gt;</span> <span class="dt">UserId</span></span></code></pre></div>
<p>Who knows? And to be honest, who cares? We are done thinking about implementations here, let’s delegate that job to the LLM:</p>
<pre><code>If we define:

data User = User { userId :: UserId, ... }

then the function type:

User -&gt; UserId

effectively constrains the implementation strongly enough that
it&#39;s essentially uniquely determined by the type, provided we 
assume no external context or computations:

getUserId :: User -&gt; UserId
getUserId = userId</code></pre>
<p>Perfect, now without turning this - what should be - short blog post into a deep lesson on advanced type-system design, let me conclude the lesson that I think we should take away. My view here is that there are two things going on: (1) as programmers and engineers we want to think about implementation as little as possible and instead elevate our thinking to the type level, and (b) LLMs are a powerful automation tool to write code. Combining these two points, I think, we should aiming to move as much of our thinking to the type-level as possible and then derive as much implementation as possible computationally (either through theorem-provers and the like or LLMs where we can’t rely on more reliable automatic systems).</p>
<h3 id="references">References<a href="#references" class="heading-anchor">#</a></h3>
<p>[1] https://people.mpi-sws.org/~dreyer/tor/papers/wadler.pdf</p>]]></description>
    <pubDate>true</pubDate>
    <guid>https://chrisgrounds.github.io/2025/05/31/coding-without-implementation.html</guid>
    <dc:creator>Chris Grounds</dc:creator>
</item>
<item>
    <title>Two Types of TDD</title>
    <link>https://chrisgrounds.github.io/2025/04/26/two-types-of-tdd.html</link>
    <description><![CDATA[<p>Test Driven Development has gotten a large amount of attention over the years, probably too much attention. I think there is a better way of developing software, and it funnily enough has the same acronym. Let’s look at both types of software-development practice.</p>
<h2 id="tdd">TDD<a href="#tdd" class="heading-anchor">#</a></h2>
<p>The flow of Test Driven Development is very well understood at this point. You start by writing the most basic test you can, which will of course fail because you’ve written no code yet. Then you write the most basic code you can to pass the test. You continue adding tests and writing code to pass them until your requirements are satisfied.</p>
<p>This works well as a design method because it forces you to think about your code’s API and how other people will interface with it, and it naturally leads to a full test suite that gives you confidence in your code. But it’s important to remember that the core pillar of TDD is that it is a design-tool.</p>
<h2 id="the-second-tdd-aka-tydd">The second TDD, aka TyDD<a href="#the-second-tdd-aka-tydd" class="heading-anchor">#</a></h2>
<p>The second type of TDD is Type Driven Development, which I will abbreviate as “TyDD”. The flow here is similar to TDD in some respects and different in others. You start by defining types to represent your domain model. Similar to Domain Driven Design, this is where you need to sit down with the domain expert and gain a thorough understanding of the domain. You’ll generally represent the domain through Algebraic Data Types (Sum, Product, Union, Discriminated Union, Record, etc) and define any appropriate categorical concepts (<em>this</em> thing is a functor, or <em>that</em> thing is a monoid, etc). As you go through this collaborative process all you are doing is defining the core data types of your application.</p>
<p>In one of the earliest articles on TyDD I can find, Tomas Petricek<sup>[1]</sup> noted after doing this for a real example,</p>
<blockquote>
<p>What have we achieved so far? We described the domain model for the Journey log application with less than 20 lines of code. When writing the code for the first time, I typed a few type declarations, explained them to my friend and he gave me feedback saying what was wrong with my first design. This means that the process was quite collaborative and iterative.</p>
</blockquote>
<p>The next step is to translate your business logic into functions that only have type-signatures. You won’t implement the functions, you will simply assert through the type system that they take and return the expected data types.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ot">calculateEbitda ::</span> <span class="dt">IncomeStream</span> <span class="ot">-&gt;</span> <span class="dt">ExpensesStream</span> <span class="ot">-&gt;</span> <span class="dt">Ebitda</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>calculateEbitda <span class="ot">=</span> <span class="fu">undefined</span></span></code></pre></div>
<p>Then once the code is compiling you can begin to implement your functions, adding tests where appropriate. In fact, at this stage you can even go through TDD’s <em>Red-Green-Refactor</em> flow for each function. The only difference is that you are not <em>designing</em> your code through the tests because it has already been designed. But there is still much value in the <em>Red-Green-Refactor</em> flow. Providing the code continues to compile and any tests you add pass, you should have very high confidence that the code will do what you expect and that you have met your user requirements and correctly modelled your domain.</p>
<p>Of course, throughout the development process and after you will constantly be asking of each other - <em>is this the right kind of abstraction</em>, <em>do we really need this to be a monad?</em>, etc. TyDD is and should be incredibly collaborative.</p>
<h3 id="types-are-tests">Types are tests<a href="#types-are-tests" class="heading-anchor">#</a></h3>
<p>On the subject of tests, you can think of types as being exhaustive test-cases, or of tests as being specific instances of types. Generally, more type-heavy language communities like to focus more of their attention to property-based testing than less-expressive languages. This is because property-based testing lets us focus on testing properties rather than specific examples, which should be handled by the type-system as much as possible.</p>
<h2 id="appendix">Appendix<a href="#appendix" class="heading-anchor">#</a></h2>
<p>[1] https://tomasp.net/blog/type-first-development.aspx</p>]]></description>
    <pubDate>true</pubDate>
    <guid>https://chrisgrounds.github.io/2025/04/26/two-types-of-tdd.html</guid>
    <dc:creator>Chris Grounds</dc:creator>
</item>
<item>
    <title>A Steelman Anti-TDD Argument</title>
    <link>https://chrisgrounds.github.io/2025/04/24/steelman-anti-tdd.html</link>
    <description><![CDATA[<p>I’ve seen lots of arguments, good and bad, for and against Test Driven Development. And this topic has recently come up again on LinkedIn after Jason Gorman<sup>[1]</sup> brought up a 2015 study<sup>[2]</sup> showing only 1.7% of developers actually practice TDD (I’m not surprised based on my anecdotal experience). In any case, both sides of the TDD divide love a good strawman. So I want here to present the best argument I can think of against TDD. And I do this as someone who loves writing tests, and thinks there is much value in a test-first approach to testing.</p>
<h2 id="the-argument">The Argument<a href="#the-argument" class="heading-anchor">#</a></h2>
<h3 id="the-short-version">The Short Version<a href="#the-short-version" class="heading-anchor">#</a></h3>
<p>The short version of the argument goes like this: the promise of TDD is better software design and safer (bug-free) software. Both of these things are better achieved through Type-Driven-Development. Does this mean we shouldn’t practice TDD? To the extent we want these things, yes, we should instead practice TyDD.</p>
<h3 id="the-long-version">The Long Version<a href="#the-long-version" class="heading-anchor">#</a></h3>
<p>To begin with let’s think about what the purpose of a test is. There are various reasons to write tests but one of the main pillars as far as TDD is concerned is to drive the design of software. I think as far as it drives good design, this is a very good and powerful reason to use TDD. Now another pillar is that the test-first approach leads to good bug-free software that meets user requirements. It’s along this axis the steelman argument will proceed.</p>
<p>For take what a test is: a single execution environment that verifies behaviour non-exhaustively. This point is completely banal - you write one test, then another, then another, until you are satisfied you have captured the intent of the code. Writing numerous iterations of similar tests is required because single tests do not exhaustively verify behaviour. A test that checks that your function adds up two monetary amounts correctly will only ever check it for, let’s say, two integers. Of course, this is where the red-green-refactor cycle comes in, and where TDD proponents will say you should write more tests. But unless your range of possible inputs is practically enumerable in some way, you will never be able to write enough tests. There will always be potential edge cases. If you wanted guarantees about your function, which takes two i64s and returns a new i64, for all cases then you would need to write 3.4 * 10^<sup>38</sup> unit tests.</p>
<p>Now you might say, we will limit the range of inputs or constrain our inputs in some way, so that we don’t need to worry about enumerating a large number of tests to guarantee correctness. And here is the crux of the steelman argument: in doing this, you are already domain modelling and you are in fact removing both pillars in support of TDD. By constraining the range of inputs for your function under test, you are both modelling your domain via the type-system and guaranteeing correctness via the type-system. In fact, you are doing TDD - it’s just that the “T” stands for “Type” not “Test”.</p>
<p>So in order to guarantee correctness and bug-free software, you can’t use TDD - instead you must use the type-system. But in doing so, you demolish the first pillar of TDD: i.e. you will design and model your code through the type-system, not the tests.</p>
<p>I think this points to an interesting observation. In languages with weak type-systems, where it is harder to model and guarantee correctness, you must use more and more tests (but never achieve full enumeration of all test cases). This tells us that tests are actually a representation of language weakness. The more tests you need, the weaker your language is in terms of its expressiveness and correctness.</p>
<h2 id="appendix">Appendix<a href="#appendix" class="heading-anchor">#</a></h2>
<p>[1] https://www.linkedin.com/posts/jasongorman_a-large-scale-study-of-dev-activity-in-the-activity-7321034693926137856-XiZc?utm_source=share&amp;utm_medium=member_desktop&amp;rcm=ACoAAB3lsLkBm67ca1ExADEE8ZduzUtGgEi_xak</p>
<p>[2] https://gousios.org/pub/developer-testing-in-IDE.pdf</p>]]></description>
    <pubDate>true</pubDate>
    <guid>https://chrisgrounds.github.io/2025/04/24/steelman-anti-tdd.html</guid>
    <dc:creator>Chris Grounds</dc:creator>
</item>
<item>
    <title>Domain Modelling with Category Theory</title>
    <link>https://chrisgrounds.github.io/2024/10/01/domain-modelling-with-category-theory.html</link>
    <description><![CDATA[<p>Gandalf finds himself sitting at his computer one rainy Wednesday and in a moment of procrastination loads up his favourite online staff shop. He finds the latest newly released staff, that all his wizarding friends have been raving about, and loads the product page. His mouse tantalizingly hovers over the “add to basket” button until his impulsiveness gets the better of him and… he clicks.</p>
<p>Gandalf doesn’t need a new staff. His old one is more than up to the task, having helped defeat <spoiler>. And as fun as this story is you’re a software engineer, and you’d much rather know how to model this user journey than find out whether Gandalf went through and bought the new staff.</p>
<p>So how can we model this process and how can we use Category Theory to help?</p>
<p>In OO-land, we might create a Basket class along with an addItem(Item item) method. But we can think about this interaction differently. Rather than thinking about the user adding an item to a basket, we can think about the user adding a basket to a basket. Let’s forget about items for the minute, and just think about how this would work if we constrain ourselves to baskets?</p>
<p>To begin with, the user adds a basket containing something to an already existing basket. This already existing basket must be empty because it’s the first item to be added (so this first basket functions as a no-op). Then any additional items are additional baskets being added to the previous basket.</p>
<p>Another crucial feature of our basket model is that the order in which baskets are added together doesn’t matter. That is, if I add my wine_basket with my cheese_basket, and add that basket with my bread_basket, then that’s going to give me the same final basket as adding my cheese_basket with my bread_basket, and then adding that to my wine_basket. In the end I end up with wine, cheese, and bread. In other words, our baskets are associative.</p>
<p>(wine + cheese) + bread = wine + (cheese + bread)
In the language of category theory, our basket model forms a semigroup as we‘ve defined a binary associative operator on it, and it forms a monoid because additionally we’ve given it an identity value — the empty basket. Let’s call our binary associative operator “append” and we can see how we might think about semigroups and monoids pictorially.</p>
<p>A categorical image of semigroups and monoids, expressing the arrows with their relevant type-signature. You can read “::” as “has type”.
A common example of a semigroup is the positive natural numbers (1..) where the binary associative operator is addition, and an example of a monoid is the natural numbers (0..) where the identity value is 0. Naturally, 0 is the identity value as n + 0 = n. Incidentally, in OOP, we can think of the Builder pattern as a monoid. The initial Builder construction is the identity value, and withX methods are binary associative operations from Builder to Builder.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co">// identity value</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>new CarBuilder()</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>  <span class="co">// binary associative function withWheels</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>  <span class="co">// takes a CarBuilder and returns a CarBuilder</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>  <span class="op">.</span>withHeadlights()</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>  <span class="op">.</span>withEngine()</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>  <span class="op">.</span>withWheels(<span class="dv">4</span>)</span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>  <span class="op">.</span>build()<span class="op">;</span></span></code></pre></div>
<p>In Rust, we can define two traits, Semigroup and Monoid, to represent these categorical concepts. Semigroup contains a binary function, which we’ve called sappend to differentiate it from the monoidal definition. Monoid contains mappend (essentially Semigroup’s sappend) and an mempty value to represent identity. We give the functions such strange names because append is generally used in languages for List-type structures (“sappend”: semigroup-append, and “mappend”: monoid-append), and we call the identity value mempty because it generally represents an empty variant of a type and id would either be confusing or reserved.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">trait</span> Semigroup <span class="op">{</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">fn</span> sappend(<span class="kw">self</span><span class="op">,</span> b<span class="op">:</span> <span class="dt">Self</span>) <span class="op">-&gt;</span> <span class="dt">Self</span><span class="op">;</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="kw">trait</span> Monoid<span class="op">:</span> Semigroup <span class="op">{</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>    <span class="kw">fn</span> mempty() <span class="op">-&gt;</span> <span class="dt">Self</span><span class="op">;</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>    <span class="kw">fn</span> mappend(<span class="kw">self</span><span class="op">,</span> b<span class="op">:</span> <span class="dt">Self</span>) <span class="op">-&gt;</span> <span class="dt">Self</span><span class="op">;</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>Let’s define our Basket type. And for the sake of brevity, we’ll just assume Bucket stores items in a Vector and we won’t care about the Item type itself.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> Basket <span class="op">{</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>    items<span class="op">:</span> <span class="dt">Vec</span><span class="op">&lt;</span>Item<span class="op">&gt;</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>With this, we can define how we add two Basket’s together,</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">impl</span> Semigroup <span class="cf">for</span> Basket <span class="op">{</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">fn</span> sappend(<span class="kw">self</span><span class="op">,</span> b<span class="op">:</span> Basket) <span class="op">-&gt;</span> Basket <span class="op">{</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>        Basket <span class="op">{</span> items<span class="op">:</span> <span class="kw">self</span><span class="op">.</span>items<span class="op">.</span>into_iter()<span class="op">.</span>chain(b<span class="op">.</span>items)<span class="op">.</span>collect() <span class="op">}</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>And to define our Basket as a monoid, we can piggyback on our Semigroup definition and simply provide an identity value,</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">impl</span> Monoid <span class="cf">for</span> Basket <span class="op">{</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">fn</span> mempty() <span class="op">-&gt;</span> Basket <span class="op">{</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>        Basket <span class="op">{</span> items<span class="op">:</span> <span class="pp">vec!</span>[] <span class="op">}</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>    </span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>    <span class="kw">fn</span> mappend(<span class="kw">self</span><span class="op">,</span> b<span class="op">:</span> Basket) <span class="op">-&gt;</span> Basket <span class="op">{</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>        <span class="kw">self</span><span class="op">.</span>sappend(b)</span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a>With this <span class="kw">in</span> place we can construct our basket by first creating an empty basket and then adding additional baskets<span class="op">.</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a>```rust</span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> basket1 <span class="op">=</span> Basket <span class="op">{</span> items<span class="op">:</span> <span class="pp">vec!</span>[Item(<span class="st">&quot;apple&quot;</span><span class="op">.</span>to_string())] <span class="op">};</span></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> basket2 <span class="op">=</span> Basket <span class="op">{</span> items<span class="op">:</span> <span class="pp">vec!</span>[Item(<span class="st">&quot;orange&quot;</span><span class="op">.</span>to_string())] <span class="op">};</span></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a><span class="pp">Basket::</span>mempty()</span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a>  <span class="op">.</span>mappend(basket1)</span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a>  <span class="op">.</span>mappend(basket2)<span class="op">;</span></span>
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-20"><a href="#cb5-20" aria-hidden="true" tabindex="-1"></a><span class="co">// Result: Basket { items: [Item(&quot;apple&quot;), Item(&quot;orange&quot;)] }</span></span></code></pre></div>
<p>Now whenever the user clicks “add to basket”, we simply mappend another basket to the current basket.</p>
<p>Let’s extend the requirements here: suppose we want the user to be able to see a history of their basket and revert to a certain point. This is actually fairly simple with our monoidal structure.</p>
<p>But first, let’s consider our OO-Basket class. In order to go back in time we’d have to create a removeItem method, keep track of when addItem was called, with what, and with how much, and then call removeItem on any items that were added after a certain point. This would work, but it feels messy.</p>
<p>With the monoidal approach, all we need is to keep track of what Basket’s existed and when. Once the user selects a time to revert their basket to, the monoidal nature of the Basket type means we simply append all the previous versions together — and because of the associative nature of monoids, the order of application doesn’t matter. (And this works because fundamentally adding things to a basket is associative — the order of adding things doesn’t matter. You can try this out in a supermarket!)</p>
<p>So if we store our Baskets in a Vector or List type along with a timestamp (representing when they were created), we can fold over the Vec/List (itself a monoid) and rebuild our Basket to the desired point in time. To do this we filter our stored baskets to include only those before our desired time and then fold over the result, calling mappend on each iteration to build up our new Basket. The initial value is the identity value (Basket::mempty) so if there is nothing to fold over, we always have at least an empty Basket.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> BasketHistory <span class="op">{</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>    saved_states<span class="op">:</span> <span class="dt">Vec</span><span class="op">&lt;</span>(SystemTime<span class="op">,</span> Basket)<span class="op">&gt;</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a><span class="kw">impl</span> BasketHistory <span class="op">{</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>    <span class="kw">fn</span> recover_to_time(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> target_time<span class="op">:</span> SystemTime) <span class="op">-&gt;</span> Basket <span class="op">{</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>        <span class="kw">self</span><span class="op">.</span>saved_states</span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>            <span class="op">.</span>iter()</span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a>            <span class="op">.</span>filter(<span class="op">|</span>(time<span class="op">,</span> _)<span class="op">|</span> time <span class="op">&lt;=</span> <span class="op">&amp;</span>target_time)</span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a>            <span class="op">.</span>map(<span class="op">|</span>(_<span class="op">,</span> basket)<span class="op">|</span> basket<span class="op">.</span>clone())</span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a>            <span class="op">.</span>fold(<span class="pp">Basket::</span>mempty()<span class="op">,</span> <span class="op">|</span>acc<span class="op">,</span> basket<span class="op">|</span> acc<span class="op">.</span>mappend(basket))</span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>This suddenly feels very much like a lightweight version of event-sourcing, which we’re almost getting for free. The difference is we’re not explicitly creating events and we don’t care about processing sequentially. This all comes down to the fact our core computation is a binary associative operator, which gives us a branching structure. (Incidentally, this branching structure — think of how (1+(2+(3+4))+(4+(3+(2+1)))) is also branching — actually gives us thread-safe code that could be parallelized.)</p>
<p>Before going down too much of a rabbit-hole, let’s take stock. With just two concepts from category theory, we’ve been able to rigorously and iteratively model a very common process. We’ve done this just by thinking about how we compose base elements together in a way that respects relatively straightforward rules (namely, associativity and identity). This is just touching on the surface of what category theory has to offer us when it comes to domain modelling and building robust software. But for now, I think Gandalf would be happy knowing his Basket is a proper monoidal object.</p>]]></description>
    <pubDate>true</pubDate>
    <guid>https://chrisgrounds.github.io/2024/10/01/domain-modelling-with-category-theory.html</guid>
    <dc:creator>Chris Grounds</dc:creator>
</item>

    </channel>
</rss>
