<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Oneiro]]></title><description><![CDATA[Oneiro]]></description><link>https://blog.oneiro.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1757518177596/628487ca-9576-4104-9e60-c8bf58f6cae8.png</url><title>Oneiro</title><link>https://blog.oneiro.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Fri, 10 Apr 2026 14:41:52 GMT</lastBuildDate><atom:link href="https://blog.oneiro.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Git: Banishing Stacked Pull Request Demons]]></title><description><![CDATA[A software engineer's guide to sane feature layering
As software engineers, we often find ourselves in a productivity paradox. You finish a solid chunk of work and ship it off for review. But the review cycle is a slow-moving beast, and you can’t jus...]]></description><link>https://blog.oneiro.dev/git-banishing-stacked-pull-request-demons</link><guid isPermaLink="true">https://blog.oneiro.dev/git-banishing-stacked-pull-request-demons</guid><category><![CDATA[stacked pull requests]]></category><category><![CDATA[stacked diffs]]></category><category><![CDATA[Git]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[Pull Requests]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[software development]]></category><dc:creator><![CDATA[Mark Pro]]></dc:creator><pubDate>Sat, 07 Feb 2026 03:34:00 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-a-software-engineers-guide-to-sane-feature-layering">A software engineer's guide to sane feature layering</h2>
<p>As software engineers, we often find ourselves in a productivity paradox. You finish a solid chunk of work and ship it off for review. But the review cycle is a slow-moving beast, and you can’t just sit on your hands. So, you do the logical thing: you start the next task, which is dependent on the first. You’ve just started a "stack."</p>
<p>Everything is fine until the dreaded PR comment arrives. A suggestion is made to the underlying foundation of your work. You address it, and suddenly... <strong>BAM.</strong> Conflicts.</p>
<p>Instead of a clean workflow, you’re forced into a series of dark Git incantations to summon the delta of commits up from the seventh circle of Git hell. You <code>git switch</code>, <code>git commit --amend</code>, and then attempt the surgical <code>git rebase --onto</code> to transmute your dependent <code>second-branch</code> into a proper, elegant human. If you're lucky, it works. If you're not, you’re left with a mutant homunculus born of unwise rebase choices and manual conflict resolution.</p>
<p>There is an easier way for us mere mortals. If your organization is savvy enough to use GitHub, a dedicated tool called <strong>Aviator (av)</strong> can handle the heavy lifting of stacked diffs for you. It is open-source, free to use, and turns those complex rebases into a simple, automated process.</p>
<h3 id="heading-the-new-incantations-a-technical-breakdown">The New Incantations: A Technical Breakdown</h3>
<p>To replace the manual "surgery" with automation, <code>av</code> uses a hybrid approach: you use standard Git for your logic and <code>av</code> for the structural "plumbing."</p>
<h4 id="heading-1-initialize-the-altar">1. Initialize the Altar</h4>
<p>Before you start stacking, initialize your repository. This connects the CLI to your GitHub metadata (using your existing <code>gh</code> credentials).</p>
<pre><code class="lang-bash">av init
</code></pre>
<h4 id="heading-2-laying-the-foundation">2. Laying the Foundation</h4>
<p>Instead of <code>git checkout -b</code>, use <code>av branch</code>. This "tags" the branch in the tool's metadata so it knows exactly who the parent is.</p>
<pre><code class="lang-bash">av branch first-branch
<span class="hljs-comment"># ... make changes ...</span>
git commit -m <span class="hljs-string">"Add API layer"</span>
</code></pre>
<h4 id="heading-3-building-the-tower">3. Building the Tower</h4>
<p>Stay on your current branch and create the next layer. <code>av</code> automatically recognizes the current branch as the parent.</p>
<pre><code class="lang-bash">av branch second-branch
<span class="hljs-comment"># ... make changes ...</span>
git commit -m <span class="hljs-string">"Add UI component"</span>
</code></pre>
<h4 id="heading-4-the-ritual-of-synchronization">4. The Ritual of Synchronization</h4>
<p>When a PR comment forces you to change <code>first-branch</code>:</p>
<ol>
<li><p><strong>Switch:</strong> <code>git checkout first-branch</code></p>
</li>
<li><p><strong>Fix:</strong> Address the comments and <code>git commit</code> (or <code>amend</code>).</p>
</li>
<li><p><strong>The Cleanse:</strong> Run the synchronization command:</p>
</li>
</ol>
<pre><code class="lang-bash">av sync
</code></pre>
<p><code>av sync</code> identifies that the foundation has moved, calculates the new delta, and automatically rebases your dependent <code>second-branch</code> onto the new head of its parent.</p>
<h4 id="heading-5-ascending-to-github">5. Ascending to GitHub</h4>
<p>To push your work without manually managing "Base Branch" settings in the GitHub UI:</p>
<pre><code class="lang-bash">av pr
</code></pre>
<p>This pushes your entire stack and tells GitHub exactly which branch each PR depends on. Reviewers only see the incremental diff for each layer, keeping reviews fast and focused.</p>
<h3 id="heading-the-verdict">The Verdict</h3>
<p>I’ll be integrating this into my workflow over the next few weeks to see how it holds up against the chaos of real-world remote dev life.</p>
<p>Check out the <a target="_blank" href="https://docs.aviator.co/aviator-cli">Aviator CLI Documentation</a> to try it yourself.</p>
]]></content:encoded></item><item><title><![CDATA[Local State Management w/ Quiddity]]></title><description><![CDATA[This post explores an alternative to reducers in React to manage non-trivial local state though the use of Quiddiny: a local first state management package.
Choosing Local State Intentionally
In React, there are many state management libraries availa...]]></description><link>https://blog.oneiro.dev/quiddity-local-state-management</link><guid isPermaLink="true">https://blog.oneiro.dev/quiddity-local-state-management</guid><category><![CDATA[local state management]]></category><category><![CDATA[Local state]]></category><category><![CDATA[React]]></category><category><![CDATA[State Management ]]></category><category><![CDATA[Redux]]></category><category><![CDATA[Reducers ]]></category><category><![CDATA[reducer]]></category><category><![CDATA[useState]]></category><category><![CDATA[useReducer]]></category><category><![CDATA[zustand]]></category><category><![CDATA[Jotai]]></category><dc:creator><![CDATA[Mark Pro]]></dc:creator><pubDate>Mon, 05 Jan 2026 04:51:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767588233713/19d4b3fb-aba1-456e-8de3-a317868eeed2.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>This post explores an alternative to reducers in React to manage non-trivial local state though the use of <a target="_blank" href="https://github.com/oneirosoft/quiddity">Quiddiny: a local first state management package</a>.</em></p>
<h2 id="heading-choosing-local-state-intentionally">Choosing Local State Intentionally</h2>
<p>In React, there are many state management libraries available for global state. However, it is surprisingly difficult to find a good solution for managing <em>local</em> state. You might ask why one would want, or even prefer, local state over global state. The answer is fairly simple: global state can become messy and may introduce unintended side effects, since many components can end up coupled to the same shared state.</p>
<p>With local state, changes are isolated to the component where the state is used. This isolation improves testability and makes components easier to reason about and debug.</p>
<p>For simple cases, the <code>useState</code> hook works well enough, and basic derived state can be handled with <code>useMemo</code>. However, as local state grows in size or complexity, useState becomes less appealing. You end up tracking multiple state values along with how and where each one is updated. React does provide a built-in solution for this in the form of <code>useReducer</code>, which handles more complex local state well. That said, useReducer requires some upfront boilerplate, and its action-based approach can feel clunky in practice.</p>
<h2 id="heading-scaling-local-state-with-usestate">Scaling Local State with <code>useState</code></h2>
<p>Lets take the simple example using a counter to represent the use of local state while exploring how <code>useState</code> scales over time.</p>
<p>The <code>useState</code> hook works well for simple local state and keeps components easy to reason about. This example starts small, but it provides a useful foundation for seeing how local state can become harder to manage as additional behavior and derived values are introduced.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { useState, useMemo, useRef } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>

