<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
<title>Incremental Elm Tips</title>
<description>Incremental Elm Consulting</description>
<link>https://incrementalelm.com/tips</link>
<lastBuildDate>Mon, 23 Feb 2026 23:04:45 +0000</lastBuildDate>
<generator>elm-pages</generator>
<item>
<title>Announcing the Intro to elm-ts-interop Course</title>
<description>Today I&apos;m announcing a new course on elm-ts-interop fundamentals and setup instructions. This is a free course, and it focuses on the Community Edition of elm-ts-interop, plus a chapter on the differences between the Community and Pro Editions.</description>
<link>https://incrementalelm.com/announcing-elm-ts-interop-course</link>
<guid>https://incrementalelm.com/announcing-elm-ts-interop-course</guid>
<pubDate>Mon, 15 Nov 2021 00:00:00 GMT</pubDate>
<content>&lt;p&gt;Today I&apos;m announcing &lt;a href=&quot;http://incrementalelm.com/courses/elm-ts-interop&quot;&gt;a new course on &lt;code&gt;elm-ts-interop&lt;/code&gt; fundamentals and setup instructions&lt;/a&gt;. This is a free course, and it focuses on the Community Edition of &lt;code&gt;elm-ts-interop&lt;/code&gt;, plus a chapter on the differences between the Community and Pro Editions.&lt;/p&gt;&lt;p&gt;It includes some detailed examples, explanations, and exercises. I even created a small command-line tool (&lt;a href=&quot;https://www.npmjs.com/package/ellie-app&quot;&gt;&lt;code&gt;ellie-app-cli&lt;/code&gt;&lt;/a&gt;) to let you instantly open and run the interactive exercises with your favorite editor in your local environment. You can try it out on one of the exercises from the &lt;code&gt;elm-ts-interop&lt;/code&gt; course:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;npm install -g elm-live elm-json ellie-app &amp;amp;&amp;amp; ellie-app fQWYRPSQxvsa1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I&apos;m working on some paid Elm courses, so stay tuned for those. My goal is to produce quality content that explores techniques for making Elm applications more maintainable, as well as some tutorials on Elm libraries that go beyond the basics. I hope this will be a valuable resource for Elm devs looking to dive deeper, and I also hope it can help support my continued work on open source projects like &lt;code&gt;elm-pages&lt;/code&gt;, &lt;code&gt;elm-graphql&lt;/code&gt;, &lt;code&gt;elm-markdown&lt;/code&gt;, and other efforts in my mission to make the Elm ecosystem as vibrant as possible.&lt;/p&gt;&lt;p&gt;If you have any feedback on this course, or anything you&apos;d like to see in future courses, I&apos;d love to hear your thoughts!&lt;/p&gt;</content>
<content:encoded><![CDATA[<p>Today I'm announcing <a href="http://incrementalelm.com/courses/elm-ts-interop">a new course on <code>elm-ts-interop</code> fundamentals and setup instructions</a>. This is a free course, and it focuses on the Community Edition of <code>elm-ts-interop</code>, plus a chapter on the differences between the Community and Pro Editions.</p><p>It includes some detailed examples, explanations, and exercises. I even created a small command-line tool (<a href="https://www.npmjs.com/package/ellie-app"><code>ellie-app-cli</code></a>) to let you instantly open and run the interactive exercises with your favorite editor in your local environment. You can try it out on one of the exercises from the <code>elm-ts-interop</code> course:</p><pre><code>npm install -g elm-live elm-json ellie-app &amp;&amp; ellie-app fQWYRPSQxvsa1
</code></pre><p>I'm working on some paid Elm courses, so stay tuned for those. My goal is to produce quality content that explores techniques for making Elm applications more maintainable, as well as some tutorials on Elm libraries that go beyond the basics. I hope this will be a valuable resource for Elm devs looking to dive deeper, and I also hope it can help support my continued work on open source projects like <code>elm-pages</code>, <code>elm-graphql</code>, <code>elm-markdown</code>, and other efforts in my mission to make the Elm ecosystem as vibrant as possible.</p><p>If you have any feedback on this course, or anything you'd like to see in future courses, I'd love to hear your thoughts!</p>]]></content:encoded>
</item>
<item>
<title>I&apos;m Not a Programmer, I&apos;m an Automator</title>
<description>Automation isn&apos;t just about efficiency, it&apos;s also about, quality, safety, and continuous improvement.</description>
<link>https://incrementalelm.com/automating-quality</link>
<guid>https://incrementalelm.com/automating-quality</guid>
<pubDate>Mon, 12 Oct 2020 00:00:00 GMT</pubDate>
<content>&lt;p&gt;Recently I wrote about a core ingredient to manageable code: [&lt;a title=&quot;Relentless Tiny Habits&quot; href=&quot;relentless-tiny-habits&quot;&gt;relentless-tiny-habits&lt;/a&gt;].&lt;/p&gt;&lt;p&gt;Having brilliant code design ideas doesn&apos;t necessarily help you manage your codebase better. But building in relentless, tiny habits is likely to bring out some brilliant designs.&lt;/p&gt;&lt;p&gt;I think we can generalize this one essential technique a step further. Habits are indeed very powerful, and greater than the sum of their parts. But a habit is just a special case of an automation.&lt;/p&gt;&lt;p&gt;Instead of thinking of myself as a programmer, I like to frame my job as an &lt;strong&gt;automator&lt;/strong&gt;. Programming is a type of automation. I think you can argue that a habit is a kind of automation as well. It&apos;s just that we&apos;re automating by training the wiring in our brain instead of automating through code or scripts. Thinking of our role as an automator rather than a programmer can deeply change the way you approach your work.&lt;/p&gt;&lt;h2&gt;Who Cares If It&apos;s &quot;Programmer&quot; or &quot;Automator&quot;?&lt;/h2&gt;&lt;p&gt;It&apos;s a subtle distinction. I think the power comes from having clarity on what tasks to prioritize, and how to approach your work. Is manually running a set of deployment steps a good use of time for a Programmer? Perhaps. But it&apos;s pretty clear that there is some important work to prioritize improving that manual process when you see yourself as an &quot;Automator.&quot;&lt;/p&gt;&lt;p&gt;Embrace that role, and use that framing of the role as a cue to step back and make a small investment to automate a repetitive task.&lt;/p&gt;&lt;h2&gt;Identifying Automation Opportunities&lt;/h2&gt;&lt;h3&gt;Examples of Automation&lt;/h3&gt;&lt;p&gt;Automation opportunities can take many forms.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Organizational/process automation&lt;/li&gt;&lt;li&gt;Grouping code into functions (a function is really a way of automating machine instructions)&lt;/li&gt;&lt;li&gt;Automated tests instead of manual testing&lt;/li&gt;&lt;li&gt;Shortcut keys instead of repetitive menu navigation&lt;/li&gt;&lt;li&gt;Code generation for tasks like internationalization&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Signs of Automation Opportunities&lt;/h3&gt;&lt;p&gt;There are some broad patterns to look out for.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Repetitive steps&lt;/li&gt;&lt;li&gt;Multiple sources of truth&lt;/li&gt;&lt;li&gt;Context shifts&lt;/li&gt;&lt;li&gt;Quality issues (frequent bugs from a process or area of code)&lt;/li&gt;&lt;li&gt;Steps to add quality after (rather than built-in quality)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Stepping back a level, it&apos;s important to build the habit of recognizing these signs and taking a moment to identify and address them. There are many ways to do this, like retrospectives or keeping a running list of things that are slowing you down.&lt;/p&gt;&lt;h2&gt;Familiar Elm automation tools&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;The Elm type system&lt;/li&gt;&lt;li&gt;elm-test&lt;/li&gt;&lt;li&gt;elm-program-test&lt;/li&gt;&lt;li&gt;elm-review&lt;/li&gt;&lt;li&gt;elm-graphql (and other codegen)&lt;/li&gt;&lt;li&gt;elm-format&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;These examples may seem obvious because we&apos;re used to them. But those tools weren&apos;t necessarily obvious before somebody saw that automation opportunity and took action. Could you find similar opportunities in your own codebase? And what low-hanging fruit could you automate right now? Any time you can automate quality and correctness instead of verifying it manually, it&apos;s a huge win.&lt;/p&gt;&lt;h2&gt;Continuous Improvement&lt;/h2&gt;&lt;p&gt;Fully baked automation tools are great. But code is a lot easier to iterate on than hardware. So the best automation you can invest in is automating your habit of making small improvements as you see them!&lt;/p&gt;&lt;p&gt;If you notice repeatedly doing an action in your editor, you could take a moment to look up the keyboard shortcut. Or move a set of manual commands into a repeatable script. You can even think of Making Impossible States Impossible as a type of automation, providing guardrails and ensuring that future changes don&apos;t run into the same issues. In Toyota culture, this concept of automating quality is called &lt;a href=&quot;https://kanbanize.com/lean-management/improvement/what-is-poka-yoke&quot;&gt;poka yoke&lt;/a&gt;, or foolproofing.&lt;/p&gt;&lt;p&gt;Importantly, these small habits and automations are greater than the sum of their parts. If you learn a keyboard shortcut for the rename function refactoring, you&apos;re not only saving the time you would have spent renaming manually or accessing the refactoring with the mouse. You are reducing the barrier to that action, which leads to better names in your codebase over time.&lt;/p&gt;&lt;h2&gt;Toyota Is Not a Car Manufacturer&lt;/h2&gt;&lt;p&gt;Toyota is famous for the saying that they&apos;re not a car manufacturing company. They&apos;re a car manufacturing improvement company. Their product is not cars, it&apos;s their automated, continually improving process for manufacturing cars. Quality cars are the by-product.&lt;/p&gt;&lt;p&gt;In the same way, you can apply Systems Thinking to your code. You improve the results you&apos;re getting by improving the process that generates those results. That doesn&apos;t mean you ignore the results. It&apos;s essential to drive improvements based on concrete opportunities and pain points, not theoretical or anticipated ones. But once you have something concrete, step back and look at the system, and automate it.&lt;/p&gt;</content>
<content:encoded><![CDATA[<p>Recently I wrote about a core ingredient to manageable code: [<a title="Relentless Tiny Habits" href="relentless-tiny-habits">relentless-tiny-habits</a>].</p><p>Having brilliant code design ideas doesn't necessarily help you manage your codebase better. But building in relentless, tiny habits is likely to bring out some brilliant designs.</p><p>I think we can generalize this one essential technique a step further. Habits are indeed very powerful, and greater than the sum of their parts. But a habit is just a special case of an automation.</p><p>Instead of thinking of myself as a programmer, I like to frame my job as an <strong>automator</strong>. Programming is a type of automation. I think you can argue that a habit is a kind of automation as well. It's just that we're automating by training the wiring in our brain instead of automating through code or scripts. Thinking of our role as an automator rather than a programmer can deeply change the way you approach your work.</p><h2>Who Cares If It's "Programmer" or "Automator"?</h2><p>It's a subtle distinction. I think the power comes from having clarity on what tasks to prioritize, and how to approach your work. Is manually running a set of deployment steps a good use of time for a Programmer? Perhaps. But it's pretty clear that there is some important work to prioritize improving that manual process when you see yourself as an "Automator."</p><p>Embrace that role, and use that framing of the role as a cue to step back and make a small investment to automate a repetitive task.</p><h2>Identifying Automation Opportunities</h2><h3>Examples of Automation</h3><p>Automation opportunities can take many forms.</p><ul><li>Organizational/process automation</li><li>Grouping code into functions (a function is really a way of automating machine instructions)</li><li>Automated tests instead of manual testing</li><li>Shortcut keys instead of repetitive menu navigation</li><li>Code generation for tasks like internationalization</li></ul><h3>Signs of Automation Opportunities</h3><p>There are some broad patterns to look out for.</p><ul><li>Repetitive steps</li><li>Multiple sources of truth</li><li>Context shifts</li><li>Quality issues (frequent bugs from a process or area of code)</li><li>Steps to add quality after (rather than built-in quality)</li></ul><p>Stepping back a level, it's important to build the habit of recognizing these signs and taking a moment to identify and address them. There are many ways to do this, like retrospectives or keeping a running list of things that are slowing you down.</p><h2>Familiar Elm automation tools</h2><ul><li>The Elm type system</li><li>elm-test</li><li>elm-program-test</li><li>elm-review</li><li>elm-graphql (and other codegen)</li><li>elm-format</li></ul><p>These examples may seem obvious because we're used to them. But those tools weren't necessarily obvious before somebody saw that automation opportunity and took action. Could you find similar opportunities in your own codebase? And what low-hanging fruit could you automate right now? Any time you can automate quality and correctness instead of verifying it manually, it's a huge win.</p><h2>Continuous Improvement</h2><p>Fully baked automation tools are great. But code is a lot easier to iterate on than hardware. So the best automation you can invest in is automating your habit of making small improvements as you see them!</p><p>If you notice repeatedly doing an action in your editor, you could take a moment to look up the keyboard shortcut. Or move a set of manual commands into a repeatable script. You can even think of Making Impossible States Impossible as a type of automation, providing guardrails and ensuring that future changes don't run into the same issues. In Toyota culture, this concept of automating quality is called <a href="https://kanbanize.com/lean-management/improvement/what-is-poka-yoke">poka yoke</a>, or foolproofing.</p><p>Importantly, these small habits and automations are greater than the sum of their parts. If you learn a keyboard shortcut for the rename function refactoring, you're not only saving the time you would have spent renaming manually or accessing the refactoring with the mouse. You are reducing the barrier to that action, which leads to better names in your codebase over time.</p><h2>Toyota Is Not a Car Manufacturer</h2><p>Toyota is famous for the saying that they're not a car manufacturing company. They're a car manufacturing improvement company. Their product is not cars, it's their automated, continually improving process for manufacturing cars. Quality cars are the by-product.</p><p>In the same way, you can apply Systems Thinking to your code. You improve the results you're getting by improving the process that generates those results. That doesn't mean you ignore the results. It's essential to drive improvements based on concrete opportunities and pain points, not theoretical or anticipated ones. But once you have something concrete, step back and look at the system, and automate it.</p>]]></content:encoded>
</item>
<item>
<title>Combinators - Inverting Top-Down Transforms</title>
<description>One of my favorite things about functional programming is the ability to work and think in a very localized area of code. Let&apos;s talk about some of the patterns that make that possible.</description>
<link>https://incrementalelm.com/combinators</link>
<guid>https://incrementalelm.com/combinators</guid>
<pubDate>Tue, 16 Feb 2021 00:00:00 GMT</pubDate>
<content>&lt;p&gt;I won&apos;t go into how immutability, managed effects, or lack of global variables help us reason locally - although they really do! What I want to focus on here is the power of individual transformations composed together, rather than making one big transformation. This concept is sometimes called a Combinator.&lt;/p&gt;&lt;h2&gt;Top-Down Vs. Bottom-Up Transformations&lt;/h2&gt;&lt;p&gt;In my JavaScript days, I remember a particularly tricky area of code where we had a big list of data in a format defined by the server. We needed to take product listings, pull out specific options and inventory information, normalize them, and then apply filters from the UI to show/hide and sort search results.&lt;/p&gt;&lt;p&gt;There were deeply nested fields, and some incongruities in the shape of the data. Some values were nullable. Some had specific normalization we needed to apply to get data from multiple sources to match.&lt;/p&gt;&lt;p&gt;We had a lot of big unit tests to make sure things were working. Even so, it was so difficult to go in to our series of lodash function calls and find &lt;em&gt;where&lt;/em&gt; you needed to make a change. And once you did, you would want to make sure you added several new test cases to make sure you didn&apos;t miss a spot or mishandle a special case.&lt;/p&gt;&lt;p&gt;We would sit in awe as the person most familiar with the codebase correctly traversed the nested arrays and objects to get to the exact right place and make a change on the first try. It was a lot to hold in our heads, and it was quite error prone.&lt;/p&gt;&lt;p&gt;The challenge was that using that paradigm to normalize JSON data required thinking of the data as a monolith. We certainly abstracted out functions to help with parts of the normalization. And we used lodash to do functional style mapping over the arrays of data and key-value objects. But mapping over arrays and objects only gets you part of the way there. We still needed to keep a map in our heads of the structure of the data so we could go into a specific area, traverse it, and change it.&lt;/p&gt;&lt;p&gt;We weren&apos;t using TypeScript at the time, but even if we had been, the challenge would remain of having to navigate the structure from the top down in order to add a new transformation. Type-safety is a huge help, but it only gets you part of the way there to the benefits of localized reasoning.&lt;/p&gt;&lt;h2&gt;What is a Combinator?&lt;/h2&gt;&lt;p&gt;The term Combinator is used because you can &lt;em&gt;combine&lt;/em&gt; the smaller units to build up the whole. A Combinator is the idea of building something up by combining small &quot;base&quot; values into more complex ones. The values could be a JSON Decoder, a syntax parser, a random number generator. The key is that the simplest form can be composed together to build something complex - but (this is important) the simple thing and the complex thing are the same kind of thing!&lt;/p&gt;&lt;p&gt;Combinators share some parallels with the concept of recursion (defining a function in terms of itself).&lt;/p&gt;&lt;pre&gt;&lt;code&gt;fibonacci n =
  if n &amp;lt;= 1 then
    n
  else
    fibonacci ( n - 1 ) + fibonacci ( n - 2 )
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This recursive definition can be read as &lt;em&gt;what&lt;/em&gt; a fibonacci number is (declarative), instead of &lt;em&gt;how&lt;/em&gt; it is calculated (imperative). No mention of &lt;em&gt;how&lt;/em&gt; to loop over in each iteration - it&apos;s simply written in terms of itself, like a math equation. It&apos;s pretty close to how you would teach fibonacci to a human. It just so happens that computers can understand it, too!&lt;/p&gt;&lt;p&gt;Recursion is a good analogy for Combinators:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;A Combinator is declarative (not imperative)&lt;/li&gt;&lt;li&gt;A Combinator is either defined in terms of other Combinators (analogous to a recursive self-invocation), or it is a &quot;base&quot; combinator (analogous to a recursive base case)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Let&apos;s look at an example of a Combinator in Elm. The &lt;code&gt;elm/json&lt;/code&gt; package is how you turn untyped JSON data into typed Elm data (or an &lt;code&gt;Err&lt;/code&gt; &lt;code&gt;Result&lt;/code&gt; value if the structure doesn&apos;t match).&lt;/p&gt;&lt;pre&gt;&lt;code&gt;personDecoder :
    Decoder
        { name : String
        , birthday : Time.Posix
        }
personDecoder =
    Decode.map2
        (\name birthday -&amp;gt;
            { name = name
            , birthday = birthday
            }
        )
        nameDecoder
        birthdayDecoder
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What are &lt;code&gt;nameDecoder&lt;/code&gt; and &lt;code&gt;birthdayDecoder&lt;/code&gt;? They&apos;re both some kind of &lt;code&gt;Decoder&lt;/code&gt;. We&apos;re &lt;em&gt;combining&lt;/em&gt; them. Note that we can think about the Decoders here at a high-level, and drop into the details as needed.&lt;/p&gt;&lt;p&gt;At some point, following our &lt;code&gt;Decoder&lt;/code&gt; definitions we will find a &lt;code&gt;Decoder&lt;/code&gt; that doesn&apos;t just compose Decoders, but directly resolves to a value (similar to our recursive &lt;strong&gt;base case&lt;/strong&gt;).&lt;/p&gt;&lt;pre&gt;&lt;code&gt;nameDecoder : Decoder String
nameDecoder =
    Decode.string
        |&amp;gt; Decode.field &quot;full-name&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;Decode.string&lt;/code&gt; is going to resolve to some value. But we can compose Decoders together in more ways than just combining them or reading JSON values within a JSON property (like &lt;code&gt;&quot;full-name&quot;&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;Another key technique for a Combinator is that we can transform them.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;birthdayDecoder : Decoder Time.Posix
birthdayDecoder =
  Decode.field &quot;birthday-date-time&quot; iso8601DateDecoder
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If we follow the definitions, we finally end up with a direct &quot;base&quot; Decoder for &lt;code&gt;birthdayDecoder&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;iso8601DateDecoder : Decoder Time.Posix
iso8601DateDecoder =
    Decode.string
        |&amp;gt; Decode.andThen
            (\dateTimeString -&amp;gt;
                case iso8601StringToTime dateTimeString of
                    Ok time -&amp;gt;
                        Decode.succeed time

                    Err error -&amp;gt;
                        Decode.fail error
            )
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This time, we are transforming the raw String into an Elm &lt;code&gt;Time.Posix&lt;/code&gt; value. &lt;code&gt;iso8601StringToTime&lt;/code&gt; is a function that takes a &lt;code&gt;String&lt;/code&gt; and gives a &lt;code&gt;Result String Time.Posix&lt;/code&gt;. When we transform the &quot;base&quot; &lt;code&gt;Decoder&lt;/code&gt; (&lt;code&gt;Decode.string&lt;/code&gt;), the type changes with it.&lt;/p&gt;&lt;p&gt;These are some of the basic building blocks of a Combinator. Let&apos;s explore how these building blocks lend themselves to breaking down a problem into smaller pieces, without needing to pull in all the surrounding context.&lt;/p&gt;&lt;h2&gt;Combinators are trees&lt;/h2&gt;&lt;p&gt;With the JS code where I was working with JSON from the server, I was working on the data top-down, and carving out new &quot;seams&quot; to transform data as needed. In contrast, with a Combinator you already have a place to work. You can visualize a Combinator as a tree. You build up a complex Combinator from the bottom up, and you can think locally about any sub-problem (think sub-tree). Each sub-problem is like a little box that you can work in. Once you find the right box, you don&apos;t need to worry about what&apos;s outside of that box, you can just focus on what&apos;s inside the box you&apos;re working on. You don&apos;t need to create a new point to make your transformation, because it already exists.&lt;/p&gt;&lt;p&gt;For example, if we need to normalize the names we&apos;re getting from the server, we just find the right box and work within that.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;nameDecoder : Decoder String
nameDecoder =
    Decode.string
        |&amp;gt; Decode.map normalizeName
        |&amp;gt; Decode.field &quot;full-name&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Or if the name may have a different format, we just make that change within our box, blissfully unaware of the JSON structure surrounding our box.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;nameDecoder : Decoder String
nameDecoder =
    Decode.oneOf [ Decode.string, firstLastDecoder ]
        |&amp;gt; Decode.map normalizeName
        |&amp;gt; Decode.field &quot;full-name&quot;

firstLastDecoder : Decoder String
firstLastDecoder =
    Decode.map2 (++)
        (Decode.field &quot;first&quot;)
        (Decode.field &quot;last&quot;)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Joël Quenneville has a nice visualization of this concept in his article &lt;a href=&quot;https://thoughtbot.com/blog/elms-universal-pattern&quot;&gt;Elm&apos;s Universal Pattern&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;Intermediary Data&lt;/h2&gt;&lt;p&gt;Another quality of transforming data in an imperative style is that it can happen in multiple passes. Each iteration to transform the data can yield an intermediary data format that isn&apos;t useful except as input to the next transformation phase.&lt;/p&gt;&lt;p&gt;By using a Combinator, you can avoid passing data through various transformation stages. A Combinator represents a set of operations/transformations. You don&apos;t actually &lt;em&gt;use&lt;/em&gt; the Combinator until you&apos;ve built it up. If you need to tweak something, you transform the Combinator. So the data is never exposed in your app in an intermediary format.&lt;/p&gt;&lt;p&gt;We are building up both the JSON Decoder and its type information at the same time. Since the types and operations/transformations are in sync, we are guaranteed to either&lt;/p&gt;&lt;ol&gt;&lt;li&gt;End up with well-typed data (happy path), or&lt;/li&gt;&lt;li&gt;End up with a clear error&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;I really love this quality of the Combinator pattern. This gives you explicit, clearly defined paths, with types fully describing the possibilites. Take a look at Alexis King&apos;s article &lt;a href=&quot;https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/&quot;&gt;Parse, Don&apos;t Validate&lt;/a&gt; for some great insights about that.&lt;/p&gt;&lt;h2&gt;Combinators Beyond JSON Decoders&lt;/h2&gt;&lt;p&gt;This pattern isn&apos;t specific to a language, like Elm, or a domain, like JSON.&lt;/p&gt;&lt;p&gt;You can use these same concepts in TypeScript with a library like &lt;a href=&quot;https://github.com/gcanti/io-ts&quot;&gt;&lt;code&gt;io-ts&lt;/code&gt;&lt;/a&gt;. And you can apply this thinking to a lot more problems than JSON. Some examples in the Elm ecosystem:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://package.elm-lang.org/packages/elm/random/latest/&quot;&gt;&lt;code&gt;elm/random&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://package.elm-lang.org/packages/elm/parser/latest/&quot;&gt;&lt;code&gt;elm/parser&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Creating fuzzers (property-based test data) in &lt;a href=&quot;https://package.elm-lang.org/packages/elm-explorations/test/latest/&quot;&gt;&lt;code&gt;elm/test&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/dillonkearns/elm-graphql&quot;&gt;&lt;code&gt;elm-graphql&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://package.elm-lang.org/packages/rtfeldman/elm-validate/latest/&quot;&gt;&lt;code&gt;elm-validate&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Next Post&lt;/h2&gt;&lt;p&gt;Thanks for reading! In the next post, I will share lessons learned about this technique and how it made me realize why I loved one type-safe library I built, and am now rebuilding another to learn these lessons a few years later.&lt;/p&gt;</content>
<content:encoded><![CDATA[<p>I won't go into how immutability, managed effects, or lack of global variables help us reason locally - although they really do! What I want to focus on here is the power of individual transformations composed together, rather than making one big transformation. This concept is sometimes called a Combinator.</p><h2>Top-Down Vs. Bottom-Up Transformations</h2><p>In my JavaScript days, I remember a particularly tricky area of code where we had a big list of data in a format defined by the server. We needed to take product listings, pull out specific options and inventory information, normalize them, and then apply filters from the UI to show/hide and sort search results.</p><p>There were deeply nested fields, and some incongruities in the shape of the data. Some values were nullable. Some had specific normalization we needed to apply to get data from multiple sources to match.</p><p>We had a lot of big unit tests to make sure things were working. Even so, it was so difficult to go in to our series of lodash function calls and find <em>where</em> you needed to make a change. And once you did, you would want to make sure you added several new test cases to make sure you didn't miss a spot or mishandle a special case.</p><p>We would sit in awe as the person most familiar with the codebase correctly traversed the nested arrays and objects to get to the exact right place and make a change on the first try. It was a lot to hold in our heads, and it was quite error prone.</p><p>The challenge was that using that paradigm to normalize JSON data required thinking of the data as a monolith. We certainly abstracted out functions to help with parts of the normalization. And we used lodash to do functional style mapping over the arrays of data and key-value objects. But mapping over arrays and objects only gets you part of the way there. We still needed to keep a map in our heads of the structure of the data so we could go into a specific area, traverse it, and change it.</p><p>We weren't using TypeScript at the time, but even if we had been, the challenge would remain of having to navigate the structure from the top down in order to add a new transformation. Type-safety is a huge help, but it only gets you part of the way there to the benefits of localized reasoning.</p><h2>What is a Combinator?</h2><p>The term Combinator is used because you can <em>combine</em> the smaller units to build up the whole. A Combinator is the idea of building something up by combining small "base" values into more complex ones. The values could be a JSON Decoder, a syntax parser, a random number generator. The key is that the simplest form can be composed together to build something complex - but (this is important) the simple thing and the complex thing are the same kind of thing!</p><p>Combinators share some parallels with the concept of recursion (defining a function in terms of itself).</p><pre><code>fibonacci n =
  if n &lt;= 1 then
    n
  else
    fibonacci ( n - 1 ) + fibonacci ( n - 2 )
</code></pre><p>This recursive definition can be read as <em>what</em> a fibonacci number is (declarative), instead of <em>how</em> it is calculated (imperative). No mention of <em>how</em> to loop over in each iteration - it's simply written in terms of itself, like a math equation. It's pretty close to how you would teach fibonacci to a human. It just so happens that computers can understand it, too!</p><p>Recursion is a good analogy for Combinators:</p><ul><li>A Combinator is declarative (not imperative)</li><li>A Combinator is either defined in terms of other Combinators (analogous to a recursive self-invocation), or it is a "base" combinator (analogous to a recursive base case)</li></ul><p>Let's look at an example of a Combinator in Elm. The <code>elm/json</code> package is how you turn untyped JSON data into typed Elm data (or an <code>Err</code> <code>Result</code> value if the structure doesn't match).</p><pre><code>personDecoder :
    Decoder
        { name : String
        , birthday : Time.Posix
        }
personDecoder =
    Decode.map2
        (\name birthday -&gt;
            { name = name
            , birthday = birthday
            }
        )
        nameDecoder
        birthdayDecoder
</code></pre><p>What are <code>nameDecoder</code> and <code>birthdayDecoder</code>? They're both some kind of <code>Decoder</code>. We're <em>combining</em> them. Note that we can think about the Decoders here at a high-level, and drop into the details as needed.</p><p>At some point, following our <code>Decoder</code> definitions we will find a <code>Decoder</code> that doesn't just compose Decoders, but directly resolves to a value (similar to our recursive <strong>base case</strong>).</p><pre><code>nameDecoder : Decoder String
nameDecoder =
    Decode.string
        |&gt; Decode.field "full-name"
</code></pre><p><code>Decode.string</code> is going to resolve to some value. But we can compose Decoders together in more ways than just combining them or reading JSON values within a JSON property (like <code>"full-name"</code>).</p><p>Another key technique for a Combinator is that we can transform them.</p><pre><code>birthdayDecoder : Decoder Time.Posix
birthdayDecoder =
  Decode.field "birthday-date-time" iso8601DateDecoder
</code></pre><p>If we follow the definitions, we finally end up with a direct "base" Decoder for <code>birthdayDecoder</code>.</p><pre><code>iso8601DateDecoder : Decoder Time.Posix
iso8601DateDecoder =
    Decode.string
        |&gt; Decode.andThen
            (\dateTimeString -&gt;
                case iso8601StringToTime dateTimeString of
                    Ok time -&gt;
                        Decode.succeed time

                    Err error -&gt;
                        Decode.fail error
            )
</code></pre><p>This time, we are transforming the raw String into an Elm <code>Time.Posix</code> value. <code>iso8601StringToTime</code> is a function that takes a <code>String</code> and gives a <code>Result String Time.Posix</code>. When we transform the "base" <code>Decoder</code> (<code>Decode.string</code>), the type changes with it.</p><p>These are some of the basic building blocks of a Combinator. Let's explore how these building blocks lend themselves to breaking down a problem into smaller pieces, without needing to pull in all the surrounding context.</p><h2>Combinators are trees</h2><p>With the JS code where I was working with JSON from the server, I was working on the data top-down, and carving out new "seams" to transform data as needed. In contrast, with a Combinator you already have a place to work. You can visualize a Combinator as a tree. You build up a complex Combinator from the bottom up, and you can think locally about any sub-problem (think sub-tree). Each sub-problem is like a little box that you can work in. Once you find the right box, you don't need to worry about what's outside of that box, you can just focus on what's inside the box you're working on. You don't need to create a new point to make your transformation, because it already exists.</p><p>For example, if we need to normalize the names we're getting from the server, we just find the right box and work within that.</p><pre><code>nameDecoder : Decoder String
nameDecoder =
    Decode.string
        |&gt; Decode.map normalizeName
        |&gt; Decode.field "full-name"
</code></pre><p>Or if the name may have a different format, we just make that change within our box, blissfully unaware of the JSON structure surrounding our box.</p><pre><code>nameDecoder : Decoder String
nameDecoder =
    Decode.oneOf [ Decode.string, firstLastDecoder ]
        |&gt; Decode.map normalizeName
        |&gt; Decode.field "full-name"

firstLastDecoder : Decoder String
firstLastDecoder =
    Decode.map2 (++)
        (Decode.field "first")
        (Decode.field "last")
</code></pre><p>Joël Quenneville has a nice visualization of this concept in his article <a href="https://thoughtbot.com/blog/elms-universal-pattern">Elm's Universal Pattern</a>.</p><h2>Intermediary Data</h2><p>Another quality of transforming data in an imperative style is that it can happen in multiple passes. Each iteration to transform the data can yield an intermediary data format that isn't useful except as input to the next transformation phase.</p><p>By using a Combinator, you can avoid passing data through various transformation stages. A Combinator represents a set of operations/transformations. You don't actually <em>use</em> the Combinator until you've built it up. If you need to tweak something, you transform the Combinator. So the data is never exposed in your app in an intermediary format.</p><p>We are building up both the JSON Decoder and its type information at the same time. Since the types and operations/transformations are in sync, we are guaranteed to either</p><ol><li>End up with well-typed data (happy path), or</li><li>End up with a clear error</li></ol><p>I really love this quality of the Combinator pattern. This gives you explicit, clearly defined paths, with types fully describing the possibilites. Take a look at Alexis King's article <a href="https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/">Parse, Don't Validate</a> for some great insights about that.</p><h2>Combinators Beyond JSON Decoders</h2><p>This pattern isn't specific to a language, like Elm, or a domain, like JSON.</p><p>You can use these same concepts in TypeScript with a library like <a href="https://github.com/gcanti/io-ts"><code>io-ts</code></a>. And you can apply this thinking to a lot more problems than JSON. Some examples in the Elm ecosystem:</p><ul><li><a href="https://package.elm-lang.org/packages/elm/random/latest/"><code>elm/random</code></a></li><li><a href="https://package.elm-lang.org/packages/elm/parser/latest/"><code>elm/parser</code></a></li><li>Creating fuzzers (property-based test data) in <a href="https://package.elm-lang.org/packages/elm-explorations/test/latest/"><code>elm/test</code></a></li><li><a href="https://github.com/dillonkearns/elm-graphql"><code>elm-graphql</code></a></li><li><a href="https://package.elm-lang.org/packages/rtfeldman/elm-validate/latest/"><code>elm-validate</code></a></li></ul><h2>Next Post</h2><p>Thanks for reading! In the next post, I will share lessons learned about this technique and how it made me realize why I loved one type-safe library I built, and am now rebuilding another to learn these lessons a few years later.</p>]]></content:encoded>
</item>
<item>
<title>elm-ts-interop init Command, Codec and Pipeline APIs, and docs site</title>
<description>This week I have some improvements to announce for elm-ts-interop. In particular, I&apos;ve been focused on making it easier to get started with the free Community Edition. And I&apos;ve also shipped some new APIs for building up your type-safe Encoders and Decoders.</description>
<link>https://incrementalelm.com/elm-ts-interop-improvements</link>
<guid>https://incrementalelm.com/elm-ts-interop-improvements</guid>
<pubDate>Mon, 18 Oct 2021 00:00:00 GMT</pubDate>
<content>&lt;h1&gt;&lt;code&gt;elm-ts-interop&lt;/code&gt; init Command, Codec and Pipeline APIs, and docs site&lt;/h1&gt;&lt;p&gt;This week I have some improvements to announce for &lt;code&gt;elm-ts-interop&lt;/code&gt;. In particular, I&apos;ve been focused on making it easier to get started with the free Community Edition. And I&apos;ve also shipped some new APIs for building up your type-safe Encoders and Decoders.&lt;/p&gt;&lt;p&gt;If you&apos;re not familiar with &lt;code&gt;elm-ts-interop&lt;/code&gt;, it&apos;s a tool that generates TypeScript types from your custom JSON Decoders and Encoders to ensure that the types are in sync between your Elm app and TypeScript code. If you&apos;re not using TypeScript in your project, you can even benefit from the improved type-safety using vanilla JavaScript by using JSDoc comments ([&lt;a title=&quot;TypeScript Without Transpilation&quot; href=&quot;typescript-without-transpilation&quot;&gt;typescript-without-transpilation&lt;/a&gt;]).&lt;/p&gt;&lt;p&gt;You can learn more about &lt;a href=&quot;https://elm-ts-interop.com/how-it-works&quot;&gt;how elm-ts-interop works&lt;/a&gt; and find some &lt;a href=&quot;https://elm-ts-interop.com/resources&quot;&gt;learning resources&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;Starter Repo with ViteJS and TypeScript&lt;/h2&gt;&lt;p&gt;There is now &lt;a href=&quot;https://github.com/dillonkearns/elm-ts-interop-starter&quot;&gt;a starter setup for the &lt;code&gt;elm-ts-interop&lt;/code&gt; Community Edition&lt;/a&gt;. It uses a simple ViteJS config to load in Elm and TypeScript code, and it also has an &lt;code&gt;eslint&lt;/code&gt; rule to enforce exhaustive switch statements so you can add a new &lt;code&gt;FromElm&lt;/code&gt; port (Cmd port) and get a type error reminding you to handle that new data in TypeScript. It feels just like adding a new &lt;code&gt;Msg&lt;/code&gt; variant and handling it in your Elm &lt;code&gt;update&lt;/code&gt; function.&lt;/p&gt;&lt;p&gt;The starter repo is at &lt;a href=&quot;https://github.com/dillonkearns/elm-ts-interop-starter&quot;&gt;github.com/dillonkearns/elm-ts-interop-starter&lt;/a&gt;. And if you&apos;re a pro user, you can see the example Pro setup &lt;a href=&quot;https://github.com/dillonkearns/elm-ts-interop-starter/tree/pro&quot;&gt;on the &lt;code&gt;pro&lt;/code&gt; branch&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;elm-ts-interop Community Edition Setup Guide&lt;/h2&gt;&lt;p&gt;You can find the newly published docs which inclue a &lt;a href=&quot;https://elm-ts-interop.com/setup&quot;&gt;thorough walkthrough for how to get started with &lt;code&gt;elm-ts-interop&lt;/code&gt;&lt;/a&gt;. To make it easier to get set up, I&apos;ve also added an &lt;code&gt;elm-ts-interop init&lt;/code&gt; command that will scaffold the two Elm files you need to get started. Try it out and let me know how it goes!&lt;/p&gt;&lt;h2&gt;elm-ts-interop Watch Mode&lt;/h2&gt;&lt;p&gt;You can run &lt;code&gt;elm-ts-interop&lt;/code&gt; with a &lt;code&gt;--watch&lt;/code&gt; flag to have it re-run any time your Elm code changes. That helps speed up the feedback loop and get compiler errors as you change your InteropDefinitions module.&lt;/p&gt;&lt;h2&gt;Codec and Pipeline APIs&lt;/h2&gt;&lt;p&gt;The &lt;a href=&quot;https://package.elm-lang.org/packages/dillonkearns/elm-ts-json/latest/&quot;&gt;&lt;code&gt;elm-ts-json&lt;/code&gt;&lt;/a&gt; Elm package is the secret sauce for getting type information from your Decoders and Encoders. The core API remains the same, but version 2.0 of the package now comes with a &lt;code&gt;Codec&lt;/code&gt; API (thank you miniBill for the great API design!) as well as a port of the &lt;a href=&quot;https://package.elm-lang.org/packages/NoRedInk/elm-json-decode-pipeline/latest/&quot;&gt;&lt;code&gt;NoRedInk/elm-json-decode-pipeline&lt;/code&gt;&lt;/a&gt; API. The Codec API also includes a new addition from miniBill&apos;s Codec API for customizing the names of fields (rather than having custom types encoded as an Array of positional values). Check out the new &lt;a href=&quot;https://package.elm-lang.org/packages/dillonkearns/elm-ts-json/latest/TsJson-Codec&quot;&gt;&lt;code&gt;TsJson.Codec&lt;/code&gt; API&lt;/a&gt; and &lt;a href=&quot;https://package.elm-lang.org/packages/dillonkearns/elm-ts-json/latest/TsJson-Decode-Pipeline&quot;&gt;&lt;code&gt;TsJson.Pipeline&lt;/code&gt; API&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;Simplified Discriminated Union Decoder&lt;/h2&gt;&lt;p&gt;If your using the convention of having a string field that is used to determine the type of decoding to use (Discriminated Unions), there&apos;s now a helper that makes it a lot easier to do that with &lt;code&gt;elm-ts-json&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;import TsJson.Decode as TsDecode

type User
    = Admin { id : Int }
    | Guest

TsDecode.discriminatedUnion &quot;role&quot;
    [ ( &quot;admin&quot;
        , TsDecode.succeed (\id -&amp;gt; Admin { id = id })
        |&amp;gt; TsDecode.andMap (TsDecode.field &quot;id&quot; TsDecode.int)
        )
    , ( &quot;guest&quot;, TsDecode.succeed Guest )
    ]
    |&amp;gt; TsDecode.runExample &quot;&quot;&quot;{&quot;role&quot;: &quot;admin&quot;, &quot;id&quot;: 123}&quot;&quot;&quot;
--&amp;gt; { decoded = Ok (Admin { id = 123 })
--&amp;gt; , tsType = &quot;&quot;&quot;{ id : number; role : &quot;admin&quot; } | { role : &quot;guest&quot; }&quot;&quot;&quot;
--&amp;gt; }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As always, feel free to ask me questions on Twitter &lt;a href=&quot;https://twitter.com/dillontkearns&quot;&gt;@dillontkearns&lt;/a&gt;, or on Slack (we have an &lt;a href=&quot;https://elmlang.slack.com/archives/C01ST485YAU&quot;&gt;#elm-ts-interop channel&lt;/a&gt;). Happy type-safe coding!&lt;/p&gt;</content>
<content:encoded><![CDATA[<h1><code>elm-ts-interop</code> init Command, Codec and Pipeline APIs, and docs site</h1><p>This week I have some improvements to announce for <code>elm-ts-interop</code>. In particular, I've been focused on making it easier to get started with the free Community Edition. And I've also shipped some new APIs for building up your type-safe Encoders and Decoders.</p><p>If you're not familiar with <code>elm-ts-interop</code>, it's a tool that generates TypeScript types from your custom JSON Decoders and Encoders to ensure that the types are in sync between your Elm app and TypeScript code. If you're not using TypeScript in your project, you can even benefit from the improved type-safety using vanilla JavaScript by using JSDoc comments ([<a title="TypeScript Without Transpilation" href="typescript-without-transpilation">typescript-without-transpilation</a>]).</p><p>You can learn more about <a href="https://elm-ts-interop.com/how-it-works">how elm-ts-interop works</a> and find some <a href="https://elm-ts-interop.com/resources">learning resources</a>.</p><h2>Starter Repo with ViteJS and TypeScript</h2><p>There is now <a href="https://github.com/dillonkearns/elm-ts-interop-starter">a starter setup for the <code>elm-ts-interop</code> Community Edition</a>. It uses a simple ViteJS config to load in Elm and TypeScript code, and it also has an <code>eslint</code> rule to enforce exhaustive switch statements so you can add a new <code>FromElm</code> port (Cmd port) and get a type error reminding you to handle that new data in TypeScript. It feels just like adding a new <code>Msg</code> variant and handling it in your Elm <code>update</code> function.</p><p>The starter repo is at <a href="https://github.com/dillonkearns/elm-ts-interop-starter">github.com/dillonkearns/elm-ts-interop-starter</a>. And if you're a pro user, you can see the example Pro setup <a href="https://github.com/dillonkearns/elm-ts-interop-starter/tree/pro">on the <code>pro</code> branch</a>.</p><h2>elm-ts-interop Community Edition Setup Guide</h2><p>You can find the newly published docs which inclue a <a href="https://elm-ts-interop.com/setup">thorough walkthrough for how to get started with <code>elm-ts-interop</code></a>. To make it easier to get set up, I've also added an <code>elm-ts-interop init</code> command that will scaffold the two Elm files you need to get started. Try it out and let me know how it goes!</p><h2>elm-ts-interop Watch Mode</h2><p>You can run <code>elm-ts-interop</code> with a <code>--watch</code> flag to have it re-run any time your Elm code changes. That helps speed up the feedback loop and get compiler errors as you change your InteropDefinitions module.</p><h2>Codec and Pipeline APIs</h2><p>The <a href="https://package.elm-lang.org/packages/dillonkearns/elm-ts-json/latest/"><code>elm-ts-json</code></a> Elm package is the secret sauce for getting type information from your Decoders and Encoders. The core API remains the same, but version 2.0 of the package now comes with a <code>Codec</code> API (thank you miniBill for the great API design!) as well as a port of the <a href="https://package.elm-lang.org/packages/NoRedInk/elm-json-decode-pipeline/latest/"><code>NoRedInk/elm-json-decode-pipeline</code></a> API. The Codec API also includes a new addition from miniBill's Codec API for customizing the names of fields (rather than having custom types encoded as an Array of positional values). Check out the new <a href="https://package.elm-lang.org/packages/dillonkearns/elm-ts-json/latest/TsJson-Codec"><code>TsJson.Codec</code> API</a> and <a href="https://package.elm-lang.org/packages/dillonkearns/elm-ts-json/latest/TsJson-Decode-Pipeline"><code>TsJson.Pipeline</code> API</a>.</p><h2>Simplified Discriminated Union Decoder</h2><p>If your using the convention of having a string field that is used to determine the type of decoding to use (Discriminated Unions), there's now a helper that makes it a lot easier to do that with <code>elm-ts-json</code>.</p><pre><code>import TsJson.Decode as TsDecode

type User
    = Admin { id : Int }
    | Guest

TsDecode.discriminatedUnion "role"
    [ ( "admin"
        , TsDecode.succeed (\id -&gt; Admin { id = id })
        |&gt; TsDecode.andMap (TsDecode.field "id" TsDecode.int)
        )
    , ( "guest", TsDecode.succeed Guest )
    ]
    |&gt; TsDecode.runExample """{"role": "admin", "id": 123}"""
--&gt; { decoded = Ok (Admin { id = 123 })
--&gt; , tsType = """{ id : number; role : "admin" } | { role : "guest" }"""
--&gt; }
</code></pre><p>As always, feel free to ask me questions on Twitter <a href="https://twitter.com/dillontkearns">@dillontkearns</a>, or on Slack (we have an <a href="https://elmlang.slack.com/archives/C01ST485YAU">#elm-ts-interop channel</a>). Happy type-safe coding!</p>]]></content:encoded>
</item>
<item>
<title>Using elm types to prevent logging social security #&apos;s</title>
<description>One of the most successful techniques I&apos;ve seen for making sure you don&apos;t break elm code the next time you touch it is a technique I call an Exit Gatekeeper.</description>
<link>https://incrementalelm.com/exit-gatekeepers</link>
<guid>https://incrementalelm.com/exit-gatekeepers</guid>
<pubDate>Wed, 01 Jan 2020 00:00:00 GMT</pubDate>
<content>&lt;p&gt;Let&apos;s say you have these innocent functions in your app. How do you know that you won&apos;t get your wires crossed and log a user&apos;s social security number?&lt;/p&gt;&lt;pre&gt;&lt;code&gt;securelySaveSSN : String -&amp;gt; Cmd Msg

reportError : String -&amp;gt; Cmd Msg
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You might wrap it in a type wrapper like so:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;module SSN exposing (SSN(..))

type SSN = SSN String
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;securelySaveSSN : SSN -&amp;gt; Cmd Msg

reportError : String -&amp;gt; Cmd Msg
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;SSN&lt;/code&gt; type wrapper is a good start. But how do you know it won&apos;t be unwrapped and passed around somewhere where it could mistakenly be misused?&lt;/p&gt;&lt;pre&gt;&lt;code&gt;storeSSN : SSN -&amp;gt; Cmd Msg
storeSSN (SSN rawSsn) =
    genericSendData (ssnPayload rawSsn) saveSsnEndpoint

genericSendData : Json.Encode.Value -&amp;gt; String -&amp;gt; Cmd Msg
genericSendData payload endpoint =
-- generic data sending function
-- if there&apos;s an HTTP error, it sends the payload
-- and error to our error reporting service
-- ⚠️ Not good for SSNs!
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Whoops, somebody forgot that we had a special &lt;code&gt;securelySaveSSN&lt;/code&gt; function that encrypts the SSN and masks the SSN when reporting errors. Do you dare look at the commit history? It could well have been your past self (we&apos;ve all been there)!&lt;/p&gt;&lt;p&gt;Humans make mistakes, so let&apos;s not expect them to be perfect. The core issue here is that the &lt;code&gt;SSN&lt;/code&gt; type wrapper has failed to communicate the limits of how we want it to be used. It&apos;s merely a convention to use &lt;code&gt;securelySaveSSN&lt;/code&gt; instead of calling the generic &lt;code&gt;genericSendData&lt;/code&gt; with the raw String. In this article, you&apos;ll learn a technique that gets the elm compiler to help guide us towards using data as intended: Exit Gatekeepers.&lt;/p&gt;&lt;h2&gt;🔑 Exit Gatekeepers&lt;/h2&gt;&lt;p&gt;So how do we make sure we don&apos;t log, Tweet, or otherwise misuse the user&apos;s SSN? We control the exits.&lt;/p&gt;&lt;p&gt;There are two ways for the raw data to exit. If raw data exits, then we don&apos;t have control over it. So we want to close off these two exit routes.&lt;/p&gt;&lt;h2&gt;🔓 Unsecure Exit 1 - Public Constructor&lt;/h2&gt;&lt;p&gt;If you expose the constructor, then we can pattern match to get the raw SSN. This means that enforcing the rules for how we want to use SSNs leaks out all over our code instead of being in one central place that we can easily maintain.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;-- the (..) exposes the constructor
module SSN exposing (SSN(..))
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;🔓 Unsecure Exit 2 - Public Accessor&lt;/h2&gt;&lt;p&gt;Similarly, you can unwrap the raw SSN directly from outside the module if we expose an accessor (also known as getters) which returns the /raw data/. In this case, our primitive representation of the SSN is a String, so we could have an unsecure exit by exposing a &lt;code&gt;toString&lt;/code&gt; accessor.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;module SSN exposing (SSN, toString)

toString : SSN -&amp;gt; String
toString (SSN rawSsn) = rawSsn
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The public accessor function has the same effect as our publicly exposed constructor did, allowing us to accidentally pass the raw data to our &lt;code&gt;genericSendData&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;storeSsn : SSN -&amp;gt; Cmd Msg
storeSsn ssn =
    genericSendData (ssnPayload (SSN.toString ssn)) saveSsnEndpoint
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;The role of a gatekeeper&lt;/h2&gt;&lt;p&gt;Think of a Gatekeeper like the Model in Model-View-Controller frameworks. The Model acts as a gatekeeper that ensures the integrity of all persistence in our app. Similarly, our Exit Gatekeeper ensures the integrity of a Domain concept (SSNs in this case) throughout our app.&lt;/p&gt;&lt;h2&gt;How to control the exits&lt;/h2&gt;&lt;p&gt;To add an Exit Gatekeeper, all we need to do is define every function needed to use SSNs internally within the &lt;code&gt;SSN&lt;/code&gt; module. And of course, each of those functions is responsible for using it appropriately. (And on the other side of that coin, that means that the calling code is free of that responsibility!).&lt;/p&gt;&lt;p&gt;Let&apos;s make a function to securely send an SSN. We need to guarantee that:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The SSN is encrypted using the proper key&lt;/li&gt;&lt;li&gt;It is sent to the correct endpoint&lt;/li&gt;&lt;li&gt;It is sent with https&lt;/li&gt;&lt;li&gt;It is masked before being being sent to our error reporting&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;We don&apos;t want to check for all those things everywhere we call this code every time. We want to be able to make sure the code in this module is good whenever it changes, and then completely trust it from the calling code.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;module SSN exposing (SSN)

securelySendSsn : Ssn -&amp;gt; Http.Request
securelySendSsn ssn =
    Http.post
    { url = &quot;https://yoursecuresite.com/secure-endpoint&quot;
    , body = encryptedSsnBody ssn,
    , expect = ...
    }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we can be confident that the calling code will never mistakenly send SSNs to the wrong endpoint, or forget to encrypt them!&lt;/p&gt;&lt;h2&gt;Displaying the SSN&lt;/h2&gt;&lt;p&gt;What if you only want to display the last 4 digits of the SSN? How do you make sure that you, your team members, and your future self all remember to do that?&lt;/p&gt;&lt;p&gt;You could vigilantly put that in a code review checklist, or come up with all sorts of creative heuristics to avoid that mistake. I like to reach for the Exit Gatekeeper pattern as my first choice. Then you need to check very carefully any time you are modifying the SSN module itself, and you can trust the module and treat it as a blackbox when you&apos;re not modifying it.&lt;/p&gt;&lt;p&gt;It&apos;s very likely that you&apos;ll miss something if you have to think about where SSNs are used throughout your codebase. But it&apos;s quite manageable to keep the entire SSN module in your head and feel confident that you&apos;re not forgetting anything important.&lt;/p&gt;&lt;p&gt;Here&apos;s a simple implementation of our last 4 digits view:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;module SSN exposing (SSN)

lastFourView : SSN -&amp;gt; Html msg
lastFourView ssn =
    Html.text (&quot;xxx-xx-&quot; ++ lastFour ssn)
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Takeaways&lt;/h2&gt;&lt;p&gt;You can start applying the Exit Gatekeeper pattern to your elm code right away!&lt;/p&gt;&lt;p&gt;Here are some steps you can apply:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Notice some data in your codebase that you have to be careful to use safely or correctly&lt;/li&gt;&lt;li&gt;Wrap it in a Custom Type (if you haven&apos;t already)&lt;/li&gt;&lt;li&gt;Expose the constructor at first to make the change small and manageable&lt;/li&gt;&lt;li&gt;Get everything compiling and committed!&lt;/li&gt;&lt;li&gt;One by one, copy each function that is consuming your new Custom Type and call it from the new module&lt;/li&gt;&lt;li&gt;Once that&apos;s done, you can now hide the constructor, and you now have a proper Exit Gatekeeper for your type!&lt;/li&gt;&lt;/ol&gt;</content>
<content:encoded><![CDATA[<p>Let's say you have these innocent functions in your app. How do you know that you won't get your wires crossed and log a user's social security number?</p><pre><code>securelySaveSSN : String -&gt; Cmd Msg

reportError : String -&gt; Cmd Msg
</code></pre><p>You might wrap it in a type wrapper like so:</p><pre><code>module SSN exposing (SSN(..))

type SSN = SSN String
</code></pre><pre><code>securelySaveSSN : SSN -&gt; Cmd Msg

reportError : String -&gt; Cmd Msg
</code></pre><p>The <code>SSN</code> type wrapper is a good start. But how do you know it won't be unwrapped and passed around somewhere where it could mistakenly be misused?</p><pre><code>storeSSN : SSN -&gt; Cmd Msg
storeSSN (SSN rawSsn) =
    genericSendData (ssnPayload rawSsn) saveSsnEndpoint

genericSendData : Json.Encode.Value -&gt; String -&gt; Cmd Msg
genericSendData payload endpoint =
-- generic data sending function
-- if there's an HTTP error, it sends the payload
-- and error to our error reporting service
-- ⚠️ Not good for SSNs!
</code></pre><p>Whoops, somebody forgot that we had a special <code>securelySaveSSN</code> function that encrypts the SSN and masks the SSN when reporting errors. Do you dare look at the commit history? It could well have been your past self (we've all been there)!</p><p>Humans make mistakes, so let's not expect them to be perfect. The core issue here is that the <code>SSN</code> type wrapper has failed to communicate the limits of how we want it to be used. It's merely a convention to use <code>securelySaveSSN</code> instead of calling the generic <code>genericSendData</code> with the raw String. In this article, you'll learn a technique that gets the elm compiler to help guide us towards using data as intended: Exit Gatekeepers.</p><h2>🔑 Exit Gatekeepers</h2><p>So how do we make sure we don't log, Tweet, or otherwise misuse the user's SSN? We control the exits.</p><p>There are two ways for the raw data to exit. If raw data exits, then we don't have control over it. So we want to close off these two exit routes.</p><h2>🔓 Unsecure Exit 1 - Public Constructor</h2><p>If you expose the constructor, then we can pattern match to get the raw SSN. This means that enforcing the rules for how we want to use SSNs leaks out all over our code instead of being in one central place that we can easily maintain.</p><pre><code>-- the (..) exposes the constructor
module SSN exposing (SSN(..))
</code></pre><h2>🔓 Unsecure Exit 2 - Public Accessor</h2><p>Similarly, you can unwrap the raw SSN directly from outside the module if we expose an accessor (also known as getters) which returns the /raw data/. In this case, our primitive representation of the SSN is a String, so we could have an unsecure exit by exposing a <code>toString</code> accessor.</p><pre><code>module SSN exposing (SSN, toString)

toString : SSN -&gt; String
toString (SSN rawSsn) = rawSsn
</code></pre><p>The public accessor function has the same effect as our publicly exposed constructor did, allowing us to accidentally pass the raw data to our <code>genericSendData</code>.</p><pre><code>storeSsn : SSN -&gt; Cmd Msg
storeSsn ssn =
    genericSendData (ssnPayload (SSN.toString ssn)) saveSsnEndpoint
</code></pre><h2>The role of a gatekeeper</h2><p>Think of a Gatekeeper like the Model in Model-View-Controller frameworks. The Model acts as a gatekeeper that ensures the integrity of all persistence in our app. Similarly, our Exit Gatekeeper ensures the integrity of a Domain concept (SSNs in this case) throughout our app.</p><h2>How to control the exits</h2><p>To add an Exit Gatekeeper, all we need to do is define every function needed to use SSNs internally within the <code>SSN</code> module. And of course, each of those functions is responsible for using it appropriately. (And on the other side of that coin, that means that the calling code is free of that responsibility!).</p><p>Let's make a function to securely send an SSN. We need to guarantee that:</p><ul><li>The SSN is encrypted using the proper key</li><li>It is sent to the correct endpoint</li><li>It is sent with https</li><li>It is masked before being being sent to our error reporting</li></ul><p>We don't want to check for all those things everywhere we call this code every time. We want to be able to make sure the code in this module is good whenever it changes, and then completely trust it from the calling code.</p><pre><code>module SSN exposing (SSN)

securelySendSsn : Ssn -&gt; Http.Request
securelySendSsn ssn =
    Http.post
    { url = "https://yoursecuresite.com/secure-endpoint"
    , body = encryptedSsnBody ssn,
    , expect = ...
    }
</code></pre><p>Now we can be confident that the calling code will never mistakenly send SSNs to the wrong endpoint, or forget to encrypt them!</p><h2>Displaying the SSN</h2><p>What if you only want to display the last 4 digits of the SSN? How do you make sure that you, your team members, and your future self all remember to do that?</p><p>You could vigilantly put that in a code review checklist, or come up with all sorts of creative heuristics to avoid that mistake. I like to reach for the Exit Gatekeeper pattern as my first choice. Then you need to check very carefully any time you are modifying the SSN module itself, and you can trust the module and treat it as a blackbox when you're not modifying it.</p><p>It's very likely that you'll miss something if you have to think about where SSNs are used throughout your codebase. But it's quite manageable to keep the entire SSN module in your head and feel confident that you're not forgetting anything important.</p><p>Here's a simple implementation of our last 4 digits view:</p><pre><code>module SSN exposing (SSN)

lastFourView : SSN -&gt; Html msg
lastFourView ssn =
    Html.text ("xxx-xx-" ++ lastFour ssn)
</code></pre><h2>Takeaways</h2><p>You can start applying the Exit Gatekeeper pattern to your elm code right away!</p><p>Here are some steps you can apply:</p><ol><li>Notice some data in your codebase that you have to be careful to use safely or correctly</li><li>Wrap it in a Custom Type (if you haven't already)</li><li>Expose the constructor at first to make the change small and manageable</li><li>Get everything compiling and committed!</li><li>One by one, copy each function that is consuming your new Custom Type and call it from the new module</li><li>Once that's done, you can now hide the constructor, and you now have a proper Exit Gatekeeper for your type!</li></ol>]]></content:encoded>
</item>
<item>
<title>Frame Then Fill In</title>
<description>How do you get started on a 1,000-piece jigsaw puzzle? I&apos;ll bet you get the obvious pieces out of the way quickly: the corners and edges. It turns out that your jigsaw puzzle strategy can teach you a lot about how to write elm code effectively and easily!</description>
<link>https://incrementalelm.com/frame-then-fill-in</link>
<guid>https://incrementalelm.com/frame-then-fill-in</guid>
<pubDate>Sat, 23 Mar 2019 00:00:00 GMT</pubDate>
<content>&lt;h1&gt;Frame Then Fill In&lt;/h1&gt;&lt;p&gt;How do you get started on a 1,000-piece jigsaw puzzle? I&apos;ll bet you get the obvious pieces out of the way quickly: the corners and edges. It turns out that your jigsaw puzzle strategy can teach you a lot about how to write elm code effectively and easily!&lt;/p&gt;&lt;h2&gt;The Jigsaw Approach - Frame Then Fill In&lt;/h2&gt;&lt;p&gt;Once you&apos;ve built out the Frame of a jigsaw puzzle, you&apos;ve actually accomplished a lot in very little time! You&apos;ve placed less than 10% of the 1,000 pieces. But in starting with those pieces, you&apos;ve gained several advantages:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;You placed those pieces very quickly.&lt;/li&gt;&lt;li&gt;You now have a frame of reference that will make it faster to place the remaining pieces.&lt;/li&gt;&lt;li&gt;You know the scale of the entire puzzle so you can anchor things like &quot;halfway down&quot; or &quot;at the very top.&quot;&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Frame then Fill In your code&lt;/h2&gt;&lt;p&gt;What&apos;s the equivalent of a puzzle&apos;s Frame in elm code?&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Type definitions&lt;/li&gt;&lt;li&gt;Type annotations&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;In other words, Types! Types create a Frame for your elm code, giving you a reference point that provides feedback as you work through the details.&lt;/p&gt;&lt;h2&gt;Frame Then Fill In a JSON Decoder&lt;/h2&gt;&lt;p&gt;How do you build up a JSON Decoder using the Frame then Fill In technique? Let&apos;s walk through the steps.&lt;/p&gt;&lt;p&gt;You&apos;re given a JSON endpoint which gives you a list of Attendees for an Event. Attendees always provide their first name, but may not have provided their last name. Here&apos;s the Frame for Attendees.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;type alias Attendee =
    { first : String
    , last : Maybe String
    }

decoder : Decoder (List Attendee)
decoder =
    Debug.todo &quot;Implement this&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With this tiny step, you&apos;ve just built a compiling Frame! This gives you exactly the same benefits as with the jigsaw&apos;s Frame:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;You could write the Frame (type definition and type annotation) far faster than the details for it.&lt;/li&gt;&lt;li&gt;You now have a frame of reference that gives you feedback if you take a step in the wrong direction.&lt;/li&gt;&lt;li&gt;You know the general shape, now you just have to fill in the details.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Avoid intertwining Framing and Filling In. You&apos;ll move a lot faster that way.&lt;/p&gt;&lt;h2&gt;Fill In the Frame As Simply As Possible&lt;/h2&gt;&lt;p&gt;You have a special advantage in elm that you don&apos;t get with the jigsaw puzzle. The puzzle only has a single Frame. But with your elm code, you can break out several sub-problems and use Frame and Fill In to solve each sub-problem. So let&apos;s take the smallest step to Fill In our Frame. That way we can create a &quot;Sub-Frame&quot; to move through the rest of the problem even faster!&lt;/p&gt;&lt;p&gt;Your code is compiling and crashing (as is often the case with a Frame). So let&apos;s take the smallest step to get it compiling and not crashing. What&apos;s the Shortest Path there? It&apos;s so obvious you may not even think of it!&lt;/p&gt;&lt;pre&gt;&lt;code&gt;decoder : Decoder (List Attendee)
decoder =
    Decode.succeed []
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It would have been a longer path if you tried to actually get a Decoder for the Attendee. This small step is like a waypoint, allowing you to get feedback before venturing out on the next small step.&lt;/p&gt;&lt;h2&gt;Frame the Next Sub-Problem&lt;/h2&gt;&lt;p&gt;You&apos;ll need to decode a single Attendee. So you have a &quot;puzzle&quot; (the next sub-problem). Now you just need a Frame!&lt;/p&gt;&lt;pre&gt;&lt;code&gt;decoder : Decoder (List Attendee)
decoder =
    Decode.list attendeeDecoder

attendeeDecoder : Decoder Attendee
attendeeDecoder =
    Debug.todo &quot;TODO&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Again, this Frame is compiling but crashing. So again, the next step is to Fill In the Frame as simply as possible to get it compiling and not crashing.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;attendeeDecoder : Decoder Attendee
attendeeDecoder =
    Decode.succeed { first = &quot;John&quot;, last = Nothing }
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Completing the Frame&lt;/h2&gt;&lt;p&gt;You&apos;ve just got one more step before you have your complete jigsaw Frame. After that, you&apos;ll just have to fill in the details!&lt;/p&gt;&lt;pre&gt;&lt;code&gt;attendeeDecoder : Decoder Attendee
attendeeDecoder =
    Decode.map2 Attendee firstNameDecoder lastNameDecoder

firstNameDecoder : Decoder String
firstNameDecoder =
    Decode.succeed &quot;John&quot;

lastNameDecoder : Decoder (Maybe String)
lastNameDecoder =
    Decode.succeed Nothing
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Finally, Just Fill In the Blanks&lt;/h2&gt;&lt;p&gt;At this point, you&apos;ve done the &quot;easy part&quot;. The 10% of the puzzle that makes the other 90% way faster and easier. You&apos;ve built your Frame. Now you just need to get the details right for the first and last name decoders. But you could actually run this decoder and see whether you get back the correct number of Attendees. They would all be named John for now, but hey, you would know that you&apos;re on the right track!&lt;/p&gt;</content>
<content:encoded><![CDATA[<h1>Frame Then Fill In</h1><p>How do you get started on a 1,000-piece jigsaw puzzle? I'll bet you get the obvious pieces out of the way quickly: the corners and edges. It turns out that your jigsaw puzzle strategy can teach you a lot about how to write elm code effectively and easily!</p><h2>The Jigsaw Approach - Frame Then Fill In</h2><p>Once you've built out the Frame of a jigsaw puzzle, you've actually accomplished a lot in very little time! You've placed less than 10% of the 1,000 pieces. But in starting with those pieces, you've gained several advantages:</p><ul><li>You placed those pieces very quickly.</li><li>You now have a frame of reference that will make it faster to place the remaining pieces.</li><li>You know the scale of the entire puzzle so you can anchor things like "halfway down" or "at the very top."</li></ul><h2>Frame then Fill In your code</h2><p>What's the equivalent of a puzzle's Frame in elm code?</p><ul><li>Type definitions</li><li>Type annotations</li></ul><p>In other words, Types! Types create a Frame for your elm code, giving you a reference point that provides feedback as you work through the details.</p><h2>Frame Then Fill In a JSON Decoder</h2><p>How do you build up a JSON Decoder using the Frame then Fill In technique? Let's walk through the steps.</p><p>You're given a JSON endpoint which gives you a list of Attendees for an Event. Attendees always provide their first name, but may not have provided their last name. Here's the Frame for Attendees.</p><pre><code>type alias Attendee =
    { first : String
    , last : Maybe String
    }

decoder : Decoder (List Attendee)
decoder =
    Debug.todo "Implement this"
</code></pre><p>With this tiny step, you've just built a compiling Frame! This gives you exactly the same benefits as with the jigsaw's Frame:</p><ul><li>You could write the Frame (type definition and type annotation) far faster than the details for it.</li><li>You now have a frame of reference that gives you feedback if you take a step in the wrong direction.</li><li>You know the general shape, now you just have to fill in the details.</li></ul><p>Avoid intertwining Framing and Filling In. You'll move a lot faster that way.</p><h2>Fill In the Frame As Simply As Possible</h2><p>You have a special advantage in elm that you don't get with the jigsaw puzzle. The puzzle only has a single Frame. But with your elm code, you can break out several sub-problems and use Frame and Fill In to solve each sub-problem. So let's take the smallest step to Fill In our Frame. That way we can create a "Sub-Frame" to move through the rest of the problem even faster!</p><p>Your code is compiling and crashing (as is often the case with a Frame). So let's take the smallest step to get it compiling and not crashing. What's the Shortest Path there? It's so obvious you may not even think of it!</p><pre><code>decoder : Decoder (List Attendee)
decoder =
    Decode.succeed []
</code></pre><p>It would have been a longer path if you tried to actually get a Decoder for the Attendee. This small step is like a waypoint, allowing you to get feedback before venturing out on the next small step.</p><h2>Frame the Next Sub-Problem</h2><p>You'll need to decode a single Attendee. So you have a "puzzle" (the next sub-problem). Now you just need a Frame!</p><pre><code>decoder : Decoder (List Attendee)
decoder =
    Decode.list attendeeDecoder

attendeeDecoder : Decoder Attendee
attendeeDecoder =
    Debug.todo "TODO"
</code></pre><p>Again, this Frame is compiling but crashing. So again, the next step is to Fill In the Frame as simply as possible to get it compiling and not crashing.</p><pre><code>attendeeDecoder : Decoder Attendee
attendeeDecoder =
    Decode.succeed { first = "John", last = Nothing }
</code></pre><h2>Completing the Frame</h2><p>You've just got one more step before you have your complete jigsaw Frame. After that, you'll just have to fill in the details!</p><pre><code>attendeeDecoder : Decoder Attendee
attendeeDecoder =
    Decode.map2 Attendee firstNameDecoder lastNameDecoder

firstNameDecoder : Decoder String
firstNameDecoder =
    Decode.succeed "John"

lastNameDecoder : Decoder (Maybe String)
lastNameDecoder =
    Decode.succeed Nothing
</code></pre><h2>Finally, Just Fill In the Blanks</h2><p>At this point, you've done the "easy part". The 10% of the puzzle that makes the other 90% way faster and easier. You've built your Frame. Now you just need to get the details right for the first and last name decoders. But you could actually run this decoder and see whether you get back the correct number of Attendees. They would all be named John for now, but hey, you would know that you're on the right track!</p>]]></content:encoded>
</item>
<item>
<title>What Does &quot;If It Compiles, It Works&quot; Really Mean?</title>
<description>Does Elm fill in your business logic and build your app for you, or fix bugs in your code&apos;s logic? If not, then how can it be that &quot;if it compiles, it works?&quot;</description>
<link>https://incrementalelm.com/if-it-compiles-it-works</link>
<guid>https://incrementalelm.com/if-it-compiles-it-works</guid>
<pubDate>Mon, 20 Dec 2021 00:00:00 GMT</pubDate>
<content>&lt;h1&gt;What Does &quot;If It Compiles, It Works&quot; Really Mean?&lt;/h1&gt;&lt;p&gt;Does Elm fill in your business logic and build your app for you, or fix bugs in your code&apos;s logic? If not, then how can it be that &quot;if it compiles, it works?&quot;&lt;/p&gt;&lt;h2&gt;Finding the boundaries&lt;/h2&gt;&lt;p&gt;Let&apos;s set a bounding box around this statement as a starting point. I think we can agree that on the one extreme, Elm does not fix your bugs or write your business logic for you. So if it means anything, the phrase &quot;if it compiles, it works&quot; at least has a limit to its scope.&lt;/p&gt;&lt;p&gt;On the other extreme, when our Elm code is compiling there are some basic things that we expect (at least in practice):&lt;/p&gt;&lt;ul&gt;&lt;li&gt;No runtime exceptions (possible, but rare enough for us to be confident)&lt;/li&gt;&lt;li&gt;Types are what they say they are (we can&apos;t fool the Elm type system to bypass typechecking)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;So &quot;if it compiles, it works&quot; is somewhere between meaningless and true without caveats.&lt;/p&gt;&lt;h2&gt;If It Compiles, The Refactoring Worked&lt;/h2&gt;&lt;p&gt;People often talk about the experience of safe refactoring in Elm. You can do a sweeping change and feel confident that you didn&apos;t break anything once you get it compiling again.&lt;/p&gt;&lt;h2&gt;If It Compiles, Every Case Is Handled&lt;/h2&gt;&lt;p&gt;Even though Elm doesn&apos;t write our business logic for us, it does have a lot of tools to remind us of cases to consider. When you add new functionality, it&apos;s a common experience to have the compiler walk you through several places that now have an inexhaustive case expression. You can bypass this, so it doesn&apos;t come for free. It&apos;s our job to use the tools that Elm gives us so the compiler can safely guide us through adding new functionality (see [&lt;a title=&quot;When It Compiles, But Doesn&apos;t Work&quot; href=&quot;when-it-compiles-but-doesnt-work&quot;&gt;when-it-compiles-but-doesnt-work&lt;/a&gt;]).&lt;/p&gt;&lt;p&gt;Opaque Types also help us feel confident about our wiring beyond just having the right type of data. It can help give meaning to this data, and to give us guarantees about things like the origin of the data and how/where the data is used. See [&lt;a title=&quot;Using elm types to prevent logging social security #&apos;s&quot; href=&quot;exit-gatekeepers&quot;&gt;exit-gatekeepers&lt;/a&gt;] and [&lt;a title=&quot;Entry Gatekeepers&quot; href=&quot;entry-gatekeepers&quot;&gt;entry-gatekeepers&lt;/a&gt;].&lt;/p&gt;&lt;h2&gt;If It Compiles, It&apos;s Wired Correctly&lt;/h2&gt;&lt;p&gt;Can working from a clean slate give us the feeling that if it compiles it works? If Elm isn&apos;t writing our code for us, then it can&apos;t get us all the way there. But I have often experienced the feeling of writing an Elm custom type, being sure to wire it up (through &lt;code&gt;update&lt;/code&gt;, &lt;code&gt;view&lt;/code&gt;, etc. - of course it won&apos;t work if I forget that), and then once it&apos;s wired in it does seem to &quot;just work.&quot;&lt;/p&gt;&lt;p&gt;My personal experience with this is that it applies to wiring, which in other languages is often something that I have to take a lot of care to get right and spend a lot of time debugging as I build features from scratch. When it comes to building up sophisticated business logic, I start by writing unit tests and iterate on that, so that&apos;s far from &quot;if it compiles, it works.&quot; It&apos;s more of an iterative process where it goes through several stages of completion where it&apos;s compiling but doesn&apos;t handle all of the cases (it only does enough to make my test cases pass).&lt;/p&gt;&lt;p&gt;So for me, the phrase &quot;if it compiles, it works&quot; doesn&apos;t apply to writing my business logic from scratch, but it does apply to the wiring and that in turn lets me focus on my business logic more.&lt;/p&gt;&lt;p&gt;If you use tools to help you wire external systems safely to your Elm code ([&lt;a title=&quot;Types Without Borders&quot; href=&quot;types-without-borders&quot;&gt;types-without-borders&lt;/a&gt;]), then you can extend this confidence about correct wiring to the parts of your code that communicate with external systems.&lt;/p&gt;&lt;h2&gt;If The Tests Pass, It Works&lt;/h2&gt;&lt;p&gt;When I&apos;m writing my unit tests in Elm, I also feel more confident that &quot;if my tests pass, it works.&quot; Why would this be different in Elm than working in other languages? One major factor is that Elm doesn&apos;t need mocks (nor does it have the ability to do mocks since it&apos;s not a dynamic language). You can use dependency injection. In fact, this is baked into the paradigm with things like random numbers, the current date, or other non-deterministic data: the only way to get it is to receive it in &lt;code&gt;update&lt;/code&gt; from the Elm runtime.&lt;/p&gt;&lt;p&gt;Because I know my Elm code doesn&apos;t depend on global environment, implicit side effects, and non-deterministic functions, I can trust the green I get when my unit tests pass. Testing that an input gives an output removes many testing pitfalls and painpoints. The Elm language makes testing easy and safe, so that&apos;s all the more reason to test your Elm code.&lt;/p&gt;&lt;p&gt;That&apos;s also a good reminder that type-safety is a complement to automated tests, not a replacement for it. I can say for certain that my confidence changing my Elm applications would be very low if I didn&apos;t have automated tests.&lt;/p&gt;&lt;h2&gt;What Do You Think&lt;/h2&gt;&lt;p&gt;What does &quot;If it compiles, it works&quot; mean to you? And what are the limits? When does it compile, but not work? I&apos;d love to hear your experiences.&lt;/p&gt;</content>
<content:encoded><![CDATA[<h1>What Does "If It Compiles, It Works" Really Mean?</h1><p>Does Elm fill in your business logic and build your app for you, or fix bugs in your code's logic? If not, then how can it be that "if it compiles, it works?"</p><h2>Finding the boundaries</h2><p>Let's set a bounding box around this statement as a starting point. I think we can agree that on the one extreme, Elm does not fix your bugs or write your business logic for you. So if it means anything, the phrase "if it compiles, it works" at least has a limit to its scope.</p><p>On the other extreme, when our Elm code is compiling there are some basic things that we expect (at least in practice):</p><ul><li>No runtime exceptions (possible, but rare enough for us to be confident)</li><li>Types are what they say they are (we can't fool the Elm type system to bypass typechecking)</li></ul><p>So "if it compiles, it works" is somewhere between meaningless and true without caveats.</p><h2>If It Compiles, The Refactoring Worked</h2><p>People often talk about the experience of safe refactoring in Elm. You can do a sweeping change and feel confident that you didn't break anything once you get it compiling again.</p><h2>If It Compiles, Every Case Is Handled</h2><p>Even though Elm doesn't write our business logic for us, it does have a lot of tools to remind us of cases to consider. When you add new functionality, it's a common experience to have the compiler walk you through several places that now have an inexhaustive case expression. You can bypass this, so it doesn't come for free. It's our job to use the tools that Elm gives us so the compiler can safely guide us through adding new functionality (see [<a title="When It Compiles, But Doesn't Work" href="when-it-compiles-but-doesnt-work">when-it-compiles-but-doesnt-work</a>]).</p><p>Opaque Types also help us feel confident about our wiring beyond just having the right type of data. It can help give meaning to this data, and to give us guarantees about things like the origin of the data and how/where the data is used. See [<a title="Using elm types to prevent logging social security #'s" href="exit-gatekeepers">exit-gatekeepers</a>] and [<a title="Entry Gatekeepers" href="entry-gatekeepers">entry-gatekeepers</a>].</p><h2>If It Compiles, It's Wired Correctly</h2><p>Can working from a clean slate give us the feeling that if it compiles it works? If Elm isn't writing our code for us, then it can't get us all the way there. But I have often experienced the feeling of writing an Elm custom type, being sure to wire it up (through <code>update</code>, <code>view</code>, etc. - of course it won't work if I forget that), and then once it's wired in it does seem to "just work."</p><p>My personal experience with this is that it applies to wiring, which in other languages is often something that I have to take a lot of care to get right and spend a lot of time debugging as I build features from scratch. When it comes to building up sophisticated business logic, I start by writing unit tests and iterate on that, so that's far from "if it compiles, it works." It's more of an iterative process where it goes through several stages of completion where it's compiling but doesn't handle all of the cases (it only does enough to make my test cases pass).</p><p>So for me, the phrase "if it compiles, it works" doesn't apply to writing my business logic from scratch, but it does apply to the wiring and that in turn lets me focus on my business logic more.</p><p>If you use tools to help you wire external systems safely to your Elm code ([<a title="Types Without Borders" href="types-without-borders">types-without-borders</a>]), then you can extend this confidence about correct wiring to the parts of your code that communicate with external systems.</p><h2>If The Tests Pass, It Works</h2><p>When I'm writing my unit tests in Elm, I also feel more confident that "if my tests pass, it works." Why would this be different in Elm than working in other languages? One major factor is that Elm doesn't need mocks (nor does it have the ability to do mocks since it's not a dynamic language). You can use dependency injection. In fact, this is baked into the paradigm with things like random numbers, the current date, or other non-deterministic data: the only way to get it is to receive it in <code>update</code> from the Elm runtime.</p><p>Because I know my Elm code doesn't depend on global environment, implicit side effects, and non-deterministic functions, I can trust the green I get when my unit tests pass. Testing that an input gives an output removes many testing pitfalls and painpoints. The Elm language makes testing easy and safe, so that's all the more reason to test your Elm code.</p><p>That's also a good reminder that type-safety is a complement to automated tests, not a replacement for it. I can say for certain that my confidence changing my Elm applications would be very low if I didn't have automated tests.</p><h2>What Do You Think</h2><p>What does "If it compiles, it works" mean to you? And what are the limits? When does it compile, but not work? I'd love to hear your experiences.</p>]]></content:encoded>
</item>
<item>
<title>The Keystone Testing Habit</title>
<description>Test-Driven Development will improve your design and code quality more than any habit.</description>
<link>https://incrementalelm.com/keystone-testing-habit</link>
<guid>https://incrementalelm.com/keystone-testing-habit</guid>
<pubDate>Mon, 05 Oct 2020 00:00:00 GMT</pubDate>
<content>&lt;h1&gt;The Keystone Testing Habit&lt;/h1&gt;&lt;p&gt;We&apos;ve all experienced routines that set us up for success in our day. For some, starting with a workout makes everything else fall into place. They&apos;re more likely to eat healthy, stay on top of emails, and follow other good habits if they start our day with that one keystone habit.&lt;/p&gt;&lt;p&gt;The specific keystone habit will vary from person to person, but the concept is the same. That one habit is worth putting in the extra effort to maintain because it will act as a force multiplier.&lt;/p&gt;&lt;p&gt;With a growing Elm codebase, one keystone habit that keeps all the other habits on track is unit testing. If you follow [[tdd]], you&apos;re likely to form better habits around data modeling, code organization, function names, and almost every meaningful measure of code quality.&lt;/p&gt;&lt;p&gt;Let&apos;s look at why test-driven development has this effect.&lt;/p&gt;&lt;h2&gt;Extract Modules&lt;/h2&gt;&lt;p&gt;What do I even test here? If I asked you to go test an area of your code, what functions would you call? In what module? Are you going to call a function in your &lt;code&gt;Page/Projects.elm&lt;/code&gt; module from your test? Oh wait, but that function takes in the &lt;code&gt;Page.Projects.Model&lt;/code&gt;, so I&apos;ll need to change the function signature so it just passes in the &lt;code&gt;Project&lt;/code&gt; and returns a &lt;code&gt;Project&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;If you sit down to test something, you will naturally feel the pain of using code that&apos;s intertwined with your UI code and other unrelated domains. And feeling that pain is &lt;em&gt;a good thing&lt;/em&gt;.&lt;/p&gt;&lt;h2&gt;Read the Signals the Pain is Sending&lt;/h2&gt;&lt;p&gt;Pain is a good thing. &lt;em&gt;If&lt;/em&gt; you are able to respond to it. Imagine if you didn&apos;t have the ability to feel physical pain. Then imagine that your arm is twisted in a &quot;painful&quot; position. But you&apos;re not feeling that pain. By the time you notice, you&apos;d have a broken arm.&lt;/p&gt;&lt;p&gt;The pain is there to send you a message. When you receive that message, you respond, adjust your arm, and avoid harm. No broken arm.&lt;/p&gt;&lt;p&gt;If you&apos;re not using test-driven development, you&apos;re not feeling the pain of code with lots of dependencies, too many responsibilities in one module, and other code maintainability issues. You&apos;re missing the message. Test-driven development lets you tune into that message so you can avoid harm before it&apos;s too late. When you sit down to write a test, you will feel these pain points more acutely, which gives you the opportunity to respond.&lt;/p&gt;&lt;p&gt;Feel pain sooner, then you can respond to small pains with ease, not big ones that are hard to mend.&lt;/p&gt;&lt;h2&gt;Freedom to Change and Experiment&lt;/h2&gt;&lt;p&gt;Sure, Elm code is known for being very easy to refactor because of the language design itself. But as your codebase grows, you&apos;ll run into more complex business logic. Unit tests give you more confidence to try out different ways of modeling your data. So even when it comes to trying to [[Make Impossible States Impossible]] using data modeling techniques, you&apos;re going to feel more confident to try many different ways of modeling the data if you&apos;re supported by a test suite.&lt;/p&gt;&lt;p&gt;Great design isn&apos;t just about thinking hard and coming up with the perfect solution. It&apos;s an evolutionary process, and it requires experimentation. The better you are at experimenting, the better your designs will be &lt;em&gt;over time&lt;/em&gt; (you can&apos;t skip the over time part, because good design is a process not a destination). Test-driven development improves your ability to experiment, so it enables better designs to emerge.&lt;/p&gt;&lt;h2&gt;Emergent Design&lt;/h2&gt;&lt;p&gt;Unit tests are essential for allowing you to focus on one case at a time instead of trying to get everything working all at once. This approach of doing the simplest thing that could possibly work allows you to avoid unnecessary abstractions. You can design for your current needs instead of anticipating lots of future needs. Without test-driven development, it&apos;s very difficult to slice things down to small steps. With a unit test, you can write a failing test for the simplest possible case (an empty list when sorted is an empty list). Once you get that case working, you can move on to the next case. And at each small step, you have a simple design perfectly suited towards solving that problem (not every possible problem you might encounter in the future). So test-driven development is going to lead you towards a simpler codebase with more lightweight abstractions tailored towards exactly what the code needs.&lt;/p&gt;&lt;h2&gt;Continuous Improvement&lt;/h2&gt;&lt;p&gt;There&apos;s going to be more friction coming back to code and revisiting a design or making a small change if you don&apos;t have tests. And if you&apos;re changing the behavior in a small way (fixing a bug or adding a feature), then it will of course be a lot easier to be confident that you&apos;ve made the desired change without other unintended changes if you have a suite of tests to support you.&lt;/p&gt;&lt;h2&gt;Getting Started&lt;/h2&gt;&lt;p&gt;If you want to learn more about the Red-Green-Refactor cycle and applying those techniques in Elm, you can listen to the &lt;a href=&quot;https://elm-radio.com/episode/elm-test&quot;&gt;Elm Radio episode on testing&lt;/a&gt;. Or reach out to me and let me know your questions! Happy testing!&lt;/p&gt;</content>
<content:encoded><![CDATA[<h1>The Keystone Testing Habit</h1><p>We've all experienced routines that set us up for success in our day. For some, starting with a workout makes everything else fall into place. They're more likely to eat healthy, stay on top of emails, and follow other good habits if they start our day with that one keystone habit.</p><p>The specific keystone habit will vary from person to person, but the concept is the same. That one habit is worth putting in the extra effort to maintain because it will act as a force multiplier.</p><p>With a growing Elm codebase, one keystone habit that keeps all the other habits on track is unit testing. If you follow [[tdd]], you're likely to form better habits around data modeling, code organization, function names, and almost every meaningful measure of code quality.</p><p>Let's look at why test-driven development has this effect.</p><h2>Extract Modules</h2><p>What do I even test here? If I asked you to go test an area of your code, what functions would you call? In what module? Are you going to call a function in your <code>Page/Projects.elm</code> module from your test? Oh wait, but that function takes in the <code>Page.Projects.Model</code>, so I'll need to change the function signature so it just passes in the <code>Project</code> and returns a <code>Project</code>.</p><p>If you sit down to test something, you will naturally feel the pain of using code that's intertwined with your UI code and other unrelated domains. And feeling that pain is <em>a good thing</em>.</p><h2>Read the Signals the Pain is Sending</h2><p>Pain is a good thing. <em>If</em> you are able to respond to it. Imagine if you didn't have the ability to feel physical pain. Then imagine that your arm is twisted in a "painful" position. But you're not feeling that pain. By the time you notice, you'd have a broken arm.</p><p>The pain is there to send you a message. When you receive that message, you respond, adjust your arm, and avoid harm. No broken arm.</p><p>If you're not using test-driven development, you're not feeling the pain of code with lots of dependencies, too many responsibilities in one module, and other code maintainability issues. You're missing the message. Test-driven development lets you tune into that message so you can avoid harm before it's too late. When you sit down to write a test, you will feel these pain points more acutely, which gives you the opportunity to respond.</p><p>Feel pain sooner, then you can respond to small pains with ease, not big ones that are hard to mend.</p><h2>Freedom to Change and Experiment</h2><p>Sure, Elm code is known for being very easy to refactor because of the language design itself. But as your codebase grows, you'll run into more complex business logic. Unit tests give you more confidence to try out different ways of modeling your data. So even when it comes to trying to [[Make Impossible States Impossible]] using data modeling techniques, you're going to feel more confident to try many different ways of modeling the data if you're supported by a test suite.</p><p>Great design isn't just about thinking hard and coming up with the perfect solution. It's an evolutionary process, and it requires experimentation. The better you are at experimenting, the better your designs will be <em>over time</em> (you can't skip the over time part, because good design is a process not a destination). Test-driven development improves your ability to experiment, so it enables better designs to emerge.</p><h2>Emergent Design</h2><p>Unit tests are essential for allowing you to focus on one case at a time instead of trying to get everything working all at once. This approach of doing the simplest thing that could possibly work allows you to avoid unnecessary abstractions. You can design for your current needs instead of anticipating lots of future needs. Without test-driven development, it's very difficult to slice things down to small steps. With a unit test, you can write a failing test for the simplest possible case (an empty list when sorted is an empty list). Once you get that case working, you can move on to the next case. And at each small step, you have a simple design perfectly suited towards solving that problem (not every possible problem you might encounter in the future). So test-driven development is going to lead you towards a simpler codebase with more lightweight abstractions tailored towards exactly what the code needs.</p><h2>Continuous Improvement</h2><p>There's going to be more friction coming back to code and revisiting a design or making a small change if you don't have tests. And if you're changing the behavior in a small way (fixing a bug or adding a feature), then it will of course be a lot easier to be confident that you've made the desired change without other unintended changes if you have a suite of tests to support you.</p><h2>Getting Started</h2><p>If you want to learn more about the Red-Green-Refactor cycle and applying those techniques in Elm, you can listen to the <a href="https://elm-radio.com/episode/elm-test">Elm Radio episode on testing</a>. Or reach out to me and let me know your questions! Happy testing!</p>]]></content:encoded>
</item>
<item>
<title>Moving Faster with Tiny Steps in Elm</title>
<description>In this post, we&apos;re going to be looking up an Article in an Elm Dict, using the tiniest steps possible.</description>
<link>https://incrementalelm.com/moving-faster-with-tiny-steps</link>
<guid>https://incrementalelm.com/moving-faster-with-tiny-steps</guid>
<pubDate>Wed, 01 Jan 2020 00:00:00 GMT</pubDate>
<content>&lt;h1&gt;Moving Faster with Tiny Steps in Elm&lt;/h1&gt;&lt;p&gt;Why use tiny steps? Simple! Because we want to write Elm code faster, and with more precise error messages to guide us through each step.&lt;/p&gt;&lt;h2&gt;Setting Up Your Environment&lt;/h2&gt;&lt;p&gt;The point of taking tiny steps is that you get constant, clear feedback. So before I walk through the steps, here are some things to set up in your editor to help you get more feedback:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;See Elm compiler errors instantly without manually running a command. For example, have elm-make run whenever your files change. Or run elm-live, webpack, or parcel in watch mode.&lt;/li&gt;&lt;li&gt;Even better, get error messages in your editor whenever you save. Here are some instructions for configuring Atom with in-editor compiler errors.&lt;/li&gt;&lt;li&gt;Note that with both of these workflows, I recommend saving constantly so you get instant error messages.&lt;/li&gt;&lt;li&gt;Atom also gives you auto-completion, which is another helpful form of feedback. &lt;code&gt;intellij-elm&lt;/code&gt; is another good option for this.&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;The Problem&lt;/h2&gt;&lt;p&gt;We&apos;re doing a simple blog page that looks up articles based on the URL. We&apos;ve already got the wiring to get the article name from the URL (for example, localhost:8000/article/&lt;code&gt;articlePath&lt;/code&gt;). Now we just need to take that &lt;code&gt;articlePath&lt;/code&gt; and use it to look up the title and body of our article in a Dict.&lt;/p&gt;&lt;h2&gt;The Tiny Steps&lt;/h2&gt;&lt;p&gt;If you&apos;d like to see a short video of each of these steps, or download the code so you can try them for yourself, just sign up here and I&apos;ll send you a link.&lt;/p&gt;&lt;p&gt;Okay, now let&apos;s walk through our tiny steps for building our Dict!&lt;/p&gt;&lt;h2&gt;Step 0&lt;/h2&gt;&lt;p&gt;Always respond with &quot;Article Not Found.&quot;&lt;/p&gt;&lt;p&gt;We start with the failure case because it&apos;s easiest. This is sort of like returning 1 for for factorial(1). It&apos;s just an easy way to get something working and compiling. Think of this as our &quot;base case&quot;.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;view : Maybe String -&amp;gt; Browser.Document msg
view maybeArticlePath =
    articleNotFoundDocument

articleNotFoundDocument : Browser.Document msg
articleNotFoundDocument =
    { title = &quot;Article Not Found&quot;
    , body = [ text &quot;Article not found...&quot; ]
    }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We&apos;ve wired up our code so that when the user visits mysite.com/article/hello, you&apos;ll see our &quot;Article Not Found&quot; page.&lt;/p&gt;&lt;h2&gt;Step 1&lt;/h2&gt;&lt;p&gt;Hard code an empty dictionary.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;view : Maybe String -&amp;gt; Browser.Document msg
view maybeArticlePath =
    Dict.get articlePath Dict.empty
        |&amp;gt; Maybe.map articleDocument
        |&amp;gt; Maybe.withDefault articleNotFoundDocument
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Why bother? We know this lookup will always give back Nothing! So we haven&apos;t changed the behavior at all.&lt;/p&gt;&lt;p&gt;This step is actually quite powerful. We&apos;ve wired up our entire pipeline to reliably do a dictionary lookup and get back Nothing every time! Why is this so useful? Well, look at what we accomplish with this easy step:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;We&apos;ve made the necessary imports&lt;/li&gt;&lt;li&gt;We know that all the puzzle pieces fit perfectly together!&lt;/li&gt;&lt;li&gt;So even though we&apos;ve done almost nothing, the work that remains is all teed up for us! This is the power of incremental steps. We&apos;ve stripped out all the risk because we know that the whole picture ties together correctly.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;When you mix in the hard part (building the actual business logic) with the &quot;easy part&quot; (the wiring), you end up with something super hard! But when you do the wiring first, you can completely focus on the hard part once that&apos;s done. And amazingly, this small change in our approach makes it a lot easier.&lt;/p&gt;&lt;h2&gt;Step 2&lt;/h2&gt;&lt;p&gt;Extract the dictionary to a top-level value.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;view : Maybe String -&amp;gt; Browser.Document msg
view maybeArticlePath =
    Dict.get articlePath articles
        |&amp;gt; Maybe.map articleDocument
        |&amp;gt; Maybe.withDefault articleNotFoundDocument

articles =
    Dict.empty
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is just a simple refactor. We can refactor at any step. This is more than a superficial change, though. Pulling out this top-level value allows us to continue tweaking this small area inside a sort of sandbox. This will be much easier with a type-annotation, though…&lt;/p&gt;&lt;h2&gt;Step 3&lt;/h2&gt;&lt;p&gt;Annotate our articles top-level value.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;view : Maybe String -&amp;gt; Browser.Document msg
view maybeArticlePath =
    Dict.get articlePath articles
        |&amp;gt; Maybe.map articleDocument
        |&amp;gt; Maybe.withDefault articleNotFoundDocument

articles : Dict String Article
articles =
    Dict.empty
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This step is important because it allows the Elm compiler to give us more precise and helpful error messages. It can now pinpoint exactly where things go wrong if we take a misstep. Importantly, we&apos;re locking in the type annotation at a time when everything compiles already so we know things line up. If we added the type annotation when things weren&apos;t fully compiling, we wouldn&apos;t be very confident that we got it right.&lt;/p&gt;&lt;h2&gt;Step 4&lt;/h2&gt;&lt;p&gt;Use a &quot;synonym&quot; for Dict.empty.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;articles : Dict String Article
articles =
    Dict.fromList []
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What&apos;s a synonym? Well, it&apos;s just a different way to say the exact same thing.&lt;/p&gt;&lt;p&gt;Kent Beck calls this process &quot;Make the change easy, then make the easy change.&quot; Again, this is all about teeing ourselves up to make the next step trivial.&lt;/p&gt;&lt;h2&gt;Step 5&lt;/h2&gt;&lt;p&gt;Add a single item to your dictionary&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Dict.fromList
    [ ( &quot;hello&quot;
        , { title = &quot;Hello!&quot;,
            body = &quot;Here&apos;s a nice article for you! 🎉&quot;
          }
        )
    ]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now that we&apos;ve done all those other steps, this was super easy! We know exactly what this data structure needs to look like in order to get the type of data we need, because we&apos;ve already wired it up! And when we finally wire it up, everything just flows through uneventfully. Perhaps it&apos;s a bit anti-climactic, but hey, it&apos;s effective!&lt;/p&gt;&lt;p&gt;But isn&apos;t this just a toy example to illustrate a technique. While this technique is very powerful when it comes to more sophisticated problems, trust me when I say this is how I write code all the time, even when it&apos;s as trivial as creating a dictionary. And I promise you, having this technique in your tool belt will make it easier to write Elm code!&lt;/p&gt;&lt;h2&gt;Step 6&lt;/h2&gt;&lt;p&gt;In this example, we were dealing with hardcoded data. But it&apos;s easy to imagine grabbing this list from a database or an API. I&apos;ll leave this as an exercise for the reader, but let&apos;s explore the benefits.&lt;/p&gt;&lt;p&gt;When you start with small steps, removing hard-coding step by step, it lets you think up front about the ideal data structure. This ideal data structure dictates your code, and then from there you figure out how to massage the data from your API into the right data structure. It&apos;s easy to do things the other way around and let our JSON structures dictate how we&apos;re storing the data on the client.&lt;/p&gt;&lt;h2&gt;Thanks for Reading!&lt;/h2&gt;&lt;p&gt;You can sign up here for more tips on writing Elm code incrementally. When you sign up, I&apos;ll send the 3-minute walk-through video of each of these steps, and the download link for the starting-point code and the solution.&lt;/p&gt;&lt;p&gt;Let me know how this technique goes! I&apos;ve gotten a lot of great feedback from my clients about this approach, and I love hearing success stories. I&apos;d love to hear how you&apos;re able to apply this in your day-to-day work!&lt;/p&gt;</content>
<content:encoded><![CDATA[<h1>Moving Faster with Tiny Steps in Elm</h1><p>Why use tiny steps? Simple! Because we want to write Elm code faster, and with more precise error messages to guide us through each step.</p><h2>Setting Up Your Environment</h2><p>The point of taking tiny steps is that you get constant, clear feedback. So before I walk through the steps, here are some things to set up in your editor to help you get more feedback:</p><ul><li>See Elm compiler errors instantly without manually running a command. For example, have elm-make run whenever your files change. Or run elm-live, webpack, or parcel in watch mode.</li><li>Even better, get error messages in your editor whenever you save. Here are some instructions for configuring Atom with in-editor compiler errors.</li><li>Note that with both of these workflows, I recommend saving constantly so you get instant error messages.</li><li>Atom also gives you auto-completion, which is another helpful form of feedback. <code>intellij-elm</code> is another good option for this.</li></ul><h2>The Problem</h2><p>We're doing a simple blog page that looks up articles based on the URL. We've already got the wiring to get the article name from the URL (for example, localhost:8000/article/<code>articlePath</code>). Now we just need to take that <code>articlePath</code> and use it to look up the title and body of our article in a Dict.</p><h2>The Tiny Steps</h2><p>If you'd like to see a short video of each of these steps, or download the code so you can try them for yourself, just sign up here and I'll send you a link.</p><p>Okay, now let's walk through our tiny steps for building our Dict!</p><h2>Step 0</h2><p>Always respond with "Article Not Found."</p><p>We start with the failure case because it's easiest. This is sort of like returning 1 for for factorial(1). It's just an easy way to get something working and compiling. Think of this as our "base case".</p><pre><code>view : Maybe String -&gt; Browser.Document msg
view maybeArticlePath =
    articleNotFoundDocument

articleNotFoundDocument : Browser.Document msg
articleNotFoundDocument =
    { title = "Article Not Found"
    , body = [ text "Article not found..." ]
    }
</code></pre><p>We've wired up our code so that when the user visits mysite.com/article/hello, you'll see our "Article Not Found" page.</p><h2>Step 1</h2><p>Hard code an empty dictionary.</p><pre><code>view : Maybe String -&gt; Browser.Document msg
view maybeArticlePath =
    Dict.get articlePath Dict.empty
        |&gt; Maybe.map articleDocument
        |&gt; Maybe.withDefault articleNotFoundDocument
</code></pre><p>Why bother? We know this lookup will always give back Nothing! So we haven't changed the behavior at all.</p><p>This step is actually quite powerful. We've wired up our entire pipeline to reliably do a dictionary lookup and get back Nothing every time! Why is this so useful? Well, look at what we accomplish with this easy step:</p><ul><li>We've made the necessary imports</li><li>We know that all the puzzle pieces fit perfectly together!</li><li>So even though we've done almost nothing, the work that remains is all teed up for us! This is the power of incremental steps. We've stripped out all the risk because we know that the whole picture ties together correctly.</li></ul><p>When you mix in the hard part (building the actual business logic) with the "easy part" (the wiring), you end up with something super hard! But when you do the wiring first, you can completely focus on the hard part once that's done. And amazingly, this small change in our approach makes it a lot easier.</p><h2>Step 2</h2><p>Extract the dictionary to a top-level value.</p><pre><code>view : Maybe String -&gt; Browser.Document msg
view maybeArticlePath =
    Dict.get articlePath articles
        |&gt; Maybe.map articleDocument
        |&gt; Maybe.withDefault articleNotFoundDocument

articles =
    Dict.empty
</code></pre><p>This is just a simple refactor. We can refactor at any step. This is more than a superficial change, though. Pulling out this top-level value allows us to continue tweaking this small area inside a sort of sandbox. This will be much easier with a type-annotation, though…</p><h2>Step 3</h2><p>Annotate our articles top-level value.</p><pre><code>view : Maybe String -&gt; Browser.Document msg
view maybeArticlePath =
    Dict.get articlePath articles
        |&gt; Maybe.map articleDocument
        |&gt; Maybe.withDefault articleNotFoundDocument

articles : Dict String Article
articles =
    Dict.empty
</code></pre><p>This step is important because it allows the Elm compiler to give us more precise and helpful error messages. It can now pinpoint exactly where things go wrong if we take a misstep. Importantly, we're locking in the type annotation at a time when everything compiles already so we know things line up. If we added the type annotation when things weren't fully compiling, we wouldn't be very confident that we got it right.</p><h2>Step 4</h2><p>Use a "synonym" for Dict.empty.</p><pre><code>articles : Dict String Article
articles =
    Dict.fromList []
</code></pre><p>What's a synonym? Well, it's just a different way to say the exact same thing.</p><p>Kent Beck calls this process "Make the change easy, then make the easy change." Again, this is all about teeing ourselves up to make the next step trivial.</p><h2>Step 5</h2><p>Add a single item to your dictionary</p><pre><code>Dict.fromList
    [ ( "hello"
        , { title = "Hello!",
            body = "Here's a nice article for you! 🎉"
          }
        )
    ]
</code></pre><p>Now that we've done all those other steps, this was super easy! We know exactly what this data structure needs to look like in order to get the type of data we need, because we've already wired it up! And when we finally wire it up, everything just flows through uneventfully. Perhaps it's a bit anti-climactic, but hey, it's effective!</p><p>But isn't this just a toy example to illustrate a technique. While this technique is very powerful when it comes to more sophisticated problems, trust me when I say this is how I write code all the time, even when it's as trivial as creating a dictionary. And I promise you, having this technique in your tool belt will make it easier to write Elm code!</p><h2>Step 6</h2><p>In this example, we were dealing with hardcoded data. But it's easy to imagine grabbing this list from a database or an API. I'll leave this as an exercise for the reader, but let's explore the benefits.</p><p>When you start with small steps, removing hard-coding step by step, it lets you think up front about the ideal data structure. This ideal data structure dictates your code, and then from there you figure out how to massage the data from your API into the right data structure. It's easy to do things the other way around and let our JSON structures dictate how we're storing the data on the client.</p><h2>Thanks for Reading!</h2><p>You can sign up here for more tips on writing Elm code incrementally. When you sign up, I'll send the 3-minute walk-through video of each of these steps, and the download link for the starting-point code and the solution.</p><p>Let me know how this technique goes! I've gotten a lot of great feedback from my clients about this approach, and I love hearing success stories. I'd love to hear how you're able to apply this in your day-to-day work!</p>]]></content:encoded>
</item>
<item>
<title>Opaque Types Let You Think Locally</title>
<description>Elm&apos;s Opaque Types are a powerful tool for narrowing the surface area where you check a constraint. TypeScript&apos;s Branded Types give similar functionality but without closing outside use, so you can&apos;t be sure the constraints are enforced everywhere. Let&apos;s explore the differences to better understand why Elm&apos;s Opaque Types are such an important tool.</description>
<link>https://incrementalelm.com/opaque-types-let-you-think-locally</link>
<guid>https://incrementalelm.com/opaque-types-let-you-think-locally</guid>
<pubDate>Mon, 25 Oct 2021 00:00:00 GMT</pubDate>
<content>&lt;h1&gt;Opaque Types Let You Think Locally&lt;/h1&gt;&lt;p&gt;Elm&apos;s Opaque Types are a powerful tool for narrowing the surface area where you check a constraint. TypeScript&apos;s Branded Types give similar functionality but without closing outside use, so you can&apos;t be sure the constraints are enforced everywhere. Let&apos;s explore the differences to better understand why Elm&apos;s Opaque Types are such an important tool.&lt;/p&gt;&lt;p&gt;Strictly speaking, opaque types don&apos;t make Elm&apos;s type system more sound. Instead, they help you narrow your thinking of how a particular type is used to within a single module. They allow you to check that a constraint is enforced in a single module, rather than having to ensure that a constraint is enforced throughout your entire codebase, both now and in the future.&lt;/p&gt;&lt;p&gt;For example, if you want to ensure that a String actually represents a confirmed email address, that has nothing to do with type soundness - the type is just a String. But if the only way to get a value with &lt;code&gt;type ConfirmedEmailAddress = ConfirmedEmailAddress String&lt;/code&gt; is through an HTTP request to a specific server endpoint, then you can trust any value of that type after checking the &lt;code&gt;ConfirmedEmailAddress&lt;/code&gt; module and the API endpoint. You just need to make sure that you trust that server endpoint and the &lt;code&gt;ConfirmedEmailAddress&lt;/code&gt; module. It&apos;s the same idea as [&lt;a title=&quot;Using elm types to prevent logging social security #&apos;s&quot; href=&quot;exit-gatekeepers&quot;&gt;exit-gatekeepers&lt;/a&gt;].&lt;/p&gt;&lt;pre&gt;&lt;code&gt;module ConfirmedEmailAddress exposing (ConfirmedEmailAddress, checkEmailAddress)

type ConfirmedEmailAddress = ConfirmedEmailAddress String

checkEmailAddress : (Result Http.Error ConfirmedEmailAddress -&amp;gt; msg) -&amp;gt; String -&amp;gt; Cmd msg
checkEmailAddress toMsg emailAddress =
  Http.post
    { url = &quot;https://example.com/confirm-email-address.json?email=&quot;
          ++ Url.percentEncode emailAddress
    , body = Http.emptyBody
    , expect = Http.expectJson toMsg
        (Json.Decode.string
            |&amp;gt; Json.Decode.map ConfirmedEmailAddress
        )
    }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Compare this with Branded Types in [&lt;a title=&quot;TypeScript&quot; href=&quot;typescript&quot;&gt;typescript&lt;/a&gt;].&lt;/p&gt;&lt;pre&gt;&lt;code&gt;type ConfirmedEmailAddress = string &amp;amp; { __brand: &quot;ConfirmedEmailAddress&quot; };

// uh oh, any code can brand it
const unconfirmedEmail = &quot;unconfirmed-email@example.com&quot; as (string &amp;amp; {
  __brand: &quot;ConfirmedEmailAddress&quot;);
};
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So some drawbacks to Branded Types in TypeScript are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;They are open and can be branded by code anywhere in the codebase&lt;/li&gt;&lt;li&gt;They use casting to intersect two contradictory types. This allows you to create an artificial type that can only be created through &quot;branding&quot;, but it feels like a little bit of a hack. It also illustrates that the branding occurs through casting which allows you to tell the TypeScript compiler what the type of a value is, but &lt;a href=&quot;https://incrementalelm.com/typescript-blind-spots#casts&quot;&gt;this can be error prone because you could tell it incorrect type information&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Checking Currency&lt;/h2&gt;&lt;p&gt;Another example of a Branded Type in TypeScript is marking a type as representing a specific currency.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;type Usd = number &amp;amp; { __brand: &quot;USD&quot; };

function fromCents(cents: number): Usd {
  return cents as Usd;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This &lt;code&gt;Usd&lt;/code&gt; type allows us to brand a number so we know it represents US Dollars. That&apos;s great because we want to:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Ensure that the currency is not mistakenly combined with a different currency&lt;/li&gt;&lt;li&gt;Ensure that we use a consistent representation (for example, if the number represents cents as an integer rather than dollars as a float)&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;For point 2, we want to make sure that there is a single place that builds up currency. For example, we don&apos;t want someone to accidentally use dollars as a float somewhere. But since a Branded Type in TypeScript is &quot;open&quot; and uses casting to create it, there is no single place that we can enforce as the only place the logic for creating and dealing with that type. So any outside code can brand it like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;// whoops, (fromCents(150) === 150), (fromCents(150) !== 1.5)
const aDollarFifty = 1.5 as number &amp;amp; { __brand: &quot;USD&quot; };
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Compare that with an Opaque Type in Elm.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;module Money exposing (Money, Usd)

type Money currency = Money Int

type Usd = Usd

fromUsDollars : Int -&amp;gt; Money Usd
fromUsDollars dollarAmount = Usd (dollarAmount * 100)

fromUsCents : Int -&amp;gt; Money Usd
fromUsCents usCents = Usd usCents
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Our Elm &lt;code&gt;Usd&lt;/code&gt; type cannot be created outside of that module. If we want to see how that type is being used, we only have one place to look: within the &lt;code&gt;Money&lt;/code&gt; module where it&apos;s defined. Since it isn&apos;t exposed to the outside world, we know that we&apos;ve limited the possible ways that outside code can use that type.&lt;/p&gt;&lt;h2&gt;Branded Types and Unique Symbols&lt;/h2&gt;&lt;p&gt;The technique described above is the idiomatic approach to branded types in TypeScript (&lt;a href=&quot;https://www.typescriptlang.org/play#example/nominal-typing&quot;&gt;used in the official TypeScript examples&lt;/a&gt; and &lt;a href=&quot;https://github.com/Microsoft/TypeScript/blob/7b48a182c05ea4dea81bab73ecbbe9e013a79e99/src/compiler/types.ts#L693-L698&quot;&gt;in the TypeScript codebase&lt;/a&gt;). There is another technique that allows you to provide unique brands that are enclosed within a given scope using Unique Symbols.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;module Email {
  declare const confirmedEmail_: unique symbol;

  type ConfirmedEmail = string &amp;amp; {[confirmedEmail_]: true};

  export function fromServer(emailAddress: string): ConfirmedEmail {
    // validate email address
    return emailAddress as ConfirmedEmail;
  }
}

const unconfirmedEmail = &quot;unconfirmed-email@example.com&quot; as // ??? there&apos;s no exported type to use here
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This technique succeeds in ensuring that the &lt;code&gt;ConfirmedEmail&lt;/code&gt; type cannot be constructed outside of the scope of &lt;code&gt;Email&lt;/code&gt; (assuming you don&apos;t use &lt;code&gt;any&lt;/code&gt; types of course).&lt;/p&gt;&lt;p&gt;However, now we have no exported type to use to annotate values to ensure that the correct type is used. That means we can&apos;t write code like this outside of the scope of &lt;code&gt;Email&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;function sendEmail(email: Email.ConfirmedEmail) {
  // ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You could certainly implement &lt;code&gt;sendEmail&lt;/code&gt; within the scope of &lt;code&gt;Email&lt;/code&gt;. But I think being able to annotate values is an important feature that is likely to become a roadblock when we want to ensure we receive our unique branded type as a parameter somewhere outside of &lt;code&gt;Email&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;We could &lt;code&gt;export&lt;/code&gt; the &lt;code&gt;ConfirmedEmail&lt;/code&gt; type to outside of the &lt;code&gt;Email&lt;/code&gt; module, but then that gets us back at the initial challenge with branded types: the type can be used to cast a value that is constructed anywhere in our codebase.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;const unconfirmedEmail =
  &quot;unconfirmed-email@example.com&quot; as Email.ConfirmedEmail;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The TypeScript language could have a specific feature for opaque types (like &lt;a href=&quot;https://flow.org/en/docs/types/opaque-types/&quot;&gt;Flow&apos;s Opaque Type Aliases&lt;/a&gt;), but it &lt;a href=&quot;https://github.com/microsoft/TypeScript/issues/15807&quot;&gt;seems that they plan to stick with the current branded types approach as the recommended solution&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;More Resources&lt;/h2&gt;&lt;p&gt;Opaque Types in Elm are a powerful tool to let you narrow the scope of code you need to think about to make sure you&apos;ve gotten your constraints right.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Check out our &lt;a href=&quot;https://elm-radio.com/episode/intro-to-opaque-types&quot;&gt;Opaque Types Elm Radio episode&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;We also have an Elm Radio episode where we do a thorough &lt;a href=&quot;https://elm-radio.com/episode/ts-and-elm-type-systems&quot;&gt;comparison of Elm and TypeScript&apos;s Type Systems&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Joël Quenneville&apos;s talk &lt;a href=&quot;https://www.youtube.com/watch?v=WnTw0z7rD3E&quot;&gt;A Number by Any Other Name&lt;/a&gt; has some great examples as well.&lt;/li&gt;&lt;li&gt;[&lt;a title=&quot;TypeScript&apos;s Blind Spots&quot; href=&quot;typescript-blind-spots&quot;&gt;typescript-blind-spots&lt;/a&gt;] catalogs all the ways that you can introduce unsound types in a TypeScript program&lt;/li&gt;&lt;/ul&gt;</content>
<content:encoded><![CDATA[<h1>Opaque Types Let You Think Locally</h1><p>Elm's Opaque Types are a powerful tool for narrowing the surface area where you check a constraint. TypeScript's Branded Types give similar functionality but without closing outside use, so you can't be sure the constraints are enforced everywhere. Let's explore the differences to better understand why Elm's Opaque Types are such an important tool.</p><p>Strictly speaking, opaque types don't make Elm's type system more sound. Instead, they help you narrow your thinking of how a particular type is used to within a single module. They allow you to check that a constraint is enforced in a single module, rather than having to ensure that a constraint is enforced throughout your entire codebase, both now and in the future.</p><p>For example, if you want to ensure that a String actually represents a confirmed email address, that has nothing to do with type soundness - the type is just a String. But if the only way to get a value with <code>type ConfirmedEmailAddress = ConfirmedEmailAddress String</code> is through an HTTP request to a specific server endpoint, then you can trust any value of that type after checking the <code>ConfirmedEmailAddress</code> module and the API endpoint. You just need to make sure that you trust that server endpoint and the <code>ConfirmedEmailAddress</code> module. It's the same idea as [<a title="Using elm types to prevent logging social security #'s" href="exit-gatekeepers">exit-gatekeepers</a>].</p><pre><code>module ConfirmedEmailAddress exposing (ConfirmedEmailAddress, checkEmailAddress)

type ConfirmedEmailAddress = ConfirmedEmailAddress String

checkEmailAddress : (Result Http.Error ConfirmedEmailAddress -&gt; msg) -&gt; String -&gt; Cmd msg
checkEmailAddress toMsg emailAddress =
  Http.post
    { url = "https://example.com/confirm-email-address.json?email="
          ++ Url.percentEncode emailAddress
    , body = Http.emptyBody
    , expect = Http.expectJson toMsg
        (Json.Decode.string
            |&gt; Json.Decode.map ConfirmedEmailAddress
        )
    }
</code></pre><p>Compare this with Branded Types in [<a title="TypeScript" href="typescript">typescript</a>].</p><pre><code>type ConfirmedEmailAddress = string &amp; { __brand: "ConfirmedEmailAddress" };

// uh oh, any code can brand it
const unconfirmedEmail = "unconfirmed-email@example.com" as (string &amp; {
  __brand: "ConfirmedEmailAddress");
};
</code></pre><p>So some drawbacks to Branded Types in TypeScript are:</p><ul><li>They are open and can be branded by code anywhere in the codebase</li><li>They use casting to intersect two contradictory types. This allows you to create an artificial type that can only be created through "branding", but it feels like a little bit of a hack. It also illustrates that the branding occurs through casting which allows you to tell the TypeScript compiler what the type of a value is, but <a href="https://incrementalelm.com/typescript-blind-spots#casts">this can be error prone because you could tell it incorrect type information</a></li></ul><h2>Checking Currency</h2><p>Another example of a Branded Type in TypeScript is marking a type as representing a specific currency.</p><pre><code>type Usd = number &amp; { __brand: "USD" };

function fromCents(cents: number): Usd {
  return cents as Usd;
}
</code></pre><p>This <code>Usd</code> type allows us to brand a number so we know it represents US Dollars. That's great because we want to:</p><ol><li>Ensure that the currency is not mistakenly combined with a different currency</li><li>Ensure that we use a consistent representation (for example, if the number represents cents as an integer rather than dollars as a float)</li></ol><p>For point 2, we want to make sure that there is a single place that builds up currency. For example, we don't want someone to accidentally use dollars as a float somewhere. But since a Branded Type in TypeScript is "open" and uses casting to create it, there is no single place that we can enforce as the only place the logic for creating and dealing with that type. So any outside code can brand it like this:</p><pre><code>// whoops, (fromCents(150) === 150), (fromCents(150) !== 1.5)
const aDollarFifty = 1.5 as number &amp; { __brand: "USD" };
</code></pre><p>Compare that with an Opaque Type in Elm.</p><pre><code>module Money exposing (Money, Usd)

type Money currency = Money Int

type Usd = Usd

fromUsDollars : Int -&gt; Money Usd
fromUsDollars dollarAmount = Usd (dollarAmount * 100)

fromUsCents : Int -&gt; Money Usd
fromUsCents usCents = Usd usCents
</code></pre><p>Our Elm <code>Usd</code> type cannot be created outside of that module. If we want to see how that type is being used, we only have one place to look: within the <code>Money</code> module where it's defined. Since it isn't exposed to the outside world, we know that we've limited the possible ways that outside code can use that type.</p><h2>Branded Types and Unique Symbols</h2><p>The technique described above is the idiomatic approach to branded types in TypeScript (<a href="https://www.typescriptlang.org/play#example/nominal-typing">used in the official TypeScript examples</a> and <a href="https://github.com/Microsoft/TypeScript/blob/7b48a182c05ea4dea81bab73ecbbe9e013a79e99/src/compiler/types.ts#L693-L698">in the TypeScript codebase</a>). There is another technique that allows you to provide unique brands that are enclosed within a given scope using Unique Symbols.</p><pre><code>module Email {
  declare const confirmedEmail_: unique symbol;

  type ConfirmedEmail = string &amp; {[confirmedEmail_]: true};

  export function fromServer(emailAddress: string): ConfirmedEmail {
    // validate email address
    return emailAddress as ConfirmedEmail;
  }
}

const unconfirmedEmail = "unconfirmed-email@example.com" as // ??? there's no exported type to use here
</code></pre><p>This technique succeeds in ensuring that the <code>ConfirmedEmail</code> type cannot be constructed outside of the scope of <code>Email</code> (assuming you don't use <code>any</code> types of course).</p><p>However, now we have no exported type to use to annotate values to ensure that the correct type is used. That means we can't write code like this outside of the scope of <code>Email</code>:</p><pre><code>function sendEmail(email: Email.ConfirmedEmail) {
  // ...
}
</code></pre><p>You could certainly implement <code>sendEmail</code> within the scope of <code>Email</code>. But I think being able to annotate values is an important feature that is likely to become a roadblock when we want to ensure we receive our unique branded type as a parameter somewhere outside of <code>Email</code>.</p><p>We could <code>export</code> the <code>ConfirmedEmail</code> type to outside of the <code>Email</code> module, but then that gets us back at the initial challenge with branded types: the type can be used to cast a value that is constructed anywhere in our codebase.</p><pre><code>const unconfirmedEmail =
  "unconfirmed-email@example.com" as Email.ConfirmedEmail;
</code></pre><p>The TypeScript language could have a specific feature for opaque types (like <a href="https://flow.org/en/docs/types/opaque-types/">Flow's Opaque Type Aliases</a>), but it <a href="https://github.com/microsoft/TypeScript/issues/15807">seems that they plan to stick with the current branded types approach as the recommended solution</a>.</p><h2>More Resources</h2><p>Opaque Types in Elm are a powerful tool to let you narrow the scope of code you need to think about to make sure you've gotten your constraints right.</p><ul><li>Check out our <a href="https://elm-radio.com/episode/intro-to-opaque-types">Opaque Types Elm Radio episode</a>.</li><li>We also have an Elm Radio episode where we do a thorough <a href="https://elm-radio.com/episode/ts-and-elm-type-systems">comparison of Elm and TypeScript's Type Systems</a></li><li>Joël Quenneville's talk <a href="https://www.youtube.com/watch?v=WnTw0z7rD3E">A Number by Any Other Name</a> has some great examples as well.</li><li>[<a title="TypeScript's Blind Spots" href="typescript-blind-spots">typescript-blind-spots</a>] catalogs all the ways that you can introduce unsound types in a TypeScript program</li></ul>]]></content:encoded>
</item>
<item>
<title>Relentless Tiny Habits</title>
<description>Manageable codebases don&apos;t happen because of brilliant designs. They happen because of relentless tiny habits that move code in the right direction.</description>
<link>https://incrementalelm.com/relentless-tiny-habits</link>
<guid>https://incrementalelm.com/relentless-tiny-habits</guid>
<pubDate>Mon, 28 Sep 2020 00:00:00 GMT</pubDate>
<content>&lt;h1&gt;Relentless Tiny Habits&lt;/h1&gt;&lt;p&gt;Your development habits and processes are perfectly designed to give you exactly the results you&apos;re getting. Do you wish something was different about those results? You&apos;d like it to be easier to understand code, or add new features? The solution is simple and unglamorous. Focus on building the right habits. And step back to look at your development processes to consider how they support or hinder building those habits.&lt;/p&gt;&lt;p&gt;So what do those habits look like? The key to manageable code is not applying big, complex ideas occasionally (you know, those 4 week refactoring branches). Instead, it comes from applying very simple ideas &lt;strong&gt;persistently&lt;/strong&gt;. Relentlessly. No seriously, turn that dial up all the way to 11.&lt;/p&gt;&lt;h2&gt;Things That Get in the Way of Relentless Habits&lt;/h2&gt;&lt;p&gt;Habits are key. But in order to build these habits, you need to double down on rewarding those habits by removing friction. Habits form when you get that positive signal when you perform the habit.&lt;/p&gt;&lt;p&gt;If your build server is very slow, then you won&apos;t build a habit of refactoring as soon as you see the opportunity. If you don&apos;t have unit tests, then you won&apos;t build the habit of changing your underlying data types to better reflect your domain. If setting up a unit test is cumbersome then you won&apos;t write tests.&lt;/p&gt;&lt;p&gt;Anything that increases confidence or otherwise reduces friction to making changes is a [[keystone habit]]. That is, getting in the habit of improving your feedback loops and safety nets will enable you and your team to build other good habits faster. Keystone habits are a key ingredient to Relentless, Tiny Habits.&lt;/p&gt;&lt;h2&gt;The Core Habits&lt;/h2&gt;&lt;p&gt;What are those core habits that keep Elm code manageable? Again, these aren&apos;t big, complex ideas applied occasionally. They&apos;re &lt;em&gt;simple&lt;/em&gt;. The hard part isn&apos;t the ideas and the techniques, it&apos;s building the habits and applying them relentlessly.&lt;/p&gt;&lt;p&gt;Also keep in mind that these are not destinations, but rather directions. These do not represent perfection, but a direction to move towards. Don&apos;t let the perfect be the enemy of improvement. Improvement is the goal at every step.&lt;/p&gt;&lt;h3&gt;Narrow the source of truth&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Move things into modules&lt;/li&gt;&lt;li&gt;Use &lt;a href=&quot;https://elm-radio.com/episode/intro-to-opaque-types&quot;&gt;opaque types&lt;/a&gt; to move responsibility to a single pinch point&lt;/li&gt;&lt;li&gt;Remove &lt;a href=&quot;https://elm-radio.com/episode/impossible-states/&quot;&gt;&quot;Impossible States&quot; from your data types&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Depend on fewer things. Decouple your code. Narrow down the types that you can accept, or can return.&lt;/li&gt;&lt;li&gt;Turn redundant state that can get out of sync into derived state that can&apos;t be out of sync&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;These may sound like very fundamental techniques, and they are! But that&apos;s the point. It&apos;s not any individual step that&apos;s profound, it&apos;s the result of applying these steps constantly. The more you narrow the source of truth, the easier it will be to read and change code. Move in that direction 100 times, and now you have a transformation that was composed of several mundane changes.&lt;/p&gt;&lt;h3&gt;Name Things Better&lt;/h3&gt;&lt;p&gt;&lt;a href=&quot;https://www.digdeeproots.com/articles/on/naming-as-a-process/&quot;&gt;Naming is a process&lt;/a&gt;. Again, don&apos;t let finding the perfect name get in your way. In fact, grouping things together (i.e. extracting a value, function, or module) and giving that group a ridiculous name, is &lt;em&gt;an improvement&lt;/em&gt; over not grouping them.&lt;/p&gt;&lt;p&gt;Once you have a cohesive grouping, move towards finding a better name.&lt;/p&gt;&lt;h3&gt;Tiny Steps That Maintain a Working State&lt;/h3&gt;&lt;p&gt;As you move in the right direction, you want to &lt;em&gt;build-in and ensure quality at every step&lt;/em&gt;, rather than adding or verifying quality after the fact. That means having feedback loops, like tests and foolproof automation. You can learn more about some of these processes for building-in quality and reducing friction in [[Continuous Delivery]].
Make smaller steps, and keep things in a working state at each point along the way.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Refactor with several small steps that keep your tests (and compiler) green&lt;/li&gt;&lt;li&gt;Use automated refactorings in your IDE&lt;/li&gt;&lt;li&gt;Use an &lt;a href=&quot;https://github.com/jfmengels/elm-review&quot;&gt;&lt;code&gt;elm-review&lt;/code&gt;&lt;/a&gt; rule to catch code quality with a shorter feedback loop&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Getting Started&lt;/h2&gt;&lt;p&gt;Want to try applying some Relentless, Tiny Habits? Here&apos;s a simple way to start. Open up your project. Find one of the simple changes above, like a function name that could be more clear. We constantly find these small opportunities for improvements as we read through code.&lt;/p&gt;&lt;p&gt;Now use your IDE&apos;s automated refactoring to rename the function. And here&apos;s the scary part. Commit it. Right now. And push it all the way through to production. If you can&apos;t do that, then &lt;strong&gt;write down a list of reasons you can&apos;t&lt;/strong&gt;. Now you&apos;ve got a todo list of things that are getting in the way of building relentless tiny habits to improve code. Once you&apos;ve gotten through your todo list and are able to push that tiny improvement through to production, you&apos;re off to a great start!&lt;/p&gt;&lt;p&gt;Now do that more. Not bigger, not more complex. Just more frequently. Once that&apos;s a habit, congratulations, you&apos;re now using a Tiny Habit to improve your codebase! Now, just keep doing it. Relentlessly.&lt;/p&gt;</content>
<content:encoded><![CDATA[<h1>Relentless Tiny Habits</h1><p>Your development habits and processes are perfectly designed to give you exactly the results you're getting. Do you wish something was different about those results? You'd like it to be easier to understand code, or add new features? The solution is simple and unglamorous. Focus on building the right habits. And step back to look at your development processes to consider how they support or hinder building those habits.</p><p>So what do those habits look like? The key to manageable code is not applying big, complex ideas occasionally (you know, those 4 week refactoring branches). Instead, it comes from applying very simple ideas <strong>persistently</strong>. Relentlessly. No seriously, turn that dial up all the way to 11.</p><h2>Things That Get in the Way of Relentless Habits</h2><p>Habits are key. But in order to build these habits, you need to double down on rewarding those habits by removing friction. Habits form when you get that positive signal when you perform the habit.</p><p>If your build server is very slow, then you won't build a habit of refactoring as soon as you see the opportunity. If you don't have unit tests, then you won't build the habit of changing your underlying data types to better reflect your domain. If setting up a unit test is cumbersome then you won't write tests.</p><p>Anything that increases confidence or otherwise reduces friction to making changes is a [[keystone habit]]. That is, getting in the habit of improving your feedback loops and safety nets will enable you and your team to build other good habits faster. Keystone habits are a key ingredient to Relentless, Tiny Habits.</p><h2>The Core Habits</h2><p>What are those core habits that keep Elm code manageable? Again, these aren't big, complex ideas applied occasionally. They're <em>simple</em>. The hard part isn't the ideas and the techniques, it's building the habits and applying them relentlessly.</p><p>Also keep in mind that these are not destinations, but rather directions. These do not represent perfection, but a direction to move towards. Don't let the perfect be the enemy of improvement. Improvement is the goal at every step.</p><h3>Narrow the source of truth</h3><ul><li>Move things into modules</li><li>Use <a href="https://elm-radio.com/episode/intro-to-opaque-types">opaque types</a> to move responsibility to a single pinch point</li><li>Remove <a href="https://elm-radio.com/episode/impossible-states/">"Impossible States" from your data types</a></li><li>Depend on fewer things. Decouple your code. Narrow down the types that you can accept, or can return.</li><li>Turn redundant state that can get out of sync into derived state that can't be out of sync</li></ul><p>These may sound like very fundamental techniques, and they are! But that's the point. It's not any individual step that's profound, it's the result of applying these steps constantly. The more you narrow the source of truth, the easier it will be to read and change code. Move in that direction 100 times, and now you have a transformation that was composed of several mundane changes.</p><h3>Name Things Better</h3><p><a href="https://www.digdeeproots.com/articles/on/naming-as-a-process/">Naming is a process</a>. Again, don't let finding the perfect name get in your way. In fact, grouping things together (i.e. extracting a value, function, or module) and giving that group a ridiculous name, is <em>an improvement</em> over not grouping them.</p><p>Once you have a cohesive grouping, move towards finding a better name.</p><h3>Tiny Steps That Maintain a Working State</h3><p>As you move in the right direction, you want to <em>build-in and ensure quality at every step</em>, rather than adding or verifying quality after the fact. That means having feedback loops, like tests and foolproof automation. You can learn more about some of these processes for building-in quality and reducing friction in [[Continuous Delivery]].
Make smaller steps, and keep things in a working state at each point along the way.</p><ul><li>Refactor with several small steps that keep your tests (and compiler) green</li><li>Use automated refactorings in your IDE</li><li>Use an <a href="https://github.com/jfmengels/elm-review"><code>elm-review</code></a> rule to catch code quality with a shorter feedback loop</li></ul><h2>Getting Started</h2><p>Want to try applying some Relentless, Tiny Habits? Here's a simple way to start. Open up your project. Find one of the simple changes above, like a function name that could be more clear. We constantly find these small opportunities for improvements as we read through code.</p><p>Now use your IDE's automated refactoring to rename the function. And here's the scary part. Commit it. Right now. And push it all the way through to production. If you can't do that, then <strong>write down a list of reasons you can't</strong>. Now you've got a todo list of things that are getting in the way of building relentless tiny habits to improve code. Once you've gotten through your todo list and are able to push that tiny improvement through to production, you're off to a great start!</p><p>Now do that more. Not bigger, not more complex. Just more frequently. Once that's a habit, congratulations, you're now using a Tiny Habit to improve your codebase! Now, just keep doing it. Relentlessly.</p>]]></content:encoded>
</item>
<item>
<title>How do you test code that&apos;s not defined in a module?</title>
<description>Have you ever sat down to try to test some code and not known where to start? There&apos;s a bug in production, and you want to reproduce it in a unit test before working on the fix. But what do you even test? Where is the problem?</description>
<link>https://incrementalelm.com/testing-modules</link>
<guid>https://incrementalelm.com/testing-modules</guid>
<pubDate>Mon, 28 Sep 2020 00:00:00 GMT</pubDate>
<content>&lt;h1&gt;How do you test code that&apos;s not defined in a module?&lt;/h1&gt;&lt;p&gt;Have you ever sat down to try to test some code and not known where to start? There&apos;s a bug in production, and you want to reproduce it in a unit test before working on the fix. But what do you even test? Where is the problem?&lt;/p&gt;&lt;p&gt;I run into this issue often in Elm codebases. What code is responsible for the bug? The problem is this: the responsibility is diluted, there&apos;s not a single point of responsibility. Why? Because if anyone &lt;em&gt;can&lt;/em&gt; use the code, if they can depend on it, then they will. And so over time, all the callers of the code become dependent on the internals. The concrete implementation, the data structure. Does it need to have an empty list at the end before you send it off to the server? What&apos;s the name of the field? Does it need to be ordered a certain way when you present it? Do you hide certain entires, or display them a certain way? How can you even test it... there&apos;s no single &lt;em&gt;it&lt;/em&gt;... there are dozens of &quot;it&quot;s across your app in all the places it is used.&lt;/p&gt;&lt;p&gt;So if you don&apos;t define a single clear module that is responsible for a part of your domain, then everyone becomes responsible.&lt;/p&gt;&lt;p&gt;So how can you even test that? Do you test every single invocation? You can&apos;t. The solution is to make it so that there&apos;s only one &lt;em&gt;possible&lt;/em&gt; place where you can call the code. It&apos;s important that you narrow it down, because again, Murphy&apos;s Law for calling code... Any internals that &lt;em&gt;can&lt;/em&gt; be depended on, &lt;em&gt;will&lt;/em&gt; be depended on. So the solution: modules!&lt;/p&gt;&lt;p&gt;How do modules help? Well, modules give us two things. 1) They allow us to organize a set of values/functions/types together. And 2) we can choose to hide some of those as internal details that can&apos;t be used outside the module. That&apos;s literally all that an Elm module does. And those two mechanics unlock a huge tool for avoiding Murphy&apos;s Law of Code Internals.&lt;/p&gt;&lt;p&gt;Let&apos;s say we have a fuzzy search dropdown in our app. We have a bug where the fuzzy searching isn&apos;t handling non-ASCII characters, like é or ø. Where do you go to fix it?&lt;/p&gt;&lt;p&gt;This is the code we&apos;re working with:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;module AdminPage

type Msg
= FuzzyInputText String

fuzzySearchDropdown : Html Msg
fuzzyFilter : String -&amp;gt; Bool
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We have one type of fuzzy filter function that we use in the &lt;code&gt;AdminPage&lt;/code&gt;. But what about other pages? They do fuzzy filtering with a lot of the same logic, but it differs just a little bit.&lt;/p&gt;&lt;p&gt;We can find all the call sites and make sure to update those to fix the bug. And we can make sure that the test case we add is a good representation of all the call sites. Something doesn&apos;t feel right about that somehow. And for good reason, there&apos;s a problem: what about future call sites? This doesn&apos;t give us any confidence that our fuzzy search will &lt;em&gt;stay&lt;/em&gt; fixed. It&apos;s a bug waiting to happen.&lt;/p&gt;&lt;p&gt;It&apos;s essential to think about not only what will this code do now, but what will the compiler and structure of the code guide people towards when they add or change behavior in the future?&lt;/p&gt;&lt;p&gt;And if we fix all the call sites, we don&apos;t even have the confidence that our tests mean anything! What have we even tested? We could write a test that doesn&apos;t give us confidence about all the fuzzy drop downs in our app.&lt;/p&gt;&lt;p&gt;So let&apos;s say that we have a bug where our fuzzy search is not handling unicode characters, and just shows no results if you have a non-ascii character.&lt;/p&gt;&lt;p&gt;What&apos;s the fix?&lt;/p&gt;&lt;p&gt;We need a FuzzyDropdown module.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;module FuzzyDropdown

view : Html Msg
filter : String -&amp;gt; Bool
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Notice that the function names become much shorter because in the context of this module the meaning is clear. That&apos;s a good sign that you&apos;ve found a cohesive grouping (and also a good technique for looking for opportunities for splitting modules).&lt;/p&gt;</content>
<content:encoded><![CDATA[<h1>How do you test code that's not defined in a module?</h1><p>Have you ever sat down to try to test some code and not known where to start? There's a bug in production, and you want to reproduce it in a unit test before working on the fix. But what do you even test? Where is the problem?</p><p>I run into this issue often in Elm codebases. What code is responsible for the bug? The problem is this: the responsibility is diluted, there's not a single point of responsibility. Why? Because if anyone <em>can</em> use the code, if they can depend on it, then they will. And so over time, all the callers of the code become dependent on the internals. The concrete implementation, the data structure. Does it need to have an empty list at the end before you send it off to the server? What's the name of the field? Does it need to be ordered a certain way when you present it? Do you hide certain entires, or display them a certain way? How can you even test it... there's no single <em>it</em>... there are dozens of "it"s across your app in all the places it is used.</p><p>So if you don't define a single clear module that is responsible for a part of your domain, then everyone becomes responsible.</p><p>So how can you even test that? Do you test every single invocation? You can't. The solution is to make it so that there's only one <em>possible</em> place where you can call the code. It's important that you narrow it down, because again, Murphy's Law for calling code... Any internals that <em>can</em> be depended on, <em>will</em> be depended on. So the solution: modules!</p><p>How do modules help? Well, modules give us two things. 1) They allow us to organize a set of values/functions/types together. And 2) we can choose to hide some of those as internal details that can't be used outside the module. That's literally all that an Elm module does. And those two mechanics unlock a huge tool for avoiding Murphy's Law of Code Internals.</p><p>Let's say we have a fuzzy search dropdown in our app. We have a bug where the fuzzy searching isn't handling non-ASCII characters, like é or ø. Where do you go to fix it?</p><p>This is the code we're working with:</p><pre><code>module AdminPage

type Msg
= FuzzyInputText String

fuzzySearchDropdown : Html Msg
fuzzyFilter : String -&gt; Bool
</code></pre><p>We have one type of fuzzy filter function that we use in the <code>AdminPage</code>. But what about other pages? They do fuzzy filtering with a lot of the same logic, but it differs just a little bit.</p><p>We can find all the call sites and make sure to update those to fix the bug. And we can make sure that the test case we add is a good representation of all the call sites. Something doesn't feel right about that somehow. And for good reason, there's a problem: what about future call sites? This doesn't give us any confidence that our fuzzy search will <em>stay</em> fixed. It's a bug waiting to happen.</p><p>It's essential to think about not only what will this code do now, but what will the compiler and structure of the code guide people towards when they add or change behavior in the future?</p><p>And if we fix all the call sites, we don't even have the confidence that our tests mean anything! What have we even tested? We could write a test that doesn't give us confidence about all the fuzzy drop downs in our app.</p><p>So let's say that we have a bug where our fuzzy search is not handling unicode characters, and just shows no results if you have a non-ascii character.</p><p>What's the fix?</p><p>We need a FuzzyDropdown module.</p><pre><code>module FuzzyDropdown

view : Html Msg
filter : String -&gt; Bool
</code></pre><p>Notice that the function names become much shorter because in the context of this module the meaning is clear. That's a good sign that you've found a cohesive grouping (and also a good technique for looking for opportunities for splitting modules).</p>]]></content:encoded>
</item>
<item>
<title>To test, or not to test elm code</title>
<description>Is it as simple as &quot;only test your business logic?&quot;</description>
<link>https://incrementalelm.com/to-test-or-not-to-test</link>
<guid>https://incrementalelm.com/to-test-or-not-to-test</guid>
<pubDate>Wed, 01 Jan 2020 00:00:00 GMT</pubDate>
<content>&lt;p&gt;That... is the question Richard Feldman&apos;s keynote at Elm in the Spring last week explored. Richard talked about the interplay between unit tests and the elm type system. His general advice is the same as I&apos;ve described it:&lt;/p&gt;&lt;h2&gt;&quot;In elm, you only test your business logic.&quot;&lt;/h2&gt;&lt;p&gt;In other words, in the context of a JavaScript app, you have to be really careful to test wiring, and in many ways you have to emulate the role of a type system in your tests.&lt;/p&gt;&lt;p&gt;So a JavaScript/Ruby/other untyped test suite may include checks for:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Does it give an error if you pass an unwanted type as an argument?&lt;/li&gt;&lt;li&gt;Does it ever return null when it shouldn&apos;t?&lt;/li&gt;&lt;li&gt;Does it ever enter into an &quot;Impossible State&quot;&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;What to test in elm?&lt;/h2&gt;&lt;p&gt;So is it as simple as that in elm? You don&apos;t need to unit test your wiring, impossible states, or constraints expressed in your types (e.g. non-nullable)?&lt;/p&gt;&lt;p&gt;Yes, I think it is that simple! It&apos;s important to understand some context first, though. You can write your code in such a way that it doesn&apos;t really seem like business logic. Because the business logic gets buried, so it seems like something else.&lt;/p&gt;&lt;p&gt;It&apos;s common for wiring code to get intimately intertwined with core business logic. For example, you can have a view helper function that knows about how to display currency, or how to apply a discount code to a product. So do you test the view code in order to make sure your business logic is correct?&lt;/p&gt;&lt;h2&gt;Flip it around&lt;/h2&gt;&lt;p&gt;Instead of testing the business logic that&apos;s accessible to test in your code, make the business accessible and test it.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Keep all your interesting business logic decoupled from wiring and display logic.&lt;/li&gt;&lt;li&gt;Given your nice, independent business logic modules, write unit tests for those.&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Test-first guides you to decouple business logic&lt;/h2&gt;&lt;p&gt;Writing unit tests before versus after writing your implementation is fundamentally different. One of the core benefits of Test-Driven Development is that it guides you to keep your business logic decoupled from your wiring and view logic. Because you&apos;re writing tests first, you will naturally write testable code, since you&apos;re thinking about how to test it &lt;em&gt;before&lt;/em&gt; you think about how to implement it.&lt;/p&gt;</content>
<content:encoded><![CDATA[<p>That... is the question Richard Feldman's keynote at Elm in the Spring last week explored. Richard talked about the interplay between unit tests and the elm type system. His general advice is the same as I've described it:</p><h2>"In elm, you only test your business logic."</h2><p>In other words, in the context of a JavaScript app, you have to be really careful to test wiring, and in many ways you have to emulate the role of a type system in your tests.</p><p>So a JavaScript/Ruby/other untyped test suite may include checks for:</p><ul><li>Does it give an error if you pass an unwanted type as an argument?</li><li>Does it ever return null when it shouldn't?</li><li>Does it ever enter into an "Impossible State"</li></ul><h2>What to test in elm?</h2><p>So is it as simple as that in elm? You don't need to unit test your wiring, impossible states, or constraints expressed in your types (e.g. non-nullable)?</p><p>Yes, I think it is that simple! It's important to understand some context first, though. You can write your code in such a way that it doesn't really seem like business logic. Because the business logic gets buried, so it seems like something else.</p><p>It's common for wiring code to get intimately intertwined with core business logic. For example, you can have a view helper function that knows about how to display currency, or how to apply a discount code to a product. So do you test the view code in order to make sure your business logic is correct?</p><h2>Flip it around</h2><p>Instead of testing the business logic that's accessible to test in your code, make the business accessible and test it.</p><ul><li>Keep all your interesting business logic decoupled from wiring and display logic.</li><li>Given your nice, independent business logic modules, write unit tests for those.</li></ul><h2>Test-first guides you to decouple business logic</h2><p>Writing unit tests before versus after writing your implementation is fundamentally different. One of the core benefits of Test-Driven Development is that it guides you to keep your business logic decoupled from your wiring and view logic. Because you're writing tests first, you will naturally write testable code, since you're thinking about how to test it <em>before</em> you think about how to implement it.</p>]]></content:encoded>
</item>
<item>
<title>Types Without Borders Isn&apos;t Enough</title>
<description>When you preserve type information across environments, you know that if it compiles it works. Pairing that with a functional-style API makes it truly delightful.</description>
<link>https://incrementalelm.com/types-without-borders-isnt-enough</link>
<guid>https://incrementalelm.com/types-without-borders-isnt-enough</guid>
<pubDate>Mon, 22 Feb 2021 00:00:00 GMT</pubDate>
<content>&lt;h1&gt;Types Without Borders Isn&apos;t Enough&lt;/h1&gt;&lt;p&gt;At Elm Conf 2018, I gave a talk called [&lt;a title=&quot;Types Without Borders&quot; href=&quot;types-without-borders&quot;&gt;types-without-borders&lt;/a&gt;]. In the talk, I discussed the idea of preserving type information when crossing the boundary between languages or environments.&lt;/p&gt;&lt;p&gt;I gave a demo of two different libraries that follow that principle: &lt;a href=&quot;https://github.com/dillonkearns/elm-graphql&quot;&gt;&lt;code&gt;elm-graphql&lt;/code&gt;&lt;/a&gt;, and &lt;a href=&quot;https://github.com/dillonkearns/elm-typescript-interop&quot;&gt;&lt;code&gt;elm-typescript-interop&lt;/code&gt;&lt;/a&gt;. &lt;code&gt;elm-graphql&lt;/code&gt; has stood the test of time quite well.&lt;/p&gt;&lt;p&gt;&lt;code&gt;elm-typescript-interop&lt;/code&gt; was a solid idea at the time, but it missed something fundamentally that &lt;code&gt;elm-graphql&lt;/code&gt; got right. So I&apos;m rethinking it from scratch and introducing a new incarnation of that project that I&apos;m, creatively, calling &lt;code&gt;elm-ts-interop&lt;/code&gt;. In this post, I&apos;ll explore the missing piece, which needed a fresh look after a few years to discover: using a Combinator approach. I wrote about Combinators in-depth in last week&apos;s tip, [&lt;a title=&quot;Combinators - Inverting Top-Down Transforms&quot; href=&quot;combinators&quot;&gt;combinators&lt;/a&gt;]. Before I explain the new approach in &lt;code&gt;elm-ts-interop&lt;/code&gt;, let me describe the original approach of &lt;code&gt;elm-typescript-interop&lt;/code&gt;.&lt;/p&gt;&lt;h2&gt;The original elm-typescript-interop&lt;/h2&gt;&lt;p&gt;The idea of &lt;code&gt;elm-typescript-interop&lt;/code&gt; is to look at your Elm source code and find all the ports and their type information. In Elm, a port is a way to send messages to or from JavaScript. Since JavaScript doesn&apos;t have the same guarantees that Elm does, ports are a way to have an environment with sound types and no runtime exceptions. But you can still communicate with JavaScript, just by sending messages with the concept of the Actor Model - messages can go out, and messages can come in.&lt;/p&gt;&lt;p&gt;You define a port with a type annotation like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;-- this gives you a function to send a message to JavaScript
reportEvent : { title : String, message : String, kind : String } -&amp;gt; Cmd msg

-- this gives you a subscription to listen for messages from JavaScript
gotLocalStorage : ( { key : String, value : Json.Decode.Value } -&amp;gt; msg ) -&amp;gt; Sub msg
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can learn more in the &lt;a href=&quot;https://guide.elm-lang.org/interop/ports.html&quot;&gt;Elm Guide&apos;s section on ports&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;And in your app, you would call&lt;/p&gt;&lt;pre&gt;&lt;code&gt;reportEvent
    { title = &quot;Could not find that discount code&quot;
    , message = &quot;Could not found discount code &quot; ++ discountCode ++ &quot;.&quot;
    , kind = &quot;Warning&quot;
    }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;elm-typescript-interop&lt;/code&gt; takes that type information and generates TypeScript type definitions for setting up your Elm code. This is quite handy because it gives you&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Intellisense for autocompletions when you wire up your ports in your JavaScript entrypoint&lt;/li&gt;&lt;li&gt;TypeScript errors if you pass in incorrect data&lt;/li&gt;&lt;li&gt;TypeScript type information about the incoming data from Elm&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Wiring up your ports looks something like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;const app = Elm.Main.init({ flags: flagData });

app.ports.reportEvent.subscribe(function (data) {
  // report event based on `data` we got from Elm
});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;elm-typescript-interop&lt;/code&gt; generates TypeScript declarations to bring some type-safety to this process. In hindsight, this type-safety is great, but the overall approach misses the bigger picture.&lt;/p&gt;&lt;h2&gt;The problem with the original approach&lt;/h2&gt;&lt;p&gt;People would often ask, &quot;what&apos;s the best way to send your Elm Custom Types through a port?&quot; Elm automatically sends basic types through ports, like &lt;code&gt;String&lt;/code&gt;, &lt;code&gt;Int&lt;/code&gt;, records, lists, etc. You can also send generic JSON data through a port. You&apos;ll have to use the &lt;code&gt;elm/json&lt;/code&gt; package to turn your data to or from typed Elm data. If you&apos;re wondering why Elm doesn&apos;t just automatically convert any type to JSON, Evan Czaplicki&apos;s document describing his &lt;a href=&quot;https://gist.github.com/evancz/1c5f2cf34939336ecb79b97bb89d9da6&quot;&gt;vision for data interchange&lt;/a&gt; is worth a read.&lt;/p&gt;&lt;p&gt;Initially, I thought that I would eventually come up with a way to automatically serialize more advanced Elm types, again by statically analyzing your Elm source code. Then you could serialize that data into TypeScript types automatically. I tried sketching out some design ideas, but never came up with anything that felt satisfying.&lt;/p&gt;&lt;p&gt;So what that left you with was using &lt;code&gt;elm-typescript-interop&lt;/code&gt; to be a serialization/de-serialization layer. But you would then need a second layer to convert that into proper Elm data.&lt;/p&gt;&lt;p&gt;Let&apos;s take our example from above&lt;/p&gt;&lt;pre&gt;&lt;code&gt;reportEvent
    { title = &quot;Could not find that discount code&quot;
    , message =
        &quot;Could not found discount code &quot;
            ++ discountCode
            ++ &quot;. Please try again.&quot;
    , kind = &quot;Warning&quot;
    }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What if our ideal data type in Elm doesn&apos;t have that exact shape that we want to send to TypeScript? Let&apos;s say our ideal Elm type is this&lt;/p&gt;&lt;pre&gt;&lt;code&gt;type Error
  = FatalError ErrorCode
  | Warning { title : String, message : String }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we need a translation function to turn that data type into a format that our port can serialize.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;reportElmErrorType : Error -&amp;gt; Cmd msg
reportElmErrorType error =
    case error of
        FatalError errorCode -&amp;gt;
            reportEvent
                { title = &quot;Internal Error&quot;
                , message =
                    &quot;Please contact support with code &quot;
                        ++ errorCodeToString errorCode
                , kind = &quot;Error&quot;
                }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Just as we couldn&apos;t use our ideal data type in Elm because it used an advanced type that can&apos;t be automatically serialized, our ideal TypeScript type that we want to serialize to can&apos;t be expressed directly. We can&apos;t use expressive TypeScript types like &lt;a href=&quot;https://basarat.gitbook.io/typescript/type-system/discriminated-unions&quot;&gt;discriminated unions&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;But specific limitations with serializing to/from advanced data types isn&apos;t the root problem. Regardless of those limitations, we can&apos;t assume that the data format needed in TypeScript and the ideal format in our Elm code are the same. So we need to transfer the data, while also transforming it. If you read yesterday&apos;s post, this may all ring a bell. You guessed it - it&apos;s time for a Combinator!&lt;/p&gt;&lt;h2&gt;Getting the best of both worlds with Combinators&lt;/h2&gt;&lt;p&gt;Let&apos;s say our ideal type that TypeScript would like to work with looks like this&lt;/p&gt;&lt;pre&gt;&lt;code&gt;type Event = Error | PageNavigation;
type Error = {
  kind: &quot;Error&quot;;
  errorId?: string;
  message: string;
  context?: string;
};

type PageNavigation = {
  kind: &quot;PageNavigation&quot;;
  path: string;
};
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Our &lt;code&gt;elm-ts-interop&lt;/code&gt; &lt;code&gt;Encoder&lt;/code&gt; would look something like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;import Json.Encode as JE
import TsJson.Encode as TsEncode exposing (Encoder)


type Event
    = ErrorEvent Error
    | PageNavigationEvent Url


type alias Url =
    { path : String }


eventEncoder : Encoder Event
eventEncoder =
    TsEncode.union
        (\vPageNavigation vError value -&amp;gt;
            case value of
                PageNavigationEvent url -&amp;gt;
                    vPageNavigation url

                ErrorEvent errorData -&amp;gt;
                    vError errorData
        )
        |&amp;gt; TsEncode.variant pageNavigationEncoder
        |&amp;gt; TsEncode.variant errorEncoder
        |&amp;gt; TsEncode.buildUnion


pageNavigationEncoder : Encoder Url
pageNavigationEncoder =
    TsEncode.object
        [ TsEncode.required &quot;kind&quot; identity (TsEncode.literal &amp;lt;| JE.string &quot;PageNavigation&quot;)
        , Encode.required &quot;path&quot; .path Encode.string
        ]


errorEncoder : Encoder Error
errorEncoder =
    rawErrorEncoder
        |&amp;gt; TsEncode.map
            (\value -&amp;gt;
                case value of
                    FatalError errorCode -&amp;gt;
                        { errorId = Just (errorCodeToString errorCode)
                        , message = &quot;Fatal error.&quot;
                        , context = Nothing
                        }

                    Warning details -&amp;gt;
                        { errorId = Nothing
                        , message = details.message
                        , context = Nothing
                        }
            )


rawErrorEncoder :
    Encoder
        { errorId : Maybe String
        , message : String
        , context : Maybe String
        }
rawErrorEncoder =
    TsEncode.object
        [ TsEncode.optional &quot;errorId&quot; .errorId TsEncode.string
        , TsEncode.required &quot;message&quot; .message TsEncode.string
        , TsEncode.optional &quot;context&quot; .context TsEncode.string
        , TsEncode.required &quot;kind&quot; identity (TsEncode.literal &amp;lt;| JE.string &quot;Error&quot;)
        ]

&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Just by writing this Elm code, we have the exact TypeScript type &lt;code&gt;type Event = Error | PageNavigation&lt;/code&gt; that was our ideal TypeScript type! This allows us to express much more nuanced types between Elm and TypeScript (in this case we have a TypeScript Discriminated Union, for example). The type information from this &lt;code&gt;Encoder&lt;/code&gt; is synced by running an &lt;code&gt;elm-ts-interop&lt;/code&gt; command-line tool.&lt;/p&gt;&lt;p&gt;This &lt;code&gt;Encoder&lt;/code&gt; does two things:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Acts as an Adaptor to get the formats to line up (think American-to-European power adaptor)&lt;/li&gt;&lt;li&gt;Preserves type information on both sides as it does this!&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This is crucial. In building up the adaptor, the API builds up type information about what data it expects to come in, and what data types can go out. This is the missing piece from Types Without Borders! Why is this so important? There are several benefits:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;We can express much more nuanced type information through this API&lt;/li&gt;&lt;li&gt;This is a Combinator, so we can build up very complex transformations by composing together easy-to-understand building blocks (see yesterday&apos;s post on Combinators, [&lt;a title=&quot;Combinators - Inverting Top-Down Transforms&quot; href=&quot;combinators&quot;&gt;combinators&lt;/a&gt;])&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Because of this new approach, we now have the added benefit that we can use a single Elm port to send all possible messages to TypeScript, and a single port to receive all messages from TypeScript.&lt;/p&gt;&lt;p&gt;That means instead of remembering to register each port, but getting no warning if we forget one, we can use TypeScript to guarantee that we&apos;ve handled every incoming message!&lt;/p&gt;&lt;pre&gt;&lt;code&gt;app.ports.fromElm.subscribe((fromElm) =&amp;gt; {
  switch (fromElm.tag) {
    case &quot;reportEvent&quot;:
      AnalyticsService.notify(fromElm.data);
      break;
    case &quot;scrollIntoView&quot;:
      document.getElementById(fromElm.id)?.scrollIntoView(fromElm.data);
      break;
    // exhaustive check will fail if there are unhandled cases
  }
});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Again, this is because of the power of the Combinator pattern. Since a Combinator is just built up of other Combinators, we can build up very complex data serialization out of smaller pieces, and then &lt;em&gt;combine&lt;/em&gt; them into one type that&apos;s nice to work with in an &lt;a href=&quot;https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/switch-exhaustiveness-check.md&quot;&gt;exhaustive switch statement&lt;/a&gt;!&lt;/p&gt;&lt;h2&gt;Sneak peak of elm-ts-interop&lt;/h2&gt;&lt;p&gt;Thanks for reading! I&apos;ll be releasing &lt;code&gt;elm-ts-interop&lt;/code&gt; in 1 week (March 1, 2021)!, so stay tuned. If you want a sneak peak, you can browse Elm package documentation &lt;a href=&quot;https://package.elm-lang.org/packages/dillonkearns/elm-ts-json/latest/&quot;&gt;preview of the documentation&lt;/a&gt;. I&apos;d love to hear your thoughts. Let me know what you think on Twitter &lt;a href=&quot;https://twitter.com/dillontkearns&quot;&gt;@dillontkearns&lt;/a&gt;!&lt;/p&gt;</content>
<content:encoded><![CDATA[<h1>Types Without Borders Isn't Enough</h1><p>At Elm Conf 2018, I gave a talk called [<a title="Types Without Borders" href="types-without-borders">types-without-borders</a>]. In the talk, I discussed the idea of preserving type information when crossing the boundary between languages or environments.</p><p>I gave a demo of two different libraries that follow that principle: <a href="https://github.com/dillonkearns/elm-graphql"><code>elm-graphql</code></a>, and <a href="https://github.com/dillonkearns/elm-typescript-interop"><code>elm-typescript-interop</code></a>. <code>elm-graphql</code> has stood the test of time quite well.</p><p><code>elm-typescript-interop</code> was a solid idea at the time, but it missed something fundamentally that <code>elm-graphql</code> got right. So I'm rethinking it from scratch and introducing a new incarnation of that project that I'm, creatively, calling <code>elm-ts-interop</code>. In this post, I'll explore the missing piece, which needed a fresh look after a few years to discover: using a Combinator approach. I wrote about Combinators in-depth in last week's tip, [<a title="Combinators - Inverting Top-Down Transforms" href="combinators">combinators</a>]. Before I explain the new approach in <code>elm-ts-interop</code>, let me describe the original approach of <code>elm-typescript-interop</code>.</p><h2>The original elm-typescript-interop</h2><p>The idea of <code>elm-typescript-interop</code> is to look at your Elm source code and find all the ports and their type information. In Elm, a port is a way to send messages to or from JavaScript. Since JavaScript doesn't have the same guarantees that Elm does, ports are a way to have an environment with sound types and no runtime exceptions. But you can still communicate with JavaScript, just by sending messages with the concept of the Actor Model - messages can go out, and messages can come in.</p><p>You define a port with a type annotation like this:</p><pre><code>-- this gives you a function to send a message to JavaScript
reportEvent : { title : String, message : String, kind : String } -&gt; Cmd msg

-- this gives you a subscription to listen for messages from JavaScript
gotLocalStorage : ( { key : String, value : Json.Decode.Value } -&gt; msg ) -&gt; Sub msg
</code></pre><p>You can learn more in the <a href="https://guide.elm-lang.org/interop/ports.html">Elm Guide's section on ports</a>.</p><p>And in your app, you would call</p><pre><code>reportEvent
    { title = "Could not find that discount code"
    , message = "Could not found discount code " ++ discountCode ++ "."
    , kind = "Warning"
    }
</code></pre><p><code>elm-typescript-interop</code> takes that type information and generates TypeScript type definitions for setting up your Elm code. This is quite handy because it gives you</p><ul><li>Intellisense for autocompletions when you wire up your ports in your JavaScript entrypoint</li><li>TypeScript errors if you pass in incorrect data</li><li>TypeScript type information about the incoming data from Elm</li></ul><p>Wiring up your ports looks something like this:</p><pre><code>const app = Elm.Main.init({ flags: flagData });

app.ports.reportEvent.subscribe(function (data) {
  // report event based on `data` we got from Elm
});
</code></pre><p><code>elm-typescript-interop</code> generates TypeScript declarations to bring some type-safety to this process. In hindsight, this type-safety is great, but the overall approach misses the bigger picture.</p><h2>The problem with the original approach</h2><p>People would often ask, "what's the best way to send your Elm Custom Types through a port?" Elm automatically sends basic types through ports, like <code>String</code>, <code>Int</code>, records, lists, etc. You can also send generic JSON data through a port. You'll have to use the <code>elm/json</code> package to turn your data to or from typed Elm data. If you're wondering why Elm doesn't just automatically convert any type to JSON, Evan Czaplicki's document describing his <a href="https://gist.github.com/evancz/1c5f2cf34939336ecb79b97bb89d9da6">vision for data interchange</a> is worth a read.</p><p>Initially, I thought that I would eventually come up with a way to automatically serialize more advanced Elm types, again by statically analyzing your Elm source code. Then you could serialize that data into TypeScript types automatically. I tried sketching out some design ideas, but never came up with anything that felt satisfying.</p><p>So what that left you with was using <code>elm-typescript-interop</code> to be a serialization/de-serialization layer. But you would then need a second layer to convert that into proper Elm data.</p><p>Let's take our example from above</p><pre><code>reportEvent
    { title = "Could not find that discount code"
    , message =
        "Could not found discount code "
            ++ discountCode
            ++ ". Please try again."
    , kind = "Warning"
    }
</code></pre><p>What if our ideal data type in Elm doesn't have that exact shape that we want to send to TypeScript? Let's say our ideal Elm type is this</p><pre><code>type Error
  = FatalError ErrorCode
  | Warning { title : String, message : String }
</code></pre><p>Now we need a translation function to turn that data type into a format that our port can serialize.</p><pre><code>reportElmErrorType : Error -&gt; Cmd msg
reportElmErrorType error =
    case error of
        FatalError errorCode -&gt;
            reportEvent
                { title = "Internal Error"
                , message =
                    "Please contact support with code "
                        ++ errorCodeToString errorCode
                , kind = "Error"
                }
</code></pre><p>Just as we couldn't use our ideal data type in Elm because it used an advanced type that can't be automatically serialized, our ideal TypeScript type that we want to serialize to can't be expressed directly. We can't use expressive TypeScript types like <a href="https://basarat.gitbook.io/typescript/type-system/discriminated-unions">discriminated unions</a>.</p><p>But specific limitations with serializing to/from advanced data types isn't the root problem. Regardless of those limitations, we can't assume that the data format needed in TypeScript and the ideal format in our Elm code are the same. So we need to transfer the data, while also transforming it. If you read yesterday's post, this may all ring a bell. You guessed it - it's time for a Combinator!</p><h2>Getting the best of both worlds with Combinators</h2><p>Let's say our ideal type that TypeScript would like to work with looks like this</p><pre><code>type Event = Error | PageNavigation;
type Error = {
  kind: "Error";
  errorId?: string;
  message: string;
  context?: string;
};

type PageNavigation = {
  kind: "PageNavigation";
  path: string;
};
</code></pre><p>Our <code>elm-ts-interop</code> <code>Encoder</code> would look something like this:</p><pre><code>import Json.Encode as JE
import TsJson.Encode as TsEncode exposing (Encoder)


type Event
    = ErrorEvent Error
    | PageNavigationEvent Url


type alias Url =
    { path : String }


eventEncoder : Encoder Event
eventEncoder =
    TsEncode.union
        (\vPageNavigation vError value -&gt;
            case value of
                PageNavigationEvent url -&gt;
                    vPageNavigation url

                ErrorEvent errorData -&gt;
                    vError errorData
        )
        |&gt; TsEncode.variant pageNavigationEncoder
        |&gt; TsEncode.variant errorEncoder
        |&gt; TsEncode.buildUnion


pageNavigationEncoder : Encoder Url
pageNavigationEncoder =
    TsEncode.object
        [ TsEncode.required "kind" identity (TsEncode.literal &lt;| JE.string "PageNavigation")
        , Encode.required "path" .path Encode.string
        ]


errorEncoder : Encoder Error
errorEncoder =
    rawErrorEncoder
        |&gt; TsEncode.map
            (\value -&gt;
                case value of
                    FatalError errorCode -&gt;
                        { errorId = Just (errorCodeToString errorCode)
                        , message = "Fatal error."
                        , context = Nothing
                        }

                    Warning details -&gt;
                        { errorId = Nothing
                        , message = details.message
                        , context = Nothing
                        }
            )


rawErrorEncoder :
    Encoder
        { errorId : Maybe String
        , message : String
        , context : Maybe String
        }
rawErrorEncoder =
    TsEncode.object
        [ TsEncode.optional "errorId" .errorId TsEncode.string
        , TsEncode.required "message" .message TsEncode.string
        , TsEncode.optional "context" .context TsEncode.string
        , TsEncode.required "kind" identity (TsEncode.literal &lt;| JE.string "Error")
        ]

</code></pre><p>Just by writing this Elm code, we have the exact TypeScript type <code>type Event = Error | PageNavigation</code> that was our ideal TypeScript type! This allows us to express much more nuanced types between Elm and TypeScript (in this case we have a TypeScript Discriminated Union, for example). The type information from this <code>Encoder</code> is synced by running an <code>elm-ts-interop</code> command-line tool.</p><p>This <code>Encoder</code> does two things:</p><ul><li>Acts as an Adaptor to get the formats to line up (think American-to-European power adaptor)</li><li>Preserves type information on both sides as it does this!</li></ul><p>This is crucial. In building up the adaptor, the API builds up type information about what data it expects to come in, and what data types can go out. This is the missing piece from Types Without Borders! Why is this so important? There are several benefits:</p><ul><li>We can express much more nuanced type information through this API</li><li>This is a Combinator, so we can build up very complex transformations by composing together easy-to-understand building blocks (see yesterday's post on Combinators, [<a title="Combinators - Inverting Top-Down Transforms" href="combinators">combinators</a>])</li></ul><p>Because of this new approach, we now have the added benefit that we can use a single Elm port to send all possible messages to TypeScript, and a single port to receive all messages from TypeScript.</p><p>That means instead of remembering to register each port, but getting no warning if we forget one, we can use TypeScript to guarantee that we've handled every incoming message!</p><pre><code>app.ports.fromElm.subscribe((fromElm) =&gt; {
  switch (fromElm.tag) {
    case "reportEvent":
      AnalyticsService.notify(fromElm.data);
      break;
    case "scrollIntoView":
      document.getElementById(fromElm.id)?.scrollIntoView(fromElm.data);
      break;
    // exhaustive check will fail if there are unhandled cases
  }
});
</code></pre><p>Again, this is because of the power of the Combinator pattern. Since a Combinator is just built up of other Combinators, we can build up very complex data serialization out of smaller pieces, and then <em>combine</em> them into one type that's nice to work with in an <a href="https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/switch-exhaustiveness-check.md">exhaustive switch statement</a>!</p><h2>Sneak peak of elm-ts-interop</h2><p>Thanks for reading! I'll be releasing <code>elm-ts-interop</code> in 1 week (March 1, 2021)!, so stay tuned. If you want a sneak peak, you can browse Elm package documentation <a href="https://package.elm-lang.org/packages/dillonkearns/elm-ts-json/latest/">preview of the documentation</a>. I'd love to hear your thoughts. Let me know what you think on Twitter <a href="https://twitter.com/dillontkearns">@dillontkearns</a>!</p>]]></content:encoded>
</item>
<item>
<title>TypeScript&apos;s Blind Spots</title>
<description>It&apos;s hard to know when to trust TypeScript for Elm developers who are used to a sound type system. It helps to know where its blind spots are.</description>
<link>https://incrementalelm.com/typescript-blind-spots</link>
<guid>https://incrementalelm.com/typescript-blind-spots</guid>
<pubDate>Tue, 09 Feb 2021 00:00:00 GMT</pubDate>
<content>&lt;h1&gt;TypeScript&apos;s Blind Spots&lt;/h1&gt;&lt;p&gt;It would be quite dangerous to drive a car with blind spots, but not know exactly where those blind spots are. You still need to be extra careful, even with a full awareness of the blind spots. But at least you know where to put that extra care.&lt;/p&gt;&lt;p&gt;I look at using TypeScript in much the same way. Knowing its blind spots helps me know when to trust it and when to quadruple check. If you can drive a car without blind spots, then by all means go for the safest option. But if you are in a car with blind spots, learn its blind spots to reduce the risk. As Elm developers, we stay in Elm as much as we can. But we do need to reach out to JS/TS to leverage built-in web APIs, use the NPM ecosystem, etc. So for those times we need it, it&apos;s best to understand the blind spots.&lt;/p&gt;&lt;p&gt;In this post, I&apos;ll catalog the areas where TypeScript allows incorrect types to pass through.&lt;/p&gt;&lt;h2&gt;The &lt;code&gt;any&lt;/code&gt; type&lt;/h2&gt;&lt;p&gt;The biggest culprit for type blind spots in TypeScript is the &lt;code&gt;any&lt;/code&gt; type.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;any&lt;/code&gt; type is similar to Elm&apos;s &lt;code&gt;Debug.todo &quot;&quot;&lt;/code&gt;, which has type &lt;code&gt;a&lt;/code&gt; (a type variable that could refer to anything). But there are some key differences:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;Debug.todo&lt;/code&gt; statements can&apos;t be used in production optimized code (&lt;code&gt;elm make --optimize&lt;/code&gt;)&lt;/li&gt;&lt;li&gt;&lt;code&gt;Debug.todo&lt;/code&gt; can&apos;t be published in Elm packages&lt;/li&gt;&lt;li&gt;Even if you managed to use &lt;code&gt;Debug.todo&lt;/code&gt;, your code stops there!&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This is a huge distinction. With Elm, even if you manage to &quot;fool&quot; the type system with a &lt;code&gt;Debug.todo&lt;/code&gt; (or a hack like infinitely recursing to get any type), your code will not proceed with faulty assumptions. It&apos;s impossible to write Elm code that will continue on with incorrect types.&lt;/p&gt;&lt;p&gt;As an Elm developer, using TypeScript can feel like shaky ground because of this escape hatch. It&apos;s good to understand common sources of &lt;code&gt;any&lt;/code&gt; types to know where to look out for this kind of blind spot.&lt;/p&gt;&lt;h3&gt;Implicit &lt;code&gt;any&lt;/code&gt;&lt;/h3&gt;&lt;p&gt;By default, TypeScript will use an &lt;code&gt;any&lt;/code&gt; to describe types that it can&apos;t infer, like function parameters without annotations.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;function inspect(argument) {
  argument(&quot;Careful! `argument` is implicitly `any`&quot;);
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Using the &lt;code&gt;noImplicitAny&lt;/code&gt; option in your &lt;code&gt;tsconfig.json&lt;/code&gt; prevents this problem by giving an error in these situations and requires you to add an annotation. I recommend using the &lt;code&gt;strict&lt;/code&gt; option in your &lt;code&gt;tsconfig.json&lt;/code&gt;, which includes the &lt;code&gt;noImplicitAny&lt;/code&gt; option.&lt;/p&gt;&lt;h3&gt;Explicit &lt;code&gt;any&lt;/code&gt;&lt;/h3&gt;&lt;p&gt;Even with the &lt;code&gt;noImplicitAny&lt;/code&gt; option, TypeScript will allow you to use &lt;em&gt;explicit&lt;/em&gt; &lt;code&gt;any&lt;/code&gt; types. One useful technique here is to use the &lt;code&gt;unknown&lt;/code&gt; type as a substitute for &lt;code&gt;any&lt;/code&gt;. You can think of &lt;code&gt;unknown&lt;/code&gt; as a type-safe alternative to &lt;code&gt;any&lt;/code&gt;. &lt;code&gt;unknown&lt;/code&gt; doesn&apos;t create a blind spot in the type system that allows anything to flow through. Rather, it is much like a &lt;code&gt;Json.Decode.Value&lt;/code&gt; in Elm. You can&apos;t use it until you check its types.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;function prettyPrint(value: unknown): string | null {
  if (value === null) {
    return &quot;&amp;lt;null&amp;gt;&quot;;
  } else if (value === undefined) {
    return &quot;&amp;lt;undefined&amp;gt;&quot;;
  } else if (typeof value === &quot;string&quot;) {
    return value;
  } else if (value &amp;amp;&amp;amp; typeof value === &quot;object&quot;) {
    return &quot;Object with keys: &quot; + Object.keys(value).join(&quot;, &quot;);
  } else {
    return null;
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can pass any value in to &lt;code&gt;prettyPrint&lt;/code&gt;, just like you would be able to if the argument was annotated as &lt;code&gt;any&lt;/code&gt;. But you must check the value before using it because it is &lt;code&gt;unknown&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;You can use &lt;a href=&quot;https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-explicit-any.md&quot;&gt;&lt;code&gt;@typescript-eslint/no-explicit-any&lt;/code&gt;&lt;/a&gt; to prevent explicit &lt;code&gt;any&lt;/code&gt; types in your own code. You can even configure it to auto-fix &lt;code&gt;any&lt;/code&gt; types in your code and change them to &lt;code&gt;unknown&lt;/code&gt;.&lt;/p&gt;&lt;h3&gt;&lt;code&gt;any&lt;/code&gt; in core JavaScript APIs&lt;/h3&gt;&lt;p&gt;Some of the core JavaScript APIs have built-in &lt;code&gt;any&lt;/code&gt; types. &lt;code&gt;Json.parse&lt;/code&gt; is a common source of &lt;code&gt;any&lt;/code&gt;&apos;s leaking into your code:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;JSON.parse(text: string): any
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;const definitelyNotAFunction = JSON.parse(
  `&quot;Parsing JSON can never return a function, right?&quot;`
);
definitelyNotAFunction();
// TypeError: definitelyNotAFunction is not a function
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Yikes! Not only can you assume the parsed JSON is any valid JSON value, but you can even treat it as something besides JSON! Even worse, this can escape into the depths of your code and result in type errors that are far away from their point of origin. In a nutshell, &lt;code&gt;any&lt;/code&gt; makes it hard to trust &lt;em&gt;any&lt;/em&gt; of your code.&lt;/p&gt;&lt;p&gt;You can improve the safety of JSON parsing with tools that are similar to &lt;code&gt;elm/json&lt;/code&gt; decoding.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/gcanti/io-ts/blob/master/index.md&quot;&gt;&lt;code&gt;io-ts&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ajv.js.org/&quot;&gt;&lt;code&gt;ajv&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/pelotom/runtypes&quot;&gt;&lt;code&gt;runtypes&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;There are many different approaches to safer JSON parsing in TypeScript, and it&apos;s worth looking at different options and seeing if one is a good fit for your use case and has an API that suites your tastes.&lt;/p&gt;&lt;p&gt;Be on the lookout for &lt;code&gt;any&lt;/code&gt; types in core JS APIs. Any time you&apos;re dealing with dynamic runtime data you&apos;re likely to see this. The &lt;code&gt;eval&lt;/code&gt; function is a classic case of a function that truly does have &lt;code&gt;any&lt;/code&gt; type. That&apos;s a reminder of why &lt;code&gt;any&lt;/code&gt; is baked into TypeScript: JavaScript is inherently dynamic and dynamically typed.&lt;/p&gt;&lt;h2&gt;&lt;code&gt;any&lt;/code&gt; types coming from NPM packages or type definitions&lt;/h2&gt;&lt;p&gt;Also be aware that TypeScript&apos;s declaration functionality allows you to declare the types of existing JavaScript code. These type declarations (usually bundled with an NPM package, or installed with &lt;code&gt;npm install --save-dev @types/&amp;lt;package-name&amp;gt;&lt;/code&gt;) often use &lt;code&gt;any&lt;/code&gt; types. And even without using &lt;code&gt;any&lt;/code&gt;, they could be incorrect because they are declaring types, not &lt;em&gt;proving&lt;/em&gt; them soundly through typed code, as we are forced to do in Elm code and Elm packages.&lt;/p&gt;&lt;h2&gt;Imports&lt;/h2&gt;&lt;p&gt;Another place to be extra careful is the wiring of imports. When you do an &lt;code&gt;import&lt;/code&gt; in Elm, you know that it has resolved if your code compiles. With TypeScript, you can describe how to resolve modules using the &lt;a href=&quot;https://www.typescriptlang.org/tsconfig#paths&quot;&gt;&lt;code&gt;paths&lt;/code&gt; configuration&lt;/a&gt; in your &lt;code&gt;tsconfig.json&lt;/code&gt;. This can be convenient or necessary at times, but it&apos;s another instance where the TypeScript compiler trusts you to do things correctly and guarantee correctness.&lt;/p&gt;&lt;p&gt;The same is true for defining the &lt;a href=&quot;https://www.typescriptlang.org/tsconfig#lib&quot;&gt;&lt;code&gt;lib&lt;/code&gt; option&lt;/a&gt; to describe the types/runtime that you have available (for example, &lt;code&gt;dom&lt;/code&gt; APIs, &lt;code&gt;es2020&lt;/code&gt;, &lt;code&gt;node&lt;/code&gt;). TypeScript takes your word for setting this up correctly, but this could lead to incorrect type checking if you have &lt;code&gt;lib&lt;/code&gt;s declared that won&apos;t be there in your runtime.&lt;/p&gt;&lt;p&gt;The type systems in Elm and TypeScript are operating in fundamentally different ways, built to serve their intended use cases.&lt;/p&gt;&lt;p&gt;Elm is a sandbox where nothing can enter with assumptions about its types. You have to prove everything to the Elm type system.&lt;/p&gt;&lt;p&gt;TypeScript is built in a way that&apos;s meant to be good at gradual typing, allowing you to add type information to existing JavaScript code in an incremental way.&lt;/p&gt;&lt;h2&gt;Runtime exceptions&lt;/h2&gt;&lt;p&gt;Any TypeScript code can throw an exception without the types indicating they might fail.&lt;/p&gt;&lt;p&gt;There&apos;s no way to tell by the types, which feels very different than Elm&apos;s errors as data. In particular, be on the lookout for functions around file IO and network requests.&lt;/p&gt;&lt;h2&gt;Mutation by reference&lt;/h2&gt;&lt;p&gt;If two references point to the same object reference, you can mutate the references according to their respective types, but they both modify the shared reference. This can lead to incorrect runtime types.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;let numbers = [1, 2, 3];
let mixedValues: (number | string)[] = numbers;
mixedValues.push(&quot;Uh oh, this is not a number&quot;);

numbers.map((number) =&amp;gt; number * 2);
// [2, 4, 6, NaN]
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Index access&lt;/h2&gt;&lt;p&gt;TypeScript assumes that the index you access is there.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;const thisWillBeNull = [1, 2, 3][100];
thisWillBeNull * 2; // NaN
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The type of &lt;code&gt;thisWillBeNull&lt;/code&gt; is &lt;code&gt;number&lt;/code&gt; (it is not nullable).&lt;/p&gt;&lt;h2&gt;Inexhaustive switch statements&lt;/h2&gt;&lt;p&gt;By default, TypeScript doesn&apos;t require you to exhaustively handle all cases in &lt;code&gt;switch&lt;/code&gt; statements.&lt;/p&gt;&lt;p&gt;This can be improved with &lt;a href=&quot;https://github.com/typescript-eslint/typescript-eslint/blob/9e0f6ddef7cd29f355f398c90f1986e51c4854f7/packages/eslint-plugin/docs/rules/switch-exhaustiveness-check.md&quot;&gt;&lt;code&gt;typescript-eslint&lt;/code&gt; &lt;code&gt;switch-exhaustiveness-check&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;Non-null assertions&lt;/h2&gt;&lt;p&gt;TypeScript&apos;s non-null assertion syntax (&lt;code&gt;!&lt;/code&gt;) is another place where TypeScript trusts you rather than proving correctness. You can disallow this in your own code with &lt;a href=&quot;https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-non-null-assertion.md&quot;&gt;&lt;code&gt;@typescript-eslint/no-non-null-assertion&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;Casts&lt;/h2&gt;&lt;p&gt;Casts, also known as TypeAssertions, are another place where the TypeScript compiler will trust you and not check types. See &lt;a href=&quot;https://basarat.gitbook.io/typescript/type-system/type-assertion#assertion-considered-harmful&quot;&gt;Assertion Considered Harmul&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;Type Coercion&lt;/h2&gt;&lt;p&gt;There are several places where JavaScript will coerce types. This is often used as a feature, but it is bug-prone and warrants extra care.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Falsy values used in conditionals (see &lt;a href=&quot;https://basarat.gitbook.io/typescript/recap/truthy&quot;&gt;this handy table of truthy and falsy values&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;String concatenation&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Gary Bernhardt&apos;s famous &lt;a href=&quot;https://www.destroyallsoftware.com/talks/wat&quot;&gt;Wat video&lt;/a&gt; shows some amusing examples of unexpected type coercion. The type system doesn&apos;t stop most of these, so they&apos;re worth extra attention.&lt;/p&gt;&lt;h2&gt;Can you trust an unsound type system?&lt;/h2&gt;&lt;p&gt;Any chipping away at confidence means that you have to have a skeptical eye at every turn.&lt;/p&gt;&lt;p&gt;Let&apos;s think of it in reverse, though. Unit tests don&apos;t give us 100% certainty that our code is correct. Even Elm types don&apos;t give us 100% certainty that our types our correct. There are plenty of cases where we don&apos;t express every single constraint in the type system, and there&apos;s a point where it becomes so verbose to constrain your types that its more pragmatic to stop short of perfect types. Jeroen and I discuss this in our &lt;a href=&quot;https://elm-radio.com/episode/impossible-states&quot;&gt;Make Impossible States Impossible Elm Radio episode&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Yes, TypeScript&apos;s type system is far less robust than Elm&apos;s. But it&apos;s far &lt;em&gt;more&lt;/em&gt; robust than the type-safety you get in vanilla JavaScript! And I&apos;ll take all the added safety I can get! So any time I find myself reaching for JavaScript, I try to take advantage of TypeScript&apos;s added safety.&lt;/p&gt;&lt;p&gt;As much as I like to strive for soundness, correctness, and certainty, I will take any tool that can boost my confidence. None of these tools guarantee correctness at every level. But if you know what guarantees they offer, you can understand what suite of tools to use to give you as much confidence as possible.&lt;/p&gt;</content>
<content:encoded><![CDATA[<h1>TypeScript's Blind Spots</h1><p>It would be quite dangerous to drive a car with blind spots, but not know exactly where those blind spots are. You still need to be extra careful, even with a full awareness of the blind spots. But at least you know where to put that extra care.</p><p>I look at using TypeScript in much the same way. Knowing its blind spots helps me know when to trust it and when to quadruple check. If you can drive a car without blind spots, then by all means go for the safest option. But if you are in a car with blind spots, learn its blind spots to reduce the risk. As Elm developers, we stay in Elm as much as we can. But we do need to reach out to JS/TS to leverage built-in web APIs, use the NPM ecosystem, etc. So for those times we need it, it's best to understand the blind spots.</p><p>In this post, I'll catalog the areas where TypeScript allows incorrect types to pass through.</p><h2>The <code>any</code> type</h2><p>The biggest culprit for type blind spots in TypeScript is the <code>any</code> type.</p><p>The <code>any</code> type is similar to Elm's <code>Debug.todo ""</code>, which has type <code>a</code> (a type variable that could refer to anything). But there are some key differences:</p><ul><li><code>Debug.todo</code> statements can't be used in production optimized code (<code>elm make --optimize</code>)</li><li><code>Debug.todo</code> can't be published in Elm packages</li><li>Even if you managed to use <code>Debug.todo</code>, your code stops there!</li></ul><p>This is a huge distinction. With Elm, even if you manage to "fool" the type system with a <code>Debug.todo</code> (or a hack like infinitely recursing to get any type), your code will not proceed with faulty assumptions. It's impossible to write Elm code that will continue on with incorrect types.</p><p>As an Elm developer, using TypeScript can feel like shaky ground because of this escape hatch. It's good to understand common sources of <code>any</code> types to know where to look out for this kind of blind spot.</p><h3>Implicit <code>any</code></h3><p>By default, TypeScript will use an <code>any</code> to describe types that it can't infer, like function parameters without annotations.</p><pre><code>function inspect(argument) {
  argument("Careful! `argument` is implicitly `any`");
}
</code></pre><p>Using the <code>noImplicitAny</code> option in your <code>tsconfig.json</code> prevents this problem by giving an error in these situations and requires you to add an annotation. I recommend using the <code>strict</code> option in your <code>tsconfig.json</code>, which includes the <code>noImplicitAny</code> option.</p><h3>Explicit <code>any</code></h3><p>Even with the <code>noImplicitAny</code> option, TypeScript will allow you to use <em>explicit</em> <code>any</code> types. One useful technique here is to use the <code>unknown</code> type as a substitute for <code>any</code>. You can think of <code>unknown</code> as a type-safe alternative to <code>any</code>. <code>unknown</code> doesn't create a blind spot in the type system that allows anything to flow through. Rather, it is much like a <code>Json.Decode.Value</code> in Elm. You can't use it until you check its types.</p><pre><code>function prettyPrint(value: unknown): string | null {
  if (value === null) {
    return "&lt;null&gt;";
  } else if (value === undefined) {
    return "&lt;undefined&gt;";
  } else if (typeof value === "string") {
    return value;
  } else if (value &amp;&amp; typeof value === "object") {
    return "Object with keys: " + Object.keys(value).join(", ");
  } else {
    return null;
  }
}
</code></pre><p>You can pass any value in to <code>prettyPrint</code>, just like you would be able to if the argument was annotated as <code>any</code>. But you must check the value before using it because it is <code>unknown</code>.</p><p>You can use <a href="https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-explicit-any.md"><code>@typescript-eslint/no-explicit-any</code></a> to prevent explicit <code>any</code> types in your own code. You can even configure it to auto-fix <code>any</code> types in your code and change them to <code>unknown</code>.</p><h3><code>any</code> in core JavaScript APIs</h3><p>Some of the core JavaScript APIs have built-in <code>any</code> types. <code>Json.parse</code> is a common source of <code>any</code>'s leaking into your code:</p><pre><code>JSON.parse(text: string): any
</code></pre><pre><code>const definitelyNotAFunction = JSON.parse(
  `"Parsing JSON can never return a function, right?"`
);
definitelyNotAFunction();
// TypeError: definitelyNotAFunction is not a function
</code></pre><p>Yikes! Not only can you assume the parsed JSON is any valid JSON value, but you can even treat it as something besides JSON! Even worse, this can escape into the depths of your code and result in type errors that are far away from their point of origin. In a nutshell, <code>any</code> makes it hard to trust <em>any</em> of your code.</p><p>You can improve the safety of JSON parsing with tools that are similar to <code>elm/json</code> decoding.</p><ul><li><a href="https://github.com/gcanti/io-ts/blob/master/index.md"><code>io-ts</code></a></li><li><a href="https://ajv.js.org/"><code>ajv</code></a></li><li><a href="https://github.com/pelotom/runtypes"><code>runtypes</code></a></li></ul><p>There are many different approaches to safer JSON parsing in TypeScript, and it's worth looking at different options and seeing if one is a good fit for your use case and has an API that suites your tastes.</p><p>Be on the lookout for <code>any</code> types in core JS APIs. Any time you're dealing with dynamic runtime data you're likely to see this. The <code>eval</code> function is a classic case of a function that truly does have <code>any</code> type. That's a reminder of why <code>any</code> is baked into TypeScript: JavaScript is inherently dynamic and dynamically typed.</p><h2><code>any</code> types coming from NPM packages or type definitions</h2><p>Also be aware that TypeScript's declaration functionality allows you to declare the types of existing JavaScript code. These type declarations (usually bundled with an NPM package, or installed with <code>npm install --save-dev @types/&lt;package-name&gt;</code>) often use <code>any</code> types. And even without using <code>any</code>, they could be incorrect because they are declaring types, not <em>proving</em> them soundly through typed code, as we are forced to do in Elm code and Elm packages.</p><h2>Imports</h2><p>Another place to be extra careful is the wiring of imports. When you do an <code>import</code> in Elm, you know that it has resolved if your code compiles. With TypeScript, you can describe how to resolve modules using the <a href="https://www.typescriptlang.org/tsconfig#paths"><code>paths</code> configuration</a> in your <code>tsconfig.json</code>. This can be convenient or necessary at times, but it's another instance where the TypeScript compiler trusts you to do things correctly and guarantee correctness.</p><p>The same is true for defining the <a href="https://www.typescriptlang.org/tsconfig#lib"><code>lib</code> option</a> to describe the types/runtime that you have available (for example, <code>dom</code> APIs, <code>es2020</code>, <code>node</code>). TypeScript takes your word for setting this up correctly, but this could lead to incorrect type checking if you have <code>lib</code>s declared that won't be there in your runtime.</p><p>The type systems in Elm and TypeScript are operating in fundamentally different ways, built to serve their intended use cases.</p><p>Elm is a sandbox where nothing can enter with assumptions about its types. You have to prove everything to the Elm type system.</p><p>TypeScript is built in a way that's meant to be good at gradual typing, allowing you to add type information to existing JavaScript code in an incremental way.</p><h2>Runtime exceptions</h2><p>Any TypeScript code can throw an exception without the types indicating they might fail.</p><p>There's no way to tell by the types, which feels very different than Elm's errors as data. In particular, be on the lookout for functions around file IO and network requests.</p><h2>Mutation by reference</h2><p>If two references point to the same object reference, you can mutate the references according to their respective types, but they both modify the shared reference. This can lead to incorrect runtime types.</p><pre><code>let numbers = [1, 2, 3];
let mixedValues: (number | string)[] = numbers;
mixedValues.push("Uh oh, this is not a number");

numbers.map((number) =&gt; number * 2);
// [2, 4, 6, NaN]
</code></pre><h2>Index access</h2><p>TypeScript assumes that the index you access is there.</p><pre><code>const thisWillBeNull = [1, 2, 3][100];
thisWillBeNull * 2; // NaN
</code></pre><p>The type of <code>thisWillBeNull</code> is <code>number</code> (it is not nullable).</p><h2>Inexhaustive switch statements</h2><p>By default, TypeScript doesn't require you to exhaustively handle all cases in <code>switch</code> statements.</p><p>This can be improved with <a href="https://github.com/typescript-eslint/typescript-eslint/blob/9e0f6ddef7cd29f355f398c90f1986e51c4854f7/packages/eslint-plugin/docs/rules/switch-exhaustiveness-check.md"><code>typescript-eslint</code> <code>switch-exhaustiveness-check</code></a>.</p><h2>Non-null assertions</h2><p>TypeScript's non-null assertion syntax (<code>!</code>) is another place where TypeScript trusts you rather than proving correctness. You can disallow this in your own code with <a href="https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-non-null-assertion.md"><code>@typescript-eslint/no-non-null-assertion</code></a></p><h2>Casts</h2><p>Casts, also known as TypeAssertions, are another place where the TypeScript compiler will trust you and not check types. See <a href="https://basarat.gitbook.io/typescript/type-system/type-assertion#assertion-considered-harmful">Assertion Considered Harmul</a></p><h2>Type Coercion</h2><p>There are several places where JavaScript will coerce types. This is often used as a feature, but it is bug-prone and warrants extra care.</p><ul><li>Falsy values used in conditionals (see <a href="https://basarat.gitbook.io/typescript/recap/truthy">this handy table of truthy and falsy values</a>)</li><li>String concatenation</li></ul><p>Gary Bernhardt's famous <a href="https://www.destroyallsoftware.com/talks/wat">Wat video</a> shows some amusing examples of unexpected type coercion. The type system doesn't stop most of these, so they're worth extra attention.</p><h2>Can you trust an unsound type system?</h2><p>Any chipping away at confidence means that you have to have a skeptical eye at every turn.</p><p>Let's think of it in reverse, though. Unit tests don't give us 100% certainty that our code is correct. Even Elm types don't give us 100% certainty that our types our correct. There are plenty of cases where we don't express every single constraint in the type system, and there's a point where it becomes so verbose to constrain your types that its more pragmatic to stop short of perfect types. Jeroen and I discuss this in our <a href="https://elm-radio.com/episode/impossible-states">Make Impossible States Impossible Elm Radio episode</a>.</p><p>Yes, TypeScript's type system is far less robust than Elm's. But it's far <em>more</em> robust than the type-safety you get in vanilla JavaScript! And I'll take all the added safety I can get! So any time I find myself reaching for JavaScript, I try to take advantage of TypeScript's added safety.</p><p>As much as I like to strive for soundness, correctness, and certainty, I will take any tool that can boost my confidence. None of these tools guarantee correctness at every level. But if you know what guarantees they offer, you can understand what suite of tools to use to give you as much confidence as possible.</p>]]></content:encoded>
</item>
<item>
<title>TypeScript Without Transpilation</title>
<description>Using TypeScript for your JavaScript has a lot of benefits. For Elm devs, the transpilation step is a burden. But you can get all the benefits and skip the transpilation.</description>
<link>https://incrementalelm.com/typescript-without-transpilation</link>
<guid>https://incrementalelm.com/typescript-without-transpilation</guid>
<pubDate>Mon, 01 Feb 2021 00:00:00 GMT</pubDate>
<content>&lt;h1&gt;TypeScript Without Transpilation&lt;/h1&gt;&lt;p&gt;The first rule of Elm is that you want to write &lt;em&gt;everything&lt;/em&gt; in Elm. But sometimes we need to reach out to JavaScript, whether we&apos;re using ports, Custom Elements, or serverless functions. Sometimes JavaScript is just the right tool for the job.&lt;/p&gt;&lt;p&gt;If you&apos;re going to use JavaScript, then you may as well get the improved safety and tooling that TypeScript provides. Except that it adds an extra transpilation step. Working in Elm, that often feels like an unnecessary burden. As you may have guessed already, there is a way to get the best of both worlds!&lt;/p&gt;&lt;h2&gt;Running TypeScript on your .js files&lt;/h2&gt;&lt;p&gt;I&apos;ve been using this approach in all my projects lately and really enjoying the simplicity. Here&apos;s how it works:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Add a &lt;code&gt;tsconfig.json&lt;/code&gt; to your project as usual&lt;/li&gt;&lt;li&gt;Enable the &lt;a href=&quot;https://www.typescriptlang.org/tsconfig#checkJs&quot;&gt;&lt;code&gt;checkJs&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://www.typescriptlang.org/tsconfig#allowJs&quot;&gt;&lt;code&gt;allowJs&lt;/code&gt;&lt;/a&gt; options in your &lt;code&gt;tsconfig.json&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;You&apos;ll now get type checking in your editor for the &lt;code&gt;.js&lt;/code&gt; files you&apos;re working on! Alternatively, you can add a &lt;code&gt;// @ts-check&lt;/code&gt; comment to the top of your &lt;code&gt;.js&lt;/code&gt; files, but I prefer setting it for the whole project in my &lt;code&gt;tsconfig.json&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;You can also set the &lt;a href=&quot;https://www.typescriptlang.org/tsconfig#noEmit&quot;&gt;&lt;code&gt;noEmit&lt;/code&gt;&lt;/a&gt; option in your &lt;code&gt;tsconfig.json&lt;/code&gt; to make sure there is no transpilation output.&lt;/p&gt;&lt;p&gt;Be sure to run &lt;code&gt;tsc&lt;/code&gt; in your builds to make sure that new code doesn&apos;t make it to production if there is a TypeScript compiler error. This is one of the reasons that transpiling may feel safer, because you won&apos;t even get JavaScript output when there are errors (unless you overide &lt;a href=&quot;https://www.typescriptlang.org/tsconfig#noEmitOnError&quot;&gt;&lt;code&gt;noEmitOnError&lt;/code&gt;&lt;/a&gt; in your tsconfig 😳). I feel comfortable with that tradeoff, but it&apos;s very important to make sure your builds fail if there is a compiler error!&lt;/p&gt;&lt;p&gt;Sidenote: I recommend setting the &lt;a href=&quot;https://www.typescriptlang.org/tsconfig#strict&quot;&gt;&lt;code&gt;strict&lt;/code&gt;&lt;/a&gt; option in your tsconfig to &lt;code&gt;true&lt;/code&gt; to avoid pitfalls like implicit any. There are still pitfalls where you may have type errors even when TypeScript says your program compiles, but at least it gets you a lot closer to a sound type system. In a future post, I&apos;ll discuss when you can trust TypeScript and where it has blindspots.&lt;/p&gt;&lt;p&gt;Here&apos;s a sample &lt;code&gt;tsconfig.json&lt;/code&gt; with this setup:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;{
  &quot;compilerOptions&quot;: {
    &quot;allowJs&quot;: true,
    &quot;checkJs&quot;: true,
    &quot;noEmit&quot;: true,
    // make the compiler as strict as possible
    &quot;strict&quot;: true,
    &quot;noImplicitReturns&quot;: true,
    &quot;noFallthroughCasesInSwitch&quot;: true,
    &quot;noUncheckedIndexedAccess&quot;: true,
    // get intellisense for the available platform APIs
    &quot;lib&quot;: [&quot;dom&quot;, &quot;es2020&quot;],
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Adding TypeScript types to your .js files with JSDoc comments&lt;/h2&gt;&lt;p&gt;Once you&apos;ve got the TypeScript compiler wired in for your project, you&apos;ll need to add type annotations and define TypeScript types so that you can work with TypeScript and use strict, explicit type checks.&lt;/p&gt;&lt;p&gt;You can include type information in &lt;code&gt;JSDoc&lt;/code&gt; comments.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;/**
 * @param {Language} language
 * @param {string} name
 * @type {import(&quot;./user&quot;)} user
 * @returns {string}
 */
function greet(language, name, user) {
  /** @type {string} */
  let greeting;
  greeting = `${helloInLanguage(language)} ${name}`;
  const username = user.username;
  if (username) {
    greeting += ` (@${user.username})!`;
  }
  return greeting;
}

/** @typedef {&quot;english&quot; | &quot;spanish&quot;} Language */
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let&apos;s break down what&apos;s happening here.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;/** ... */&lt;/code&gt; defines a JSDoc comment&lt;/li&gt;&lt;li&gt;&lt;code&gt;@param&lt;/code&gt; defines a parameter (&lt;code&gt;name&lt;/code&gt; is a parameter name for the &lt;code&gt;greet&lt;/code&gt; function). You write one &lt;code&gt;@param&lt;/code&gt; line for each parameter.&lt;/li&gt;&lt;li&gt;The values between the &lt;code&gt;{}&lt;/code&gt;&apos;s (like &lt;code&gt;{string}&lt;/code&gt;) can be any valid TypeScript type, including unions and other advanced types, or types defined in other files&lt;/li&gt;&lt;li&gt;We can&apos;t &lt;code&gt;import&lt;/code&gt; TypeScript types directly in code as we can in &lt;code&gt;.ts&lt;/code&gt; files, but we can &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#import-types&quot;&gt;use &lt;code&gt;import&lt;/code&gt; within the &lt;code&gt;{}&lt;/code&gt;s in JSDoc comments to refer to types in other files or NPM packages&lt;/a&gt;&lt;/li&gt;&lt;li&gt;The &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#type&quot;&gt;&lt;code&gt;@type&lt;/code&gt;&lt;/a&gt; JSDoc comment lets us declare the type of the &lt;code&gt;greeting&lt;/code&gt; variable&lt;/li&gt;&lt;li&gt;We&apos;re using &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#typedef-callback-and-param&quot;&gt;&lt;code&gt;@typedef&lt;/code&gt;&lt;/a&gt; to define a union type called &lt;code&gt;Language&lt;/code&gt;, then specifying that the &lt;code&gt;language&lt;/code&gt; parameter has that type.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;In VS Code, you can start typing &lt;code&gt;/**&lt;/code&gt; and it will code complete for all the parameter names in the function&apos;s parameter list. Also, since this is JSDoc, you can include documentation here and it will show up in your editor tooltips.&lt;/p&gt;&lt;p&gt;You can find a full list of supported &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html&quot;&gt;JSDoc TypeScript directives&lt;/a&gt;, and take a look at the &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/intro-to-js-ts.html&quot;&gt;official TypeScript guide on using TypeScript in .js&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;What are the downsides?&lt;/h2&gt;&lt;p&gt;Most TypeScript functionality is available using JSDoc comments. And, importantly, you can enforce TypeScript rules with the exact same strictness that a &lt;code&gt;.ts&lt;/code&gt; file would allow.&lt;/p&gt;&lt;p&gt;The main limitation is that you don&apos;t have access to some TypeScript-specific syntax.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;as&lt;/code&gt;, also known as &lt;a href=&quot;https://basarat.gitbook.io/typescript/type-system/type-assertion&quot;&gt;type assertions&lt;/a&gt; (or casts)&lt;/li&gt;&lt;li&gt;&lt;code&gt;is&lt;/code&gt;, also known as &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/advanced-types.html#using-type-predicates&quot;&gt;type predicates&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;!&lt;/code&gt;, non-null assertions&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;However, non-null assertions and &lt;code&gt;as&lt;/code&gt; (casts) are two unsafe operators that, as the &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/basic-types.html#type-assertions&quot;&gt;TypeScript docs say&lt;/a&gt;, let you tell the compiler &quot;trust me, I know what I&apos;m doing.&quot; As Elm developers, we want to avoid these as much as possible. So while there are some syntax features that aren&apos;t available in JSDoc-typed &lt;code&gt;.js&lt;/code&gt; files, you can get most of what you need to take advantage of the added safety of TypeScript. And you can use the &lt;code&gt;@type&lt;/code&gt; directive to perform type casts using JSDoc syntax as well.&lt;/p&gt;&lt;h2&gt;TypeScript in Elm&lt;/h2&gt;&lt;p&gt;I&apos;m writing this series of posts about using TypeScript with Elm in preparation for the upcoming launch of my redesigned &lt;code&gt;elm-ts-interop&lt;/code&gt; tool. If you missed it, I wrote a post introducing some of the concepts in my post &lt;a href=&quot;https://functional.christmas/2020/11&quot;&gt;Types Without Borders Isn&apos;t Enough&lt;/a&gt;. I&apos;ll be launching &lt;code&gt;elm-ts-interop&lt;/code&gt; on March 1st, with a free set of core features, and some paid pro features to help with code generation.&lt;/p&gt;&lt;p&gt;Got any TypeScript for Elm users questions? Send them my way and I&apos;ll do my best to answer them!&lt;/p&gt;</content>
<content:encoded><![CDATA[<h1>TypeScript Without Transpilation</h1><p>The first rule of Elm is that you want to write <em>everything</em> in Elm. But sometimes we need to reach out to JavaScript, whether we're using ports, Custom Elements, or serverless functions. Sometimes JavaScript is just the right tool for the job.</p><p>If you're going to use JavaScript, then you may as well get the improved safety and tooling that TypeScript provides. Except that it adds an extra transpilation step. Working in Elm, that often feels like an unnecessary burden. As you may have guessed already, there is a way to get the best of both worlds!</p><h2>Running TypeScript on your .js files</h2><p>I've been using this approach in all my projects lately and really enjoying the simplicity. Here's how it works:</p><ul><li>Add a <code>tsconfig.json</code> to your project as usual</li><li>Enable the <a href="https://www.typescriptlang.org/tsconfig#checkJs"><code>checkJs</code></a> and <a href="https://www.typescriptlang.org/tsconfig#allowJs"><code>allowJs</code></a> options in your <code>tsconfig.json</code></li></ul><p>You'll now get type checking in your editor for the <code>.js</code> files you're working on! Alternatively, you can add a <code>// @ts-check</code> comment to the top of your <code>.js</code> files, but I prefer setting it for the whole project in my <code>tsconfig.json</code>.</p><p>You can also set the <a href="https://www.typescriptlang.org/tsconfig#noEmit"><code>noEmit</code></a> option in your <code>tsconfig.json</code> to make sure there is no transpilation output.</p><p>Be sure to run <code>tsc</code> in your builds to make sure that new code doesn't make it to production if there is a TypeScript compiler error. This is one of the reasons that transpiling may feel safer, because you won't even get JavaScript output when there are errors (unless you overide <a href="https://www.typescriptlang.org/tsconfig#noEmitOnError"><code>noEmitOnError</code></a> in your tsconfig 😳). I feel comfortable with that tradeoff, but it's very important to make sure your builds fail if there is a compiler error!</p><p>Sidenote: I recommend setting the <a href="https://www.typescriptlang.org/tsconfig#strict"><code>strict</code></a> option in your tsconfig to <code>true</code> to avoid pitfalls like implicit any. There are still pitfalls where you may have type errors even when TypeScript says your program compiles, but at least it gets you a lot closer to a sound type system. In a future post, I'll discuss when you can trust TypeScript and where it has blindspots.</p><p>Here's a sample <code>tsconfig.json</code> with this setup:</p><pre><code>{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": true,
    "noEmit": true,
    // make the compiler as strict as possible
    "strict": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedIndexedAccess": true,
    // get intellisense for the available platform APIs
    "lib": ["dom", "es2020"],
  }
}
</code></pre><h2>Adding TypeScript types to your .js files with JSDoc comments</h2><p>Once you've got the TypeScript compiler wired in for your project, you'll need to add type annotations and define TypeScript types so that you can work with TypeScript and use strict, explicit type checks.</p><p>You can include type information in <code>JSDoc</code> comments.</p><pre><code>/**
 * @param {Language} language
 * @param {string} name
 * @type {import("./user")} user
 * @returns {string}
 */
function greet(language, name, user) {
  /** @type {string} */
  let greeting;
  greeting = `${helloInLanguage(language)} ${name}`;
  const username = user.username;
  if (username) {
    greeting += ` (@${user.username})!`;
  }
  return greeting;
}

/** @typedef {"english" | "spanish"} Language */
</code></pre><p>Let's break down what's happening here.</p><ul><li><code>/** ... */</code> defines a JSDoc comment</li><li><code>@param</code> defines a parameter (<code>name</code> is a parameter name for the <code>greet</code> function). You write one <code>@param</code> line for each parameter.</li><li>The values between the <code>{}</code>'s (like <code>{string}</code>) can be any valid TypeScript type, including unions and other advanced types, or types defined in other files</li><li>We can't <code>import</code> TypeScript types directly in code as we can in <code>.ts</code> files, but we can <a href="https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#import-types">use <code>import</code> within the <code>{}</code>s in JSDoc comments to refer to types in other files or NPM packages</a></li><li>The <a href="https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#type"><code>@type</code></a> JSDoc comment lets us declare the type of the <code>greeting</code> variable</li><li>We're using <a href="https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#typedef-callback-and-param"><code>@typedef</code></a> to define a union type called <code>Language</code>, then specifying that the <code>language</code> parameter has that type.</li></ul><p>In VS Code, you can start typing <code>/**</code> and it will code complete for all the parameter names in the function's parameter list. Also, since this is JSDoc, you can include documentation here and it will show up in your editor tooltips.</p><p>You can find a full list of supported <a href="https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html">JSDoc TypeScript directives</a>, and take a look at the <a href="https://www.typescriptlang.org/docs/handbook/intro-to-js-ts.html">official TypeScript guide on using TypeScript in .js</a>.</p><h2>What are the downsides?</h2><p>Most TypeScript functionality is available using JSDoc comments. And, importantly, you can enforce TypeScript rules with the exact same strictness that a <code>.ts</code> file would allow.</p><p>The main limitation is that you don't have access to some TypeScript-specific syntax.</p><ul><li><code>as</code>, also known as <a href="https://basarat.gitbook.io/typescript/type-system/type-assertion">type assertions</a> (or casts)</li><li><code>is</code>, also known as <a href="https://www.typescriptlang.org/docs/handbook/advanced-types.html#using-type-predicates">type predicates</a></li><li><code>!</code>, non-null assertions</li></ul><p>However, non-null assertions and <code>as</code> (casts) are two unsafe operators that, as the <a href="https://www.typescriptlang.org/docs/handbook/basic-types.html#type-assertions">TypeScript docs say</a>, let you tell the compiler "trust me, I know what I'm doing." As Elm developers, we want to avoid these as much as possible. So while there are some syntax features that aren't available in JSDoc-typed <code>.js</code> files, you can get most of what you need to take advantage of the added safety of TypeScript. And you can use the <code>@type</code> directive to perform type casts using JSDoc syntax as well.</p><h2>TypeScript in Elm</h2><p>I'm writing this series of posts about using TypeScript with Elm in preparation for the upcoming launch of my redesigned <code>elm-ts-interop</code> tool. If you missed it, I wrote a post introducing some of the concepts in my post <a href="https://functional.christmas/2020/11">Types Without Borders Isn't Enough</a>. I'll be launching <code>elm-ts-interop</code> on March 1st, with a free set of core features, and some paid pro features to help with code generation.</p><p>Got any TypeScript for Elm users questions? Send them my way and I'll do my best to answer them!</p>]]></content:encoded>
</item>
<item>
<title>When It Compiles, But Doesn&apos;t Work</title>
<description>In Elm code, &quot;if it compiles, it works.&quot; Does that come for free? Let&apos;s see if we can find Elm code where &quot;it compiles, but it doesn&apos;t work&quot; to see what we can learn about writing maintainable Elm code.</description>
<link>https://incrementalelm.com/when-it-compiles-but-doesnt-work</link>
<guid>https://incrementalelm.com/when-it-compiles-but-doesnt-work</guid>
<pubDate>Mon, 29 Nov 2021 00:00:00 GMT</pubDate>
<content>&lt;h1&gt;When It Compiles, But Doesn&apos;t Work&lt;/h1&gt;&lt;p&gt;In Elm code, &quot;if it compiles, it works.&quot; Does that come for free? Let&apos;s see if we can find Elm code where &quot;it compiles, but it doesn&apos;t work&quot; to see what we can learn about writing maintainable Elm code.&lt;/p&gt;&lt;h2&gt;Violins and Vuvuzelas&lt;/h2&gt;&lt;p&gt;Do violins make beautiful sounds? As a non-violinist who has tried a violin, I&apos;m living proof that sometimes the sounds they create are not so beautiful. But there&apos;s plenty of proof that they make beautiful sounds as well.&lt;/p&gt;&lt;p&gt;Does a vuvuzela make beautiful sounds? I don&apos;t think it&apos;s controversial to say that it&apos;s probably not even possible. So violins are something special without a doubt. While a violin can&apos;t make beautiful sounds without the right technique, it is equipped with several tools that give it unique expressive power. By changing the angle of the bow, how close to the bridge you play, the speed of the bow, and by moving our fingers on the fingerboard (vibrato, intonation), there is as much expressive power as almost any musical instrument in the violin.&lt;/p&gt;&lt;h2&gt;The Expressive Power of a Vuvuzela&lt;/h2&gt;&lt;p&gt;Elm doesn&apos;t give you safe code for free. It does help you avoid several footguns through its explicit semantics and strict type system. But just as importantly, it provides tools for you to express the constraints of your system. And when you express those constraints in Elm, you can really trust them. There is no way to bypass the constraints or fool the type system. So having the expressive power of a vuvuzela will limit you. But tools are only as good as our ability to make use of them. Elm&apos;s guarantees are 100% not 99%. But it can&apos;t enforce constraints that are specific to your domain. So remember that it&apos;s up to you to write custom tailored guarantees for your domain. As an Elm developer, you&apos;re like the violinst - you have a beautiful tool in your hands but its up to you to use it to its full potential.&lt;/p&gt;&lt;p&gt;To make the most of these tools for enforcing constraints, lets see what happens when we don&apos;t make use of them. Lets try to pinpoint the key techniques for leveraging those tools to make Elm code feel like &quot;if it compiles, it works.&quot;&lt;/p&gt;&lt;h2&gt;Defaults, Squelched Errors, and Catch-Alls&lt;/h2&gt;&lt;pre&gt;&lt;code&gt;username
    -- this should never happen
    |&amp;gt; Maybe.withDefault &quot;&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I&apos;m guilty of writing code like this. And if you&apos;re like me, you know the feeling of spending too much time debugging a problem only to find a line of code like this and realizing that you should always expect the unexpected.&lt;/p&gt;&lt;p&gt;This can be particularly elusive when these default values trickle their way through the system to end up in states that otherwise seemed impossible. It can also lead to silent runtime failures instead of a descriptive compiler error (inexhaustive case errors, type errors, etc.).&lt;/p&gt;&lt;p&gt;That can mean the difference between Elm being a helpful and alert assistant and Elm becoming a lazy assistant. Elm can only help you enforce the constraints you tell it about.&lt;/p&gt;&lt;p&gt;Also be on the lookout for catch-all clauses like this in your code:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;type Membership
    = Pro
    | Free

case membership of
    Pro -&amp;gt;
      &quot;Pro&quot;

    _ -&amp;gt;
        &quot;Free&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will fall through to the correct value now, but if you add a new &lt;code&gt;Membership&lt;/code&gt; level then you&apos;ll end up with code that &quot;compiles, but doesn&apos;t work.&quot;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;type Membership
    = Pro
+   | Premium
    | Free
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that catch-all&apos;s can be what you want in some cases, so you need to use your judgement to make sure it&apos;s what you want. For example, perhaps an &lt;code&gt;isFree : Membership -&amp;gt; Bool&lt;/code&gt; could safely use a catch-all. Though the cost of handling it explicitly is low, and there&apos;s always a risk that the catch-all could cause you to miss something important (like adding a &lt;code&gt;FreeTrial&lt;/code&gt; level of &lt;code&gt;Membership&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;As an alternative to default values, squelched errors, and catch-alls, you can consider using &lt;code&gt;Debug.todo&lt;/code&gt; statements as a placeholder for unhandled cases during development. Then you are reminded to handle those cases before your code goes live (since &lt;code&gt;elm --optimize&lt;/code&gt; will fail if there are any uses of &lt;code&gt;Debug&lt;/code&gt;).&lt;/p&gt;&lt;h2&gt;Types &lt;em&gt;With&lt;/em&gt; Borders&lt;/h2&gt;&lt;p&gt;If you&apos;ve ever iterated on writing an &lt;code&gt;elm/json&lt;/code&gt; &lt;code&gt;Decoder&lt;/code&gt; then you know that you can easily write JSON Decoders (or encode JSON values) where it &quot;compiles, but doesn&apos;t work.&quot; That&apos;s because within the local scope of your Elm application everything is consistent, and you&apos;ve handled the possible errors in your Decoding or HTTP requests.&lt;/p&gt;&lt;p&gt;Using [&lt;a title=&quot;Types Without Borders&quot; href=&quot;types-without-borders&quot;&gt;types-without-borders&lt;/a&gt;] can help avoid this case of &quot;compiles, but doesn&apos;t work,&quot; by keeping your types in sync across the boundaries of your frontend. Some helpful tools for that include:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Lamdera&lt;/li&gt;&lt;li&gt;&lt;code&gt;elm-graphql&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Custom code generation&lt;/li&gt;&lt;li&gt;&lt;code&gt;elm-ts-interop&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/eriktim/elm-protocol-buffers&quot;&gt;&lt;code&gt;elm-protocol-buffers&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Making Impossible States &lt;em&gt;Possible&lt;/em&gt;&lt;/h2&gt;&lt;p&gt;This topic comes up a lot in the Elm world, so by now we have plenty of great resources that show how these Impossible States can cause our code to &quot;compile, but not work.&quot;&lt;/p&gt;&lt;p&gt;So be sure to [&lt;a title=&quot;Make Impossible States Impossible&quot; href=&quot;make-impossible-states-impossible&quot;&gt;make-impossible-states-impossible&lt;/a&gt;]. Reduce states to valid states as much as possible. Keep in mind that it&apos;s not just about valid combinations of state.
Make the smallest amount of state possible to express (and no less). A useful technique is to count &lt;a href=&quot;https://guide.elm-lang.org/appendix/types_as_sets.html&quot;&gt;the cardinatlity of your types&lt;/a&gt;. You can construct a table of values you can express with your types to help identify invalid ones.&lt;/p&gt;&lt;h2&gt;Primitive Obsession&lt;/h2&gt;&lt;p&gt;The code smell often referred to as &lt;a href=&quot;https://wiki.c2.com/?PrimitiveObsession&quot;&gt;Primitive Obsession&lt;/a&gt; is when values like String or Int are over-used. Under the hood, you&apos;ll need Strings and Ints, but you can give better semantics and enforce more constraints if you create new Custom Types to wrap those primitives. For example, instead of a &lt;code&gt;String&lt;/code&gt; we could use &lt;code&gt;type Username = Username String&lt;/code&gt; and instead of an &lt;code&gt;Int&lt;/code&gt; we could use &lt;code&gt;type UserId = UserId Int&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Here&apos;s some code that &quot;compiles, but doesn&apos;t work&quot; because it allows an &lt;code&gt;Int&lt;/code&gt; instead of a more constrained Custom Type:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;getUserProfileInfo :
    (Result Http.Error User -&amp;gt; msg)
    -&amp;gt; Int
    -&amp;gt; Cmd msg

update msg model =
    -- ...
    ( model,
      getUserProfileInfo
        GotUserProfile
        product.id
    )
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If we use a Custom Type, we could turn this into &quot;it doesn&apos;t compile, so it doesn&apos;t work&quot;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;getUserProfileInfo :
    (Result Http.Error User -&amp;gt; msg)
    -&amp;gt; UserId
    -&amp;gt; Cmd msg

update msg model =
    -- ...
    ( model,
      getUserProfileInfo
        GotUserProfile
        product.id -- compiler error
        -- need to use model.currentUser.id to fix it
    )
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Don&apos;t forget to [&lt;a title=&quot;Wrap Early, Unwrap Late&quot; href=&quot;wrap-early-unwrap-late&quot;&gt;wrap-early-unwrap-late&lt;/a&gt;] - the goal is to have the best representation of our data for the entire life of the value.&lt;/p&gt;&lt;h2&gt;Opaque Types&lt;/h2&gt;&lt;p&gt;A &lt;code&gt;UserId&lt;/code&gt; like the example above only helps us if we use an Opaque Type to help us enforce the constraint. If we can use &lt;code&gt;UserId product.id&lt;/code&gt; then there&apos;s not much improvement.&lt;/p&gt;&lt;p&gt;An Opaque Type helps you reduce the surface area where you need to think about a constraint ([&lt;a title=&quot;Opaque Types Let You Think Locally&quot; href=&quot;opaque-types-let-you-think-locally&quot;&gt;opaque-types-let-you-think-locally&lt;/a&gt;]). That means you can trust that a &lt;code&gt;UserId&lt;/code&gt; is what it says it is.&lt;/p&gt;&lt;p&gt;Use Opaque Types to help enforce constraints about:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Where the data came from&lt;/li&gt;&lt;li&gt;How the data can be used&lt;/li&gt;&lt;li&gt;How the data has been validated (especially helpful with the concept of [&lt;a title=&quot;Parse, Don&apos;t Validate&quot; href=&quot;parse-dont-validate&quot;&gt;parse-dont-validate&lt;/a&gt;])&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;For example, if a username must be non-empty, representing usernames as &lt;code&gt;String&lt;/code&gt;s or as a Custom Type that can be freely created outside of a Username module (non-opaque types) means you need to be careful about that constraint everywhere in your codebase.&lt;/p&gt;&lt;p&gt;By hiding the constructor from outside of the &lt;code&gt;Username&lt;/code&gt; module, you can enforce that guarantee in one place and have confidence in that guarantee everywhere outside of that module.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;module Username exposing (Username, fromString)

type Username = Username String

fromString : String -&amp;gt; Maybe Username
fromString rawUsername =
    if isValid rawUsername then
        rawUsername |&amp;gt; Username |&amp;gt; Just
    else
        Nothing

isValid : String -&amp;gt; Bool
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you need to be careful about upholding constraints on a huge surface area in your codebase, then you won&apos;t be able to make changes with confidence. If you have a small surface area with clear responsibilities enforced within opaque types, then you can be confident the constraints will still be enforced when you change code outside the Opaque Type&apos;s module.&lt;/p&gt;&lt;h2&gt;Getting to &quot;If It Compiles, It Works&quot;&lt;/h2&gt;&lt;p&gt;We&apos;re still human, and we can still introduce bugs. And these techniques work best in tandem with automated tests, not instead of tests. But if you keep these techniques in mind, you&apos;ll get the feeling of making bulletproof changes to code that comes from using the tools Elm gives us to their full potential.&lt;/p&gt;</content>
<content:encoded><![CDATA[<h1>When It Compiles, But Doesn't Work</h1><p>In Elm code, "if it compiles, it works." Does that come for free? Let's see if we can find Elm code where "it compiles, but it doesn't work" to see what we can learn about writing maintainable Elm code.</p><h2>Violins and Vuvuzelas</h2><p>Do violins make beautiful sounds? As a non-violinist who has tried a violin, I'm living proof that sometimes the sounds they create are not so beautiful. But there's plenty of proof that they make beautiful sounds as well.</p><p>Does a vuvuzela make beautiful sounds? I don't think it's controversial to say that it's probably not even possible. So violins are something special without a doubt. While a violin can't make beautiful sounds without the right technique, it is equipped with several tools that give it unique expressive power. By changing the angle of the bow, how close to the bridge you play, the speed of the bow, and by moving our fingers on the fingerboard (vibrato, intonation), there is as much expressive power as almost any musical instrument in the violin.</p><h2>The Expressive Power of a Vuvuzela</h2><p>Elm doesn't give you safe code for free. It does help you avoid several footguns through its explicit semantics and strict type system. But just as importantly, it provides tools for you to express the constraints of your system. And when you express those constraints in Elm, you can really trust them. There is no way to bypass the constraints or fool the type system. So having the expressive power of a vuvuzela will limit you. But tools are only as good as our ability to make use of them. Elm's guarantees are 100% not 99%. But it can't enforce constraints that are specific to your domain. So remember that it's up to you to write custom tailored guarantees for your domain. As an Elm developer, you're like the violinst - you have a beautiful tool in your hands but its up to you to use it to its full potential.</p><p>To make the most of these tools for enforcing constraints, lets see what happens when we don't make use of them. Lets try to pinpoint the key techniques for leveraging those tools to make Elm code feel like "if it compiles, it works."</p><h2>Defaults, Squelched Errors, and Catch-Alls</h2><pre><code>username
    -- this should never happen
    |&gt; Maybe.withDefault ""
</code></pre><p>I'm guilty of writing code like this. And if you're like me, you know the feeling of spending too much time debugging a problem only to find a line of code like this and realizing that you should always expect the unexpected.</p><p>This can be particularly elusive when these default values trickle their way through the system to end up in states that otherwise seemed impossible. It can also lead to silent runtime failures instead of a descriptive compiler error (inexhaustive case errors, type errors, etc.).</p><p>That can mean the difference between Elm being a helpful and alert assistant and Elm becoming a lazy assistant. Elm can only help you enforce the constraints you tell it about.</p><p>Also be on the lookout for catch-all clauses like this in your code:</p><pre><code>type Membership
    = Pro
    | Free

case membership of
    Pro -&gt;
      "Pro"

    _ -&gt;
        "Free"
</code></pre><p>This will fall through to the correct value now, but if you add a new <code>Membership</code> level then you'll end up with code that "compiles, but doesn't work."</p><pre><code>type Membership
    = Pro
+   | Premium
    | Free
</code></pre><p>Note that catch-all's can be what you want in some cases, so you need to use your judgement to make sure it's what you want. For example, perhaps an <code>isFree : Membership -&gt; Bool</code> could safely use a catch-all. Though the cost of handling it explicitly is low, and there's always a risk that the catch-all could cause you to miss something important (like adding a <code>FreeTrial</code> level of <code>Membership</code>).</p><p>As an alternative to default values, squelched errors, and catch-alls, you can consider using <code>Debug.todo</code> statements as a placeholder for unhandled cases during development. Then you are reminded to handle those cases before your code goes live (since <code>elm --optimize</code> will fail if there are any uses of <code>Debug</code>).</p><h2>Types <em>With</em> Borders</h2><p>If you've ever iterated on writing an <code>elm/json</code> <code>Decoder</code> then you know that you can easily write JSON Decoders (or encode JSON values) where it "compiles, but doesn't work." That's because within the local scope of your Elm application everything is consistent, and you've handled the possible errors in your Decoding or HTTP requests.</p><p>Using [<a title="Types Without Borders" href="types-without-borders">types-without-borders</a>] can help avoid this case of "compiles, but doesn't work," by keeping your types in sync across the boundaries of your frontend. Some helpful tools for that include:</p><ul><li>Lamdera</li><li><code>elm-graphql</code></li><li>Custom code generation</li><li><code>elm-ts-interop</code></li><li><a href="https://github.com/eriktim/elm-protocol-buffers"><code>elm-protocol-buffers</code></a></li></ul><h2>Making Impossible States <em>Possible</em></h2><p>This topic comes up a lot in the Elm world, so by now we have plenty of great resources that show how these Impossible States can cause our code to "compile, but not work."</p><p>So be sure to [<a title="Make Impossible States Impossible" href="make-impossible-states-impossible">make-impossible-states-impossible</a>]. Reduce states to valid states as much as possible. Keep in mind that it's not just about valid combinations of state.
Make the smallest amount of state possible to express (and no less). A useful technique is to count <a href="https://guide.elm-lang.org/appendix/types_as_sets.html">the cardinatlity of your types</a>. You can construct a table of values you can express with your types to help identify invalid ones.</p><h2>Primitive Obsession</h2><p>The code smell often referred to as <a href="https://wiki.c2.com/?PrimitiveObsession">Primitive Obsession</a> is when values like String or Int are over-used. Under the hood, you'll need Strings and Ints, but you can give better semantics and enforce more constraints if you create new Custom Types to wrap those primitives. For example, instead of a <code>String</code> we could use <code>type Username = Username String</code> and instead of an <code>Int</code> we could use <code>type UserId = UserId Int</code>.</p><p>Here's some code that "compiles, but doesn't work" because it allows an <code>Int</code> instead of a more constrained Custom Type:</p><pre><code>getUserProfileInfo :
    (Result Http.Error User -&gt; msg)
    -&gt; Int
    -&gt; Cmd msg

update msg model =
    -- ...
    ( model,
      getUserProfileInfo
        GotUserProfile
        product.id
    )
</code></pre><p>If we use a Custom Type, we could turn this into "it doesn't compile, so it doesn't work":</p><pre><code>getUserProfileInfo :
    (Result Http.Error User -&gt; msg)
    -&gt; UserId
    -&gt; Cmd msg

update msg model =
    -- ...
    ( model,
      getUserProfileInfo
        GotUserProfile
        product.id -- compiler error
        -- need to use model.currentUser.id to fix it
    )
</code></pre><p>Don't forget to [<a title="Wrap Early, Unwrap Late" href="wrap-early-unwrap-late">wrap-early-unwrap-late</a>] - the goal is to have the best representation of our data for the entire life of the value.</p><h2>Opaque Types</h2><p>A <code>UserId</code> like the example above only helps us if we use an Opaque Type to help us enforce the constraint. If we can use <code>UserId product.id</code> then there's not much improvement.</p><p>An Opaque Type helps you reduce the surface area where you need to think about a constraint ([<a title="Opaque Types Let You Think Locally" href="opaque-types-let-you-think-locally">opaque-types-let-you-think-locally</a>]). That means you can trust that a <code>UserId</code> is what it says it is.</p><p>Use Opaque Types to help enforce constraints about:</p><ul><li>Where the data came from</li><li>How the data can be used</li><li>How the data has been validated (especially helpful with the concept of [<a title="Parse, Don't Validate" href="parse-dont-validate">parse-dont-validate</a>])</li></ul><p>For example, if a username must be non-empty, representing usernames as <code>String</code>s or as a Custom Type that can be freely created outside of a Username module (non-opaque types) means you need to be careful about that constraint everywhere in your codebase.</p><p>By hiding the constructor from outside of the <code>Username</code> module, you can enforce that guarantee in one place and have confidence in that guarantee everywhere outside of that module.</p><pre><code>module Username exposing (Username, fromString)

type Username = Username String

fromString : String -&gt; Maybe Username
fromString rawUsername =
    if isValid rawUsername then
        rawUsername |&gt; Username |&gt; Just
    else
        Nothing

isValid : String -&gt; Bool
</code></pre><p>If you need to be careful about upholding constraints on a huge surface area in your codebase, then you won't be able to make changes with confidence. If you have a small surface area with clear responsibilities enforced within opaque types, then you can be confident the constraints will still be enforced when you change code outside the Opaque Type's module.</p><h2>Getting to "If It Compiles, It Works"</h2><p>We're still human, and we can still introduce bugs. And these techniques work best in tandem with automated tests, not instead of tests. But if you keep these techniques in mind, you'll get the feeling of making bulletproof changes to code that comes from using the tools Elm gives us to their full potential.</p>]]></content:encoded>
</item>
</channel>
</rss>