<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>)
    <span class="hljs-keyword">const</span> doubledCount = useMemo(<span class="hljs-function">() =&gt;</span> count * <span class="hljs-number">2</span>, [count])

    <span class="hljs-keyword">const</span> increase = <span class="hljs-function">() =&gt;</span>
        setCount(<span class="hljs-function"><span class="hljs-params">count</span> =&gt;</span> count + <span class="hljs-number">1</span>)

    <span class="hljs-keyword">const</span> multiplyBy = <span class="hljs-function">(<span class="hljs-params">x: number</span>) =&gt;</span>
        setCount(<span class="hljs-function"><span class="hljs-params">count</span> =&gt;</span> count * x)

    <span class="hljs-keyword">const</span> multiplyRef = useRef&lt;HTMLInputElement&gt;(<span class="hljs-literal">null</span>)

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Count is: {count}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Doubled count is: {doubledCount}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{increase}</span>&gt;</span>Increase count<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                multiply by:
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">{multiplyRef}</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"number"</span> /&gt;</span>

                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                    <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span>
                        multiplyRef.current &amp;&amp;
                        multiplyBy(Number(multiplyRef.current.value))
                    }
                &gt;
                    Apply Multiplication
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    )
}
</code></pre>
<p>The above example is a simple one that represents a single state of <code>count</code> which has a derived state of <code>doubledCount</code> and two actions <code>increase</code> and <code>multiplyBy</code>. Even adding one more state variable we increase the complexity and thus the state, memos, and callbacks grow. One solution is to address this with a reducer.</p>
<h2 id="heading-centralization-with-reducers">Centralization With Reducers</h2>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { useMemo, useReducer, useRef } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>

type State = {
    <span class="hljs-attr">count</span>: number
}

type Action =
    | { <span class="hljs-attr">type</span>: <span class="hljs-string">"increase"</span> }
    | { <span class="hljs-attr">type</span>: <span class="hljs-string">"multiply"</span>; factor: number }

<span class="hljs-keyword">const</span> initialState: State = {
    <span class="hljs-attr">count</span>: <span class="hljs-number">0</span>
}

<span class="hljs-keyword">const</span> reducer = (state: State, <span class="hljs-attr">action</span>: Action): <span class="hljs-function"><span class="hljs-params">State</span> =&gt;</span> {
    <span class="hljs-keyword">switch</span> (action.type) {
        <span class="hljs-keyword">case</span> <span class="hljs-string">"increase"</span>:
            <span class="hljs-keyword">return</span> { <span class="hljs-attr">count</span>: state.count + <span class="hljs-number">1</span> }

        <span class="hljs-keyword">case</span> <span class="hljs-string">"multiply"</span>:
            <span class="hljs-keyword">return</span> { <span class="hljs-attr">count</span>: state.count * action.factor }

        <span class="hljs-attr">default</span>:
            <span class="hljs-keyword">return</span> state
    }
}

<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> [state, dispatch] = useReducer(reducer, initialState)
    <span class="hljs-keyword">const</span> doubledCount = useMemo(<span class="hljs-function">() =&gt;</span> state.count * <span class="hljs-number">2</span>, [state.count])
    <span class="hljs-keyword">const</span> multiplyRef = useRef&lt;HTMLInputElement&gt;(<span class="hljs-literal">null</span>)

    <span class="hljs-keyword">const</span> increase = <span class="hljs-function">() =&gt;</span> {
        dispatch({ <span class="hljs-attr">type</span>: <span class="hljs-string">"increase"</span> })
    }

    <span class="hljs-keyword">const</span> multiplyBy = <span class="hljs-function">(<span class="hljs-params">factor: number</span>) =&gt;</span> {
        dispatch({ <span class="hljs-attr">type</span>: <span class="hljs-string">"multiply"</span>, factor })
    }

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Count is: {state.count}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Doubled count is: {doubledCount}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{increase}</span>&gt;</span>
                Increase count
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                multiply by:
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">{multiplyRef}</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"number"</span> /&gt;</span>

                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                    <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span>
                        multiplyRef.current &amp;&amp;
                        multiplyBy(Number(multiplyRef.current.value))
                    }
                &gt;
                    Apply Multiplication
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    )
}
</code></pre>
<p>The reducer here does not provide much value yet, because it requires a fair amount of setup. One pet peeve of mine is that reducer logic often ends up in a <code>switch</code> statement. It works, and it is a common pattern, but it has always felt a bit redundant to me.</p>
<h2 id="heading-quidditys-approach">Quiddity's Approach</h2>
<p>Redux appears to be falling out of favor, with many developers gravitating toward libraries that offer simpler and more expressive state management APIs, such as <strong>Jotai</strong> and <strong>Zustand</strong>. Seeing these projects made me wonder why local state could not benefit from the same level of elegance. That line of thinking ultimately led to <a target="_blank" href="https://github.com/oneirosoft/quiddity"><strong>Quiddity</strong> a simple and lightweight local state management library</a> designed specifically for React components.</p>
<p>Quiddity simplifies local state management by removing much of the ceremony around defining state and by providing helpers that improve TypeScript ergonomics. In this post we will focus on using Quiddity with TypeScript. When using JavaScript, the combine function is not required, since its primary purpose is to improve type inference.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { useRef } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>
<span class="hljs-keyword">import</span> { create, combine } <span class="hljs-keyword">from</span> <span class="hljs-string">"@oneirosoft/quiddity"</span>

<span class="hljs-keyword">const</span> useCounter = create(
    combine({ <span class="hljs-attr">count</span>: <span class="hljs-number">0</span> }, <span class="hljs-function"><span class="hljs-params">set</span> =&gt;</span> ({
        <span class="hljs-attr">increment</span>: <span class="hljs-function">() =&gt;</span>
            set(<span class="hljs-function"><span class="hljs-params">state</span> =&gt;</span> ({ <span class="hljs-attr">count</span>: state.count + <span class="hljs-number">1</span> })),

        <span class="hljs-attr">multiplyBy</span>: <span class="hljs-function">(<span class="hljs-params">factor: number</span>) =&gt;</span>
            set(<span class="hljs-function"><span class="hljs-params">state</span> =&gt;</span> ({ <span class="hljs-attr">count</span>: state.count * factor }))
    })),
    <span class="hljs-function"><span class="hljs-params">state</span> =&gt;</span> ({
        <span class="hljs-attr">doubledCount</span>: state.count * <span class="hljs-number">2</span>
    })
)

<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> { 
        count, 
        doubledCount, 
        increment, 
        multiplyBy 
    } = useCounter()

    <span class="hljs-keyword">const</span> multiplyRef = useRef&lt;HTMLInputElement&gt;(<span class="hljs-literal">null</span>)

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Count is: {count}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Doubled count is: {doubledCount}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{increment}</span>&gt;</span>
                Increase count
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                multiply by:
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">{multiplyRef}</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"number"</span> /&gt;</span>

                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                    <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span>
                        multiplyRef.current &amp;&amp;
                        multiplyBy(Number(multiplyRef.current.value))
                    }
                &gt;
                    Apply Multiplication
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    )
}
</code></pre>
<p>The approach <strong>Quiddity</strong> takes strikes a balance between the simplicity of <code>useState</code> and the structure of <code>useReducer</code>. Like <code>useState</code>, it keeps state local and easy to reason about. Like <code>useReducer</code>, it centralizes state transitions and derived values in one place. The difference is that it does so without introducing action objects, switch statements, or a separate dispatch step.</p>
<p>Compared to <code>useState</code>, this pattern scales more naturally as state grows. Related state updates, derived values, and behaviors live together, rather than being spread across multiple hooks and callbacks. Compared to <code>useReducer</code>, it removes much of the boilerplate and ceremony, allowing state changes to be expressed directly as functions with clear intent.</p>
<p>The result is local state that remains explicit and predictable, while staying lightweight and approachable. For components with non-trivial state, this can make the code easier to read, easier to extend, and easier to reason about over time.</p>
]]></content:encoded></item><item><title><![CDATA[Smooth Operators: Declarative Sequence Transformations in TypeScript]]></title><description><![CDATA[Sequence Operators

In this post, we will refer to these higher-order array methods as sequence operators, since they operate over sequences of values in a declarative way.
You can find the accompanying example code on GitHub: Declarative Sequence Tr...]]></description><link>https://blog.oneiro.dev/smooth-operators-declarative-sequence-transformations-in-typescript</link><guid isPermaLink="true">https://blog.oneiro.dev/smooth-operators-declarative-sequence-transformations-in-typescript</guid><category><![CDATA[array]]></category><category><![CDATA[sequences]]></category><category><![CDATA[Functional Programming]]></category><category><![CDATA[Monad]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[map]]></category><category><![CDATA[filtering]]></category><category><![CDATA[Reduce]]></category><category><![CDATA[transformers]]></category><dc:creator><![CDATA[Mark Pro]]></dc:creator><pubDate>Wed, 24 Dec 2025 02:00:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766541150844/f8d555a5-0449-44b1-a9cd-82a2f3d21b8d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-sequence-operators">Sequence Operators</h1>
<blockquote>
<p>In this post, we will refer to these higher-order array methods as <em>sequence operators</em>, since they operate over sequences of values in a declarative way.</p>
<p>You can find the accompanying example code on GitHub: <a target="_blank" href="https://github.com/oneirosoft/oneiro-blog/tree/main/examples/declarative-sequence-transformations">Declarative Sequence Transformations</a></p>
</blockquote>
<p>A <em>sequence operator</em> is a higher-order function that takes a sequence, like an array or list, applies a function to each element, and returns a new value or sequence, enabling declarative, step-by-step data transformations. Many modern languages provide their own versions of these operators, such as LINQ in C# or the Stream API in Java. In this post, we will focus on several core sequence operators using TypeScript as our example.</p>
<p>Throughout this article, we will make use of a Note type. Assume we already have data coming from some data source, and we will refer to it simply as notes. The aim here is to keep the article abstract and focused on transformations rather than mock data, so you will often see a variable called <code>notes</code> in the examples.</p>
<h2 id="heading-type-reference">Type Reference</h2>
<p><code>note.types.ts</code></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> Note {
    id: <span class="hljs-built_in">string</span>
    title: <span class="hljs-built_in">string</span>
    tags: <span class="hljs-built_in">string</span>[]
    sections: Section[]
}

<span class="hljs-keyword">interface</span> Section {
    heading: <span class="hljs-built_in">string</span>
    links: <span class="hljs-built_in">string</span>[]
    references: <span class="hljs-built_in">string</span>[]
}
</code></pre>
<h2 id="heading-mapt-gt-r"><code>map(T =&gt; R)</code></h2>
<p><code>map</code> transforms each element in a sequence using a function and returns a new sequence of the same length. Each element of the sequence is passed to the mapping function.</p>
<p>We can apply a transformation to each element in the notes sequence to produce cards by extracting data from each note and reshaping it into a new type.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> Card {
    id: <span class="hljs-built_in">string</span>
    title: <span class="hljs-built_in">string</span>
    sectionCount: <span class="hljs-built_in">number</span>
    tagCount: <span class="hljs-built_in">number</span>
}

<span class="hljs-keyword">const</span> cards: Card[] = 
    notes.map&lt;Card&gt;(<span class="hljs-function"><span class="hljs-params">note</span> =&gt;</span> ({
        id: note.id,
        title: note.title,
        sectionCount: note.sections.length,
        tagCount: note.tags.length
    }))
</code></pre>
<p>The result is a new sequence where each Note has been transformed into a <code>Card</code>. The original <code>notes</code> array is left untouched, and a new cards array is produced with the same number of elements, but a different data shape.</p>
<h2 id="heading-filtert-gt-boolean"><code>filter(T =&gt; boolean)</code></h2>
<p><code>filter</code> evaluates each element in a sequence using a predicate function, that is, a function that returns a boolean. <code>filter</code> does not transform items in the list. Instead, it pares down the sequence such that the resulting sequence only contains elements for which the predicate evaluates to <code>true</code>.</p>
<p>We can apply a predicate to each element in the <code>notes</code> sequence to produce a new sequence of only those notes whose tags include <code>'functional'</code>.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> functionalNotes: Note[] = 
    notes.filter(<span class="hljs-function"><span class="hljs-params">note</span> =&gt;</span> note.tags.includes(<span class="hljs-string">'functional'</span>))
</code></pre>
<p>We can also select out any notes that have sections.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> isNotEmpty = &lt;T&gt;<span class="hljs-function">(<span class="hljs-params">arr: T[]</span>) =&gt;</span> arr.length &gt; <span class="hljs-number">0</span>

<span class="hljs-keyword">const</span> notesWithContent: Note[] = 
    notes.filter(<span class="hljs-function"><span class="hljs-params">note</span> =&gt;</span> isNotEmpty(note.sections))
</code></pre>
<p>In the examples above, we pare down the original notes sequence based on the provided predicates. In the first example, we only keep notes that include a tag equal to "functional". In the second example, we only keep notes whose <code>sections</code> sequence is not empty.</p>
<h2 id="heading-flatmapt-gt-r"><code>flatMap(T =&gt; R[])</code></h2>
<p><code>flatMap</code> is very similar to <code>map</code> in that it applies a transformation to a sequence. The difference is that the provided function returns another sequence, which is then flattened down into a single sequence of elements.</p>
<p>We can use <code>flatMap</code> to grab all of the tags within each note and flatten those tags into a single sequence, without touching the original array.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> allTags: <span class="hljs-built_in">string</span>[] = 
    notes.flatMap&lt;<span class="hljs-built_in">string</span>&gt;(<span class="hljs-function"><span class="hljs-params">note</span> =&gt;</span> note.tags)
</code></pre>
<p>We can use the same concept to extract all links across all notes. Feel free to jump to the <a target="_blank" href="https://blog.oneiro.dev/smooth-operators-declarative-sequence-transformations-in-typescript#heading-type-reference">Type Reference</a> section as a refresher on the shape of the <code>Note</code> type.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> allSections: Section[] = 
    notes.flatMap&lt;Section&gt;(<span class="hljs-function"><span class="hljs-params">note</span> =&gt;</span> note.sections)

<span class="hljs-keyword">const</span> allLinks: <span class="hljs-built_in">string</span>[] =
    allSections.flatMap&lt;<span class="hljs-built_in">string</span>&gt;(<span class="hljs-function"><span class="hljs-params">section</span> =&gt;</span> section.links)
</code></pre>
<p>Now, let’s extend the example beyond simple extraction of strings and indulge in transforming notes to provide us with an entirely new sequence of link artifacts.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> LinkArtifact {
    noteId: <span class="hljs-built_in">string</span>
    heading: <span class="hljs-built_in">string</span>
    url: <span class="hljs-built_in">string</span>
}

<span class="hljs-keyword">const</span> allLinkArtifacts: LinkArtifact[] =
    notes.flatMap(<span class="hljs-function"><span class="hljs-params">note</span> =&gt;</span>
        note.sections.flatMap(<span class="hljs-function"><span class="hljs-params">section</span> =&gt;</span>
            section.links.map(<span class="hljs-function"><span class="hljs-params">link</span> =&gt;</span> ({
                noteId: note.id,
                heading: section.heading,
                url: link
            }))
        )
    )
</code></pre>
<p>In the examples above, we are transforming a sequence of notes into various data shapes by extracting nested sequences, flattening them, extracting additional values, and then flattening again. In other words, we are applying a transformation over a sequence of sequences and flattening that down into a single sequence, conceptually: <code>[[]] -&gt; []</code>.</p>
<p>Conceptually, <code>flatMap</code> can be hard to grasp. If it helps, focus on the simple example of extracting all tags from a sequence of notes and ending up with just a single sequence of tags in the end.</p>
<h2 id="heading-reducetstate-tvalue-gt-tstate-tstate"><code>reduce((TState, TValue) =&gt; TState, TState)</code></h2>
<p><code>reduce</code> is a sequence operator that applies a function to each value of a sequence and accumulates the result into a single state value. On each pass, the current element from the sequence and the previous accumulator value are provided to the reducer function. The value returned becomes the next accumulator. <code>reduce</code> requires a seed, or initial value, to kick things off.</p>
<p>The following example is for explanation, as we could more easily get the count of all tags by first applying <code>flatMap</code> and then accessing the resulting length. However, counting all items in a sequence is a simple example to grasp conceptually, and thus a good introduction to reduce.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> allTags: <span class="hljs-built_in">string</span>[] = 
    notes.flatMap(<span class="hljs-function"><span class="hljs-params">note</span> =&gt;</span> note.tags)

<span class="hljs-keyword">const</span> tagCount: <span class="hljs-built_in">number</span> =
    allTags.reduce(<span class="hljs-function">(<span class="hljs-params">count: <span class="hljs-built_in">number</span>, _tag: <span class="hljs-built_in">string</span></span>) =&gt;</span> count + <span class="hljs-number">1</span>, <span class="hljs-number">0</span>)
</code></pre>
<p>In the example above, we omit the use of the tag itself because we only care about the total count. For each element in the sequence, we add <code>1</code> to the current <code>count</code>, which becomes the next accumulator value. This continues until the end of the sequence is reached. You can think of this process as recursive in nature, since each step depends on the result of the previous one.</p>
<p>Now, let’s imagine that our goal is to count all of the individual unique tags in our notes and end up with a data shape that uses a <code>tag</code> as a key and its <code>count</code> as the value.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> TagCounts = Record&lt;<span class="hljs-built_in">string</span>, <span class="hljs-built_in">number</span>&gt;

<span class="hljs-keyword">const</span> allTags: <span class="hljs-built_in">string</span>[] =
    notes.flatMap(<span class="hljs-function"><span class="hljs-params">note</span> =&gt;</span> note.tags)

<span class="hljs-keyword">const</span> counts: TagCounts =
    allTags.reduce&lt;TagCounts&gt;(<span class="hljs-function">(<span class="hljs-params">accumulator: TagCounts, tag: <span class="hljs-built_in">string</span></span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> currentCount = accumulator[tag] ?? <span class="hljs-number">0</span>
        accumulator[tag] = currentCount + <span class="hljs-number">1</span>
        <span class="hljs-keyword">return</span> accumulator
    }, {})
</code></pre>
<p>In the example above, we first use flatMap to extract all tags from the notes sequence, producing a simple structure to work with, a single sequence of tags. We then reduce that sequence down into a single data structure, a record whose keys are tags and whose values represent how many times each tag appears in the sequence.</p>
<p>Finally, in the below example, we use reduce to accumulate multiple sequences into a single structured result. Rather than reducing to a number or a simple value, we are reducing the sequence of notes into a richer data shape that contains two sequences, one for links and one for references.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> Artifacts {
    links: <span class="hljs-built_in">string</span>[]
    references: <span class="hljs-built_in">string</span>[]
}

<span class="hljs-keyword">const</span> artifacts: Artifacts =
    notes.reduce&lt;Artifacts&gt;(<span class="hljs-function">(<span class="hljs-params">acc, note</span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> links =
            note.sections.flatMap(<span class="hljs-function"><span class="hljs-params">section</span> =&gt;</span> section.links)

        <span class="hljs-keyword">const</span> references =
            note.sections.flatMap(<span class="hljs-function"><span class="hljs-params">section</span> =&gt;</span> section.references)

        <span class="hljs-keyword">return</span> {
            links: acc.links.concat(links),
            references: acc.references.concat(references)
        }
    }, { links: [], references: [] })
</code></pre>
<p>We start with a seed value that represents an empty result, an object with two empty sequences. On each pass, we take a single note and extract its links and references by flattening over its sections. Rather than mutating the accumulator, we return a new object that combines the previously accumulated values with the newly extracted ones using concat. Each step produces a new accumulator that becomes the input to the next pass. By the end of the sequence, <code>reduce</code> has produced a final <code>Artifacts</code> value containing every link and every reference across all notes, without ever pushing to an external list or mutating shared state. <code>reduce</code> really shines here because it lets us describe how to build up a complex result from a sequence in a declarative way. In this example, we construct two sequences in parallel through the composition of <code>flatMap</code>, <code>concat</code>, and <code>reduce</code>.</p>
<h2 id="heading-building-a-sequence-immutably">Building a sequence immutably</h2>
<p>So far we have looked at <code>map</code>, <code>filter</code>, and <code>flatMap</code> as the building blocks for declarative transformations. We can combine those operators to build larger sequences without reaching for <code>reduce</code>, and without pushing into an external list.</p>
<p>Let’s say our goal is to build an artifacts sequence, but only for notes that include both the "functional" and "sequence" tags. From those notes, we want to extract all references and all links, then concatenate those results into a single list.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> Artifact {
    kind: <span class="hljs-string">'link'</span> | <span class="hljs-string">'reference'</span>
    value: <span class="hljs-built_in">string</span>
}

<span class="hljs-keyword">const</span> hasTag = <span class="hljs-function">(<span class="hljs-params">tag: <span class="hljs-built_in">string</span></span>) =&gt;</span>
    (note: Note) =&gt;
        note.tags.includes(tag)

<span class="hljs-keyword">const</span> isFunctional: <span class="hljs-function">(<span class="hljs-params">note: Note</span>) =&gt;</span> <span class="hljs-built_in">boolean</span> =
    hasTag(<span class="hljs-string">'functional'</span>)

<span class="hljs-keyword">const</span> isSequence: <span class="hljs-function">(<span class="hljs-params">note: Note</span>) =&gt;</span> <span class="hljs-built_in">boolean</span> =
    hasTag(<span class="hljs-string">'sequence'</span>)

<span class="hljs-keyword">const</span> isFunctionalSequenceNote = (note: Note): <span class="hljs-function"><span class="hljs-params">boolean</span> =&gt;</span>
    isFunctional(note) &amp;&amp; isSequence(note)

<span class="hljs-keyword">const</span> functionalSequenceNotes: Note[] =
    notes.filter(isFunctionalSequenceNote)

<span class="hljs-keyword">const</span> references: Artifact[] =
    functionalSequenceNotes
        .flatMap(<span class="hljs-function"><span class="hljs-params">note</span> =&gt;</span>
            note.sections.flatMap(<span class="hljs-function"><span class="hljs-params">section</span> =&gt;</span>
                section.references.map(<span class="hljs-function"><span class="hljs-params">reference</span> =&gt;</span> ({
                    kind: <span class="hljs-string">'reference'</span>,
                    value: reference
                }))
            )
        )

<span class="hljs-keyword">const</span> links: Artifact[] =
    functionalSequenceNotes
        .flatMap(<span class="hljs-function"><span class="hljs-params">note</span> =&gt;</span>
            note.sections.flatMap(<span class="hljs-function"><span class="hljs-params">section</span> =&gt;</span>
                section.links.map(<span class="hljs-function"><span class="hljs-params">link</span> =&gt;</span> ({
                    kind: <span class="hljs-string">'link'</span>,
                    value: link
                }))
            )
        )

<span class="hljs-keyword">const</span> artifacts: Artifact[] =
    references.concat(links)
</code></pre>
<p>The above approach keeps each step focused and readable. <code>filter</code> narrows the input sequence down to only the notes we care about, <code>flatMap</code> flattens nested sequences like sections and links, and <code>map</code> transforms each extracted value into a consistent artifact shape. Finally, <code>concat</code> combines independently-built sequences into one larger sequence, all without mutating state or manually pushing into a list.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this post, we explored a small but powerful set of sequence operators and how they can be composed to transform data in a declarative and immutable way. Using <code>map</code>, <code>filter</code>, <code>flatMap</code>, and <code>reduce</code>, we shaped, narrowed, flattened, and accumulated sequences without mutating state or relying on external loops and push operations. Each operator focuses on a single concern, and when combined, they form a clear and expressive pipeline for working with data.</p>
<p>The examples built on the same <code>notes</code> sequence, showing how small, intentional transformations can grow into more meaningful results, from simple projections to richer artifact structures. Rather than describing how to perform each step imperatively, we described what each step should produce, letting the sequence operators handle the mechanics.</p>
<p>These operators only scratch the surface of what is available when working with sequences in JavaScript. Functions like <code>some</code> and <code>every</code> help answer questions about a sequence, <code>find</code> and <code>findIndex</code> locate specific elements, <code>sort</code> and <code>slice</code> help reshape and reorder data, and <code>groupBy</code> opens the door to building structured views of a sequence. When combined with chaining and composition, these operators provide a rich vocabulary for expressing data transformations clearly and concisely.</p>
<p>Once you start thinking in terms of sequences and transformations, you may find fewer reasons to reach for mutable loops and temporary collections. Instead, you can build results step by step, directly from the data, in a way that is easier to read, reason about, and extend.</p>
<p>Building a sequence is about the journey and not a mutable destination.</p>
]]></content:encoded></item><item><title><![CDATA[Nulls Should Be Exceptional]]></title><description><![CDATA[Introduction
Null reference exceptions are the norm, they are not exceptional. For many developers, handling nothing (null) has become a mundane routine built into the mind's muscle memory. A few "if" statements there, a guard condition here, and yet...]]></description><link>https://blog.oneiro.dev/nulls-should-be-exceptional</link><guid isPermaLink="true">https://blog.oneiro.dev/nulls-should-be-exceptional</guid><category><![CDATA[Null]]></category><category><![CDATA[NullReferenceException]]></category><category><![CDATA[Monad]]></category><category><![CDATA[dotnet]]></category><category><![CDATA[.NET]]></category><category><![CDATA[Null Safety]]></category><category><![CDATA[Discriminated Union Types]]></category><category><![CDATA[C#]]></category><dc:creator><![CDATA[Mark Pro]]></dc:creator><pubDate>Wed, 10 Sep 2025 23:25:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1757546475188/d9cadb7a-e72a-4a6f-87a5-fb797cd20e3b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Null reference exceptions are the norm, they are not exceptional. For many developers, handling nothing (null) has become a mundane routine built into the mind's muscle memory. A few "if" statements there, a guard condition here, and yet many developers do not do enough to prevent their boring everyday code from becoming... well... exceptional.</p>
<p>"A billion-dollar mistake"(1), coined by its own designer, Tony Hoare and for good reason. Null has been responsible for a great number of bugs, vulnerabilities and system crashes. It has seeped into the nomenclature of systems despite the fact that null is insidious and unassumingly ambiguous.</p>
<p>What does "null" mean? Null typically means having no value or significance. However, in computer science, null is significant and represents the special value of having no value. How can something that, by definition, should not be significant, impact a great number of systems and software engineers? Despite its significance in computer science and its relatively clear layman's definition, null is ambiguous. Does null mean <em>uninitialized</em>, <em>not applicable</em>, <em>not provided</em>, <em>not existent</em>, <em>not found</em>, or simply <em>forgotten</em>?</p>
<p>Lets take a relatively trivial example of a simple user object and a variable. Lets say we have a <code>User</code> object with three fields, <em>FirstName</em>, <em>MiddleInitial</em> and <em>LastName</em> each a string value. While it might be expected of a person to not have a middle name and thus provide no middle initial to the system, a developer might simply forget to initialize the middle initial variable. If the latter is the case, then a middle initial may never be provided to the system, which can lead to bugs or guards that may never be required.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">record</span> <span class="hljs-title">User</span> {
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> FirstName { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> MiddleInitial { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> LastName { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
}

<span class="hljs-comment">// did we mean to leave out middle initial?</span>
<span class="hljs-comment">// was the middle initial not provided for this user?</span>
<span class="hljs-keyword">var</span> author = <span class="hljs-keyword">new</span> User {
    FirstName = <span class="hljs-string">"Mark"</span>,
    LastName = <span class="hljs-string">"Pro"</span>
};

<span class="hljs-comment">// what happened here? All fields are null</span>
<span class="hljs-keyword">var</span> uninitialized = <span class="hljs-keyword">new</span> User {};
</code></pre>
<p>Such a simple example trivially represents how fields may be neglected during initialization of an object. But, we can take this a step further. What if <code>FirstName</code> and <code>LastName</code> are never set, presenting a larger issue with the expressiveness of the object itself? Before we dive a little deeper, we should explore the function aspect of nulls and something that is all too common in a great many codebases I have had the pleasure of working in.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">record</span> <span class="hljs-title">User</span> {
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Id { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> FirstName { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> MiddleInitial { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> LastName { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
}

<span class="hljs-comment">// UserService Implementation</span>
<span class="hljs-keyword">sealed</span> <span class="hljs-keyword">class</span> <span class="hljs-title">UserService</span> {
    <span class="hljs-keyword">readonly</span> IDbConnection _dbConnection;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">UserService</span>(<span class="hljs-params">IDbConnection dbConnection</span>)</span> =&gt;
        _dbConnection = dbConnection;

    <span class="hljs-function"><span class="hljs-keyword">public</span> User <span class="hljs-title">GetUser</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> id</span>)</span> {
        <span class="hljs-keyword">if</span> (id == <span class="hljs-literal">null</span>) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>; <span class="hljs-comment">//&lt;- These null guards pollute their way into our code </span>

        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">var</span> user = _dbConnection.GetById(id); <span class="hljs-comment">//&lt;- used as an example here</span>
            <span class="hljs-keyword">return</span> user;
        }
        <span class="hljs-keyword">catch</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
        }
    }
}

<span class="hljs-comment">//What the consumer sees without reference</span>

<span class="hljs-keyword">var</span> userService = <span class="hljs-keyword">new</span> UserService(db);
<span class="hljs-keyword">var</span> user = userService.GetUser(<span class="hljs-string">"abc123"</span>);
</code></pre>
<p>Here we have access to the whole of the user context, which gives us some clues as to when the <code>GetUser</code> function may return null. However, we need to go looking for those clues, as the function is not "expressive" or rather it doesn't clearly state the intent and return of the function. Why is "null" potentially returned from <code>GetUser</code>? Is it because the ID was null when it was passed in, was there an exception when attempting to retrieve the information, or did the response back from the database return nothing?</p>
<p>While it might be tempting to return null as a default for when something goes awry, we have no good way of informing the consuming function that null needs to be accounted for. At least, not in how the <code>GetUser</code> function is currently implemented.</p>
<h2 id="heading-null-pollution">Null Pollution</h2>
<p>Nulls are insidious, like plastics that make their way into the environment that we then later have to clean up or deal with, as ignoring the problem could lead to larger consequences. When we encounter code that is not expressive, we must account for possible permutations of that code.</p>
<pre><code class="lang-cs"><span class="hljs-keyword">var</span> userService = <span class="hljs-keyword">new</span> UserService();
<span class="hljs-keyword">var</span> user = userService.GetUser(<span class="hljs-string">"abc123"</span>);

<span class="hljs-keyword">var</span> userName = 
    <span class="hljs-string">$"<span class="hljs-subst">{user.FirstName}</span> <span class="hljs-subst">{user.MiddleInitial}</span> <span class="hljs-subst">{user.LastName}</span>"</span>; 
    <span class="hljs-comment">//^ this will throw a NullReferenceException</span>

<span class="hljs-comment">//. do something with userName</span>
</code></pre>
<p>The above code may throw a <code>NullReferenceException</code> because the <code>GetUser</code> function may return null. Since null is a possible return value for <code>user</code> we must account for its inevitable lack of value.</p>
<pre><code class="lang-cs"><span class="hljs-keyword">if</span> (user <span class="hljs-keyword">is</span> not <span class="hljs-literal">null</span>) {
    <span class="hljs-keyword">var</span> userName = <span class="hljs-string">$"<span class="hljs-subst">{user.FirstName}</span> <span class="hljs-subst">{user.MiddleInitial}</span>. <span class="hljs-subst">{user.LastName}</span>"</span>;
    <span class="hljs-comment">//. do something with userName</span>
}
</code></pre>
<p>Despite the attempt to guard against nulls, there is still a potential bug in the code, and that is that the string may not contain the correct output, as either the first name, middle initial, or last name fields may also be null. Now we must account for all these nullable permutations of our code. In the modern .NET era, we have a variety of ways to deal with this permutation. For brevity I will use pattern matching to showcase the better handling of nulls.</p>
<pre><code class="lang-cs"><span class="hljs-keyword">var</span> userName = 
    user <span class="hljs-keyword">switch</span> {
        { FirstName: {}, MiddleInitial: {}, LastName: {} } u =&gt;
            <span class="hljs-string">$"<span class="hljs-subst">{u.FirstName}</span> <span class="hljs-subst">{u.MiddleInitial}</span>. <span class="hljs-subst">{u.LastName}</span>"</span>,
        { FirstName: {}, LastName: {} } u =&gt;
            <span class="hljs-string">$"<span class="hljs-subst">{u.FirstName}</span> <span class="hljs-subst">{u.LastName}</span>"</span>,
        _ =&gt; <span class="hljs-string">""</span>
    };
</code></pre>
<p>Using pattern matching, we have accounted for the permutations of the user object when constructing a name. Worse still is when a developer decides that an exception needs to be thrown exclusively when providing null, which most of the time is not required. In the case of modern DI, we will receive an exception stating that the object cannot be constructed if not enough information is provided, or the early construction of this object may throw exceptions on at runtime when the object is created, turning a null value passed into a constructor into a runtime error. Which, more than likely, would have still resulted in a runtime error later on, but with less explicit code. That is, either way the null reference would have to be resolved, but it could be done so without the explicit throwing of a null reference exception.</p>
<pre><code class="lang-cs"><span class="hljs-keyword">sealed</span> <span class="hljs-keyword">class</span> <span class="hljs-title">UserService</span> {
    <span class="hljs-keyword">readonly</span> IDbConnection _dbConnection;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">UserService</span>(<span class="hljs-params">IDbConnection dbConnection</span>)</span> =&gt;
        _dbConnection = 
            (dbConnection ?? <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(<span class="hljs-keyword">nameof</span>(dbConnection)));
}
</code></pre>
<p>Microsoft has taken it upon themselves to combat the boilerplate pollution that nulls problematically propagate by creating a plethora of operators to help handle unwieldy nulls.</p>
<pre><code class="lang-cs"><span class="hljs-comment">//What if a value is null and if so you want to reassign it?</span>
user ??= <span class="hljs-keyword">new</span> User();

<span class="hljs-comment">//What if a field is null and you would like to propgate the null</span>
<span class="hljs-keyword">var</span> firstName1 = user?.FirstName;

<span class="hljs-comment">//What if the field is null and you would like to provide an alternative</span>
<span class="hljs-keyword">var</span> firstName2 = user?.FirstName ?? <span class="hljs-string">"Mark"</span>;

<span class="hljs-comment">//Upcomming in dotnet 10 to replace the following</span>
user?.FirstName = <span class="hljs-string">"Mark"</span>;
</code></pre>
<h2 id="heading-moving-towards-something-better">Moving Towards Something Better</h2>
<p>After a substantial number of versions of C#, Microsoft has generously provided developers with the means to enable and support <strong>Nullable Reference Types (NRTs)</strong>. These are type markers that allow for a bit of expressiveness when defining code. To enable them, simply add <code>&lt;Nullable&gt;enable&lt;/Nullable&gt;</code> to your project file under <code>PropertyGroup</code>. Upon doing so, the compiler takes on some of the work of null checking. However, the output from the compiler is simply a warning and can be ignored. There are other instances where nullable reference types can be overridden.</p>
<pre><code class="lang-cs"><span class="hljs-keyword">sealed</span> <span class="hljs-keyword">class</span> <span class="hljs-title">UserService</span> {
    <span class="hljs-keyword">readonly</span> IDbConnection _dbConnection;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">UserService</span>(<span class="hljs-params">IDbConnection dbConnection</span>)</span> =&gt;
        _dbConnection = dbConnection;

    <span class="hljs-keyword">public</span> User? GetUser(<span class="hljs-keyword">string</span> id) {
        <span class="hljs-keyword">if</span> (id == <span class="hljs-literal">null</span>) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>; <span class="hljs-comment">//&lt;- These null gaurds pollute their way into our code </span>

        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">var</span> user = _dbConnection.GetById(id); <span class="hljs-comment">//&lt;- used as an example here</span>
            <span class="hljs-keyword">return</span> user;
        }
        <span class="hljs-keyword">catch</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
        }
    }
}
</code></pre>
<p>Given that NRTs are enabled, adding the <code>?</code> operator to the GetUser function indicates the function returned result may be null, telling the compiler that a warning should be surfaced when it detects that not all potential permutations of null are accounted for. Nullable reference types move us a bit towards expressive code, but are still easy to ignore and shut off by providing <strong>using</strong> the <code>!</code> or <code>!.</code> operator to silence any null warnings surfaced by the compiler.</p>
<pre><code class="lang-cs"><span class="hljs-keyword">var</span> userName = <span class="hljs-string">$"<span class="hljs-subst">{user!.FirstName}</span> <span class="hljs-subst">{user!.LastName}</span>"</span>; <span class="hljs-comment">//&lt;- silence compiler warning</span>
</code></pre>
<h2 id="heading-expressing-concerns">Expressing Concerns</h2>
<p>With all these built-in features for handling null, you’d think Microsoft would have provided a better way. They have with F#, but most mere mortal developers will never reach that far. Until we get fully baked discriminated unions(2) in .NET we have to make do with third-party libraries or build our own unions (for now). Wait… what is a discriminated union, you ask? A discriminated union is a way of saying that a value can be one of many possibilities, much like object inheritance “tags” an object or type to belong to a “family” of types.</p>
<p>To represent null, we can make our own simplistic discriminated union called <code>Option</code>. The idea is that <code>Option&lt;T&gt;</code> will either represent some value or none, allowing us to explicitly express that a function may or may not return a value to the caller.</p>
<pre><code class="lang-cs"><span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> <span class="hljs-keyword">record</span> <span class="hljs-title">Option</span>&lt;<span class="hljs-title">T</span>&gt; {

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">sealed</span> <span class="hljs-keyword">record</span> <span class="hljs-title">Some</span> : <span class="hljs-title">Option</span>&lt;<span class="hljs-title">T</span>&gt; {

        <span class="hljs-keyword">readonly</span> T _value;

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Some</span>(<span class="hljs-params">T <span class="hljs-keyword">value</span></span>)</span> {
            ArgumentNullException.ThrowIfNull(<span class="hljs-keyword">value</span>, <span class="hljs-keyword">nameof</span>(<span class="hljs-keyword">value</span>));
            _value = <span class="hljs-keyword">value</span>;
        }

        <span class="hljs-keyword">public</span> T Unwrap =&gt; _value;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">sealed</span> <span class="hljs-keyword">record</span> <span class="hljs-title">None</span> : <span class="hljs-title">Option</span>&lt;<span class="hljs-title">T</span>&gt;;
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Option</span> {

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-title">Option</span>&lt;<span class="hljs-title">T</span>&gt;.Some <span class="hljs-title">Some</span>&lt;<span class="hljs-title">T</span>&gt;(<span class="hljs-params">T <span class="hljs-keyword">value</span></span>)</span> =&gt;
        <span class="hljs-keyword">new</span> Option&lt;T&gt;.Some(<span class="hljs-keyword">value</span>);

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-title">Option</span>&lt;<span class="hljs-title">T</span>&gt;.None <span class="hljs-title">None</span>&lt;<span class="hljs-title">T</span>&gt;(<span class="hljs-params"></span>)</span> =&gt;
        <span class="hljs-keyword">new</span> Option&lt;T&gt;.None();

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-title">Option</span>&lt;<span class="hljs-title">T</span>&gt; <span class="hljs-title">Optional</span>&lt;<span class="hljs-title">T</span>&gt;(<span class="hljs-params">T <span class="hljs-keyword">value</span></span>)</span> =&gt;
        <span class="hljs-keyword">value</span> <span class="hljs-keyword">switch</span> {
            {} v =&gt; Some(v),
            _ =&gt; None&lt;T&gt;()
        };

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> R <span class="hljs-title">Match</span>&lt;<span class="hljs-title">T</span>, <span class="hljs-title">R</span>&gt;(<span class="hljs-params"><span class="hljs-keyword">this</span> Option&lt;T&gt; option, Func&lt;T, R&gt; some, Func&lt;R&gt; none</span>)</span> =&gt;
        option <span class="hljs-keyword">switch</span> {
            Option&lt;T&gt;.Some v =&gt; some(v.Unwrap),
            _ =&gt; none()
        };
}
</code></pre>
<p>The above represents a simplistic, expressive <code>Option</code> type which can be reused and extended to support more features, or you can use the one provided in <a target="_blank" href="https://github.com/louthy/language-ext">LanguageExt.Core</a>. Let's bring this back to the user service class example and see how the <code>GetById</code> function can now be done a bit more expressively by returning <code>Option&lt;User&gt;</code> as opposed to returning a <code>User</code> which may or may not be null. By specifying <code>Option&lt;User&gt;</code> the consumer is informed that <code>User</code> may or may not exist.</p>
<pre><code class="lang-cs"><span class="hljs-keyword">sealed</span> <span class="hljs-keyword">class</span> <span class="hljs-title">UserService</span> {
    <span class="hljs-keyword">readonly</span> IDbConnection _dbConnection;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">UserService</span>(<span class="hljs-params">IDbConnection dbConnection</span>)</span> =&gt;
        _dbConnection = dbConnection;

    <span class="hljs-function"><span class="hljs-keyword">public</span> Option&lt;User&gt; <span class="hljs-title">GetUser</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> id</span>)</span> {
        <span class="hljs-keyword">if</span> (id == <span class="hljs-literal">null</span>) <span class="hljs-keyword">return</span> Option.None&lt;User&gt;();
        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">var</span> user = _dbConnection.GetById(id); <span class="hljs-comment">//&lt;- used as an example here</span>
            <span class="hljs-keyword">return</span> Option.Some(user);
        }
        <span class="hljs-keyword">catch</span> {
            <span class="hljs-keyword">return</span> Option.None&lt;User&gt;();
        }
    }
}
</code></pre>
<p>Now the consumer code must account for either some value or none (nothing).</p>
<pre><code class="lang-cs"><span class="hljs-keyword">var</span> userService = <span class="hljs-keyword">new</span> UserService();
<span class="hljs-keyword">var</span> user = userService.GetUser(<span class="hljs-string">"abc123"</span>);

<span class="hljs-keyword">var</span> userName = 
    user.Match(
        user =&gt; <span class="hljs-string">$"<span class="hljs-subst">{user.FirstName}</span> <span class="hljs-subst">{user.MiddleInitial}</span> <span class="hljs-subst">{user.LastName}</span>"</span>,
        () =&gt; <span class="hljs-keyword">string</span>.Empty
    );
</code></pre>
<p>That simple shift in design; from returning null to returning something expressive, highlights the larger point of this article.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Null has been with us for decades, and for just as long developers have been patching around it with guards, defaults, and boilerplate. The problem is not that null exists, but that it is ambiguous. A null can mean too many things at once, and that ambiguity leaks into our designs, our APIs, and eventually our runtime errors.</p>
<p>C# gives us tools to work with null: nullable reference types, pattern matching, and null-coalescing operators. These help, but they mostly treat null as an inevitability to be managed. What changes the story is when we design for absence directly. Modeling “something or nothing” with an <code>Option&lt;T&gt;</code> or another union type makes absence part of the type system instead of leaving it as a hidden runtime hazard.</p>
<p>This shift makes our code more expressive. A function that returns <code>Option&lt;User&gt;</code> communicates its intent far more clearly than one that returns User but sometimes null. Consumers of your code know what to expect, the compiler helps enforce it, and your intent stays visible.</p>
<p>Nulls should be exceptional, not routine. By adopting more expressive patterns like <code>Option&lt;T&gt;</code>, discriminated unions, or other evolving features of C#, we can write code that is safer, more predictable, and less haunted by the billion-dollar mistake.</p>
<hr />
<h2 id="heading-references">References</h2>
<ol>
<li><p><a target="_blank" href="https://www.youtube.com/watch?v=ybrQvs4x0Ps">Null References: The Billion Dollar Mistake by Tony Hoare</a></p>
</li>
<li><p><a target="_blank" href="https://davecallan.com/discriminated-unions-csharp-update/">Discriminated Union Update</a></p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Be Expressive: Null Handling]]></title><description><![CDATA[TL;DR
C# developers have better options than relying on null. Use value types for guaranteed validity, Option<T> in libraries like LanguageExt for explicit absence, required/init for complete object construction, and Nullable Reference Types for safe...]]></description><link>https://blog.oneiro.dev/be-expressive-null-handling</link><guid isPermaLink="true">https://blog.oneiro.dev/be-expressive-null-handling</guid><category><![CDATA[Null]]></category><category><![CDATA[Nullish coalescing]]></category><category><![CDATA[Null Safety]]></category><category><![CDATA[Discriminated Union Types]]></category><category><![CDATA[union type]]></category><category><![CDATA[dotnet]]></category><category><![CDATA[C#]]></category><category><![CDATA[value types]]></category><category><![CDATA[opinion pieces]]></category><category><![CDATA[nullable-reference-type]]></category><category><![CDATA[.NET]]></category><category><![CDATA[expressive]]></category><category><![CDATA[Records]]></category><category><![CDATA[struct]]></category><dc:creator><![CDATA[Mark Pro]]></dc:creator><pubDate>Wed, 10 Sep 2025 14:27:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1757518719014/a38ddba2-974f-405a-a690-190db237f132.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong>TL;DR</strong></p>
<p>C# developers have better options than relying on <code>null</code>. Use value types for guaranteed validity, <code>Option&lt;T&gt;</code> in libraries like <code>LanguageExt</code> for explicit absence, <code>required</code>/<code>init</code> for complete object construction, and Nullable Reference Types for safer references. Together, these tools reduce ambiguity, improve safety, and make intent clear. With native discriminated unions on the horizon for C#, modeling data is becoming even more expressive.</p>
</blockquote>
<p>Tony Hoare coined the term "Null is a Billion-Dollar Mistake" and for good reason. Null is ambiguous. Does it mean <em>not found</em>, <em>not applicable</em>, <em>not initialized</em>, or just <em>a bug</em>? Null leads to countless runtime errors, with <code>NullReferenceException</code> consistently topping .NET error logs. It also encourages defensive programming, cluttering code with checks like <code>if (x is not null)</code> or <code>if (x == null)</code>. Fortunately, modern C# offers better alternatives, including those introduced through third-party libraries.</p>
<h2 id="heading-value-types">Value Types</h2>
<p>Value types require that all data be provided at the time of construction. Either a default constructor must exist, or every field must be initialized before leaving the constructor. This allows the compiler to enforce validity up front. A properly defined value type like <code>ZipCode</code> can never be <code>null</code>. Instead, it can be safely defaulted, for instance using <code>ZipCode.Default</code>, and later validated using an <code>IsDefault</code> accessor.</p>
<p>The downside is the boilerplate involved. Nearly every such type must be defined manually. However, libraries like <a target="_blank" href="https://github.com/oneirosoft/new-type/blob/main/src/NewType.Core/README.md">NewType</a> and <a target="_blank" href="https://github.com/mcintyre321/valueof">ValueOf</a> help reduce this overhead. They make it easier to create expressive value types, with optional validation logic, without writing repetitive code.</p>
<p>Value types are essentially structs where their values are encapsulated and any information must be explicitly accessed. An example of such a value type is a <code>ZipCode</code> type, which can have either a five-digit or ZIP+4 (nine-digit) code.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> System.Text.RegularExpressions;

<span class="hljs-keyword">readonly</span> <span class="hljs-keyword">record</span> <span class="hljs-title">struct</span> <span class="hljs-title">ZipCode</span> {
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> ReadOnlyMemory&lt;<span class="hljs-keyword">char</span>&gt; _value;

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">ZipCode</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">value</span></span>)</span> =&gt; _value = <span class="hljs-keyword">value</span>.AsMemory();

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> ZipCode <span class="hljs-title">Create</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">value</span></span>)</span> =&gt;
        ZipCodeRegex.Match(<span class="hljs-keyword">value</span>) <span class="hljs-keyword">switch</span> {
            { Groups: [_, { Success: <span class="hljs-literal">true</span> } fst, _, { Success: <span class="hljs-literal">true</span> } lst] } =&gt; 
                <span class="hljs-keyword">new</span> (<span class="hljs-string">$"<span class="hljs-subst">{fst.Value}</span>-<span class="hljs-subst">{lst.Value}</span>"</span>),
            { Groups: [_, { Success: <span class="hljs-literal">true</span> } fst, _, _] } =&gt; <span class="hljs-keyword">new</span> (fst.Value),
            _ =&gt; <span class="hljs-keyword">default</span>
        };

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">bool</span> IsDefault =&gt; _value.<span class="hljs-function">IsEmpy

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">string</span> <span class="hljs-title">ToString</span>(<span class="hljs-params"></span>)</span> =&gt; _value.ToString();

    <span class="hljs-keyword">static</span> <span class="hljs-keyword">readonly</span> Regex ZipCodeRegex =
        <span class="hljs-keyword">new</span> (<span class="hljs-string">"^([0-9]{5})(-([0-9]{4}))?$"</span>);
}

<span class="hljs-keyword">var</span> zip = ZipCode.Create(<span class="hljs-string">"33431"</span>);
</code></pre>
<p>The savvy .NET developer might point out that one could call <code>new ZipCode()</code> or have an array of codes that are not initialized, such as <code>new ZipCode[5]</code>. That developer is correct. However, the internal value is not <code>null</code> in these cases and is safe to act on.</p>
<h2 id="heading-expressive-absence-with-option">Expressive Absence with <code>Option&lt;T&gt;</code></h2>
<p>The <code>Option&lt;T&gt;</code> type offers a safer and more expressive way to model values that may or may not be present. Rather than returning <code>null</code>, which forces guesswork and defensive checks, an API that returns <code>Option&lt;User&gt;</code> is making its contract explicit: a user may exist, or the result may be absent, and the caller is responsible for handling both.</p>
<p>This explicitness removes ambiguity and shifts absence from an implementation detail to a part of the type system. The result is more declarative, correct, and predictable code. Rather than defensively reacting to potential <code>null</code>, you proactively engage with known possibilities. Adopting <code>Option&lt;T&gt;</code> does introduce a small learning curve and usually a third-party library, but the clarity and safety it adds often outweigh those costs. Looking ahead, Microsoft’s plans to bring discriminated unions, and potentially a native <code>Option&lt;T&gt;</code> type, to C# may reduce those barriers.</p>
<p>We can represent that a field in a record might not be present and that a value returned from a function can also represent absence.</p>
<p><strong>A compact, drop-in example (compiles with LanguageExt)</strong></p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> LanguageExt;
<span class="hljs-keyword">using</span> <span class="hljs-keyword">static</span> LanguageExt.Prelude;

<span class="hljs-keyword">record</span> <span class="hljs-title">Author</span>(<span class="hljs-title">string</span> <span class="hljs-title">FirstName</span>, <span class="hljs-title">Option</span>&lt;<span class="hljs-title">string</span>&gt; <span class="hljs-title">MiddleInitial</span>, <span class="hljs-title">string</span> <span class="hljs-title">LastName</span>);

<span class="hljs-function">Option&lt;Author&gt; <span class="hljs-title">GetAuthor</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> lastName</span>)</span> {
    Author[] authors = [
        <span class="hljs-keyword">new</span>(<span class="hljs-string">"Ernest"</span>, None, <span class="hljs-string">"Hemingway"</span>),
        <span class="hljs-keyword">new</span>(<span class="hljs-string">"Charles"</span>, None, <span class="hljs-string">"Dickens"</span>)
    ];

    <span class="hljs-keyword">foreach</span>(<span class="hljs-keyword">var</span> a <span class="hljs-keyword">in</span> authors)
        <span class="hljs-keyword">if</span>(author.LastName == lastName) <span class="hljs-keyword">return</span> Some(a);

    <span class="hljs-keyword">return</span> None;
};
</code></pre>
<h2 id="heading-enforcing-presence-with-required-and-init">Enforcing Presence with <code>required</code> and <code>init</code></h2>
<p>C# has introduced additional tools to help prevent uninitialized and partially initialized objects from entering the system. The <code>required</code> and <code>init</code> keywords are designed to encourage complete construction and immutable design. These keywords do not replace <code>Option&lt;T&gt;</code> or value types; instead, they complement them by guaranteeing required members are always set before use.</p>
<p>By default, all fields in C# <code>record</code> types must be initialized through the constructor. That is, all fields must be set during the type's construction. Fields in records can use explicit accessors without the need to specify the default constructor.</p>
<p>One could have an <code>Author</code> <code>record</code> in which the first name and last name need to be provided at construction, while the middle initial does not have to be set. However, due to the use of <code>init</code>, the value cannot be set once the type has been initialized. If a required field is not initialized during instantiation, the compiler produces an error and prevents the project from building.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">record</span> <span class="hljs-title">Author</span> {
    <span class="hljs-keyword">public</span> required <span class="hljs-keyword">string</span> FirstName { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">init</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span>? MiddleInitial { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">init</span>; }
    <span class="hljs-keyword">public</span> required <span class="hljs-keyword">string</span> LastName { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">init</span>; }
}

<span class="hljs-keyword">var</span> author1 = <span class="hljs-keyword">new</span> Author {
    FirstName = <span class="hljs-string">"Charls"</span>,
    LastName = <span class="hljs-string">"Dickens"</span>
};

<span class="hljs-keyword">var</span> author2 = <span class="hljs-keyword">new</span> Author {
    FirstName = <span class="hljs-string">"Ernest"</span>
}; <span class="hljs-comment">//. Will error as LastName is not set</span>
</code></pre>
<h2 id="heading-nullable-reference-types-nrts">Nullable Reference Types (NRTs)</h2>
<p>C# 8 introduced nullable reference types, which allow developers to specify whether a given reference can hold <code>null</code>. When enabled, the compiler will warn you when you try to dereference a value that might be null, or fail to assign to a non-nullable property.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">string</span> name = <span class="hljs-string">"Mark"</span>;    <span class="hljs-comment">// non-nullable</span>
<span class="hljs-keyword">string</span>? nickname = <span class="hljs-literal">null</span>; <span class="hljs-comment">// nullable</span>
</code></pre>
<p>These annotations provide a layer of static analysis that encourages safer handling of reference types. You can enable them per file with <code>#nullable enable</code>, or globally within a project:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">Nullable</span>&gt;</span>enable<span class="hljs-tag">&lt;/<span class="hljs-name">Nullable</span>&gt;</span>
</code></pre>
<p>Once enabled, the compiler distinguishes between <code>string</code> and <code>string?</code>, making intent clearer and usage safer. You are prompted to consider where null might flow through your system and address it accordingly.</p>
<p>This model is a significant improvement over the historical default where all references could be null implicitly. It reduces accidents and helps signal the need for validation. However, it falls short of offering true modeling power. Nullable reference types do not prevent runtime nulls. They are enforced through warnings, not type errors, which can be ignored or suppressed. They do not compose. You cannot map, match, or bind nullable references in a fluent, predictable way. Their meaning is also less precise. A <code>string?</code> might mean “optional” or it might just mean “uninitialized.” The type system cannot differentiate.</p>
<p>As a result, NRTs are a useful baseline but not a replacement for union types like <code>Option&lt;T&gt;</code>. They can be used together, and in many codebases, enabling nullable annotations is a good first step. But when your domain logic demands clear expression of absence, when you want types that communicate and behavior that composes, <code>Option&lt;T&gt;</code> remains the better choice.</p>
<blockquote>
<p>Enable NRTs to reduce accidental nulls, but use <code>Option&lt;T&gt;</code> when you want to express absence as part of your model.</p>
</blockquote>
<h2 id="heading-native-discriminated-unions-coming-to-c"><strong>Native Discriminated Unions (Coming to C#)</strong></h2>
<p>C# is moving closer to providing <strong>native support for discriminated unions</strong>. Discriminated unions are also known as <strong>“type unions</strong>," which will allow developers to define types that represent a value in one of several named cases, each potentially carrying its own data.</p>
<p>Although discriminated unions are not yet available in C# 14 or .NET 10, they are an active topic for the C# language design team. Proposals and working group notes outline how the feature might look in future versions of the language.</p>
<p>Below is a peek into Microsoft's proposal:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> union Option&lt;T&gt; {
    <span class="hljs-function"><span class="hljs-keyword">case</span> <span class="hljs-title">Some</span>(<span class="hljs-params">T Value</span>)</span>;
    <span class="hljs-keyword">case</span> None;
}
</code></pre>
<p>A union enables <strong>exhaustive pattern matching at compile time</strong>, so when you use a <code>switch</code> expression over a union, the compiler ensures all cases are handled. Such an approach is significantly safer and more expressive than traditional inheritance-based patterns.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> pet = Some(<span class="hljs-keyword">new</span> Pet(<span class="hljs-string">"Oliver"</span>, <span class="hljs-string">"cat"</span>));

_ = pet <span class="hljs-keyword">switch</span> {
    Some(<span class="hljs-keyword">value</span>) =&gt; ...,
    None =&gt; ...
};
</code></pre>
<p>Feel free to learn more about unions by reading <a target="_blank" href="https://github.com/dotnet/csharplang/blob/12e6f5b0d512d15d32c8e7ae95674bd070b2758f/meetings/working-groups/discriminated-unions/union-proposals-overview.md#target-typed-generic-type-inference">Microsoft's Union proposals overview</a>.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Null has long been a source of ambiguity and runtime bugs in C#. With the combined use of value types, <code>Option&lt;T&gt;</code>, required/init, and nullable reference types, developers have a richer toolbox for designing safer, more expressive code. And with discriminated unions being developed for future C# versions, the language is taking another major step toward making absence and alternatives explicit in the type system rather than hidden in runtime behavior. Together, these features move C# closer to a future where intent is clear, bugs are reduced, and code is easier to reason about.</p>
]]></content:encoded></item></channel></rss>