<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dwayne Crooks</title>
    <description>The latest articles on DEV Community by Dwayne Crooks (@dwayne).</description>
    <link>https://dev.to/dwayne</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F162158%2F9cd19958-382f-4763-ac40-835fe926722e.jpg</url>
      <title>DEV Community: Dwayne Crooks</title>
      <link>https://dev.to/dwayne</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dwayne"/>
    <language>en</language>
    <item>
      <title>Elm + Nix: A reproducible RealWorld clone</title>
      <dc:creator>Dwayne Crooks</dc:creator>
      <pubDate>Fri, 13 Feb 2026 13:20:39 +0000</pubDate>
      <link>https://dev.to/dwayne/elm-nix-a-reproducible-realworld-clone-19lm</link>
      <guid>https://dev.to/dwayne/elm-nix-a-reproducible-realworld-clone-19lm</guid>
      <description>&lt;p&gt;Nix has infected my soul. I don't know how else to describe it.&lt;/p&gt;

&lt;p&gt;I refactored my RealWorld clone, &lt;a href="https://github.com/dwayne/elm-conduit" rel="noopener noreferrer"&gt;&lt;code&gt;dwayne/elm-conduit&lt;/code&gt;&lt;/a&gt;, to use Nix. It uses Nix for the development environment, building, serving, running checks, deploying, and CI.&lt;/p&gt;

&lt;p&gt;As a bonus, if you have Nix running on your machine you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run the &lt;a href="https://bradfrost.com/blog/post/a-frontend-workshop-environment/" rel="noopener noreferrer"&gt;frontend workshop environment&lt;/a&gt;:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;nix run github:dwayne/elm-conduit#workshop&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Run the sandbox:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;nix run github:dwayne/elm-conduit#sandbox&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Run the development version of the web app:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;nix run github:dwayne/elm-conduit&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Run the production version of the web app, i.e. the exact version that's hosted by Netlify at &lt;a href="https://elm-conduit.netlify.app/" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://elm-conduit.netlify.app" rel="noopener noreferrer"&gt;https://elm-conduit.netlify.app&lt;/a&gt;:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;nix run github:dwayne/elm-conduit#prod&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The Elm builds are all powered by &lt;a href="https://github.com/dwayne/elm2nix" rel="noopener noreferrer"&gt;&lt;code&gt;dwayne/elm2nix&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Classics
&lt;/h2&gt;

&lt;p&gt;This actually completes my refactoring of "&lt;a href="https://elmwithdwayne.dev/#the-classics" rel="noopener noreferrer"&gt;The Classics&lt;/a&gt;" to use Nix.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/dwayne/elm-todos" rel="noopener noreferrer"&gt;TodoMVC&lt;/a&gt;, &lt;a href="https://github.com/dwayne/elm-super-rentals" rel="noopener noreferrer"&gt;Super Rentals&lt;/a&gt;, &lt;a href="https://github.com/dwayne/elm-7guis" rel="noopener noreferrer"&gt;7GUIs&lt;/a&gt;, and &lt;a href="https://github.com/dwayne/elm-conduit" rel="noopener noreferrer"&gt;Conduit&lt;/a&gt; are now all using Nix in the same way. Check out anyone of them to help you figure out how to combine Elm and Nix for fun and profit.&lt;/p&gt;

&lt;h3&gt;
  
  
  TodoMVC
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;nix run github:dwayne/elm-todos#prod&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Super Rentals
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;nix run github:dwayne/elm-super-rentals#prod&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  7GUIs
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;nix run github:dwayne/elm-7guis#prod&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Related posts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/dwayne/yet-another-tour-of-an-open-source-elm-spa-1672"&gt;Yet Another Tour of an Open-Source Elm SPA&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/dwayne/announcing-dwayneelm2nix-46pc"&gt;Announcing dwayne/elm2nix&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/dwayne/nix-pnpm-parcel-lydellelm-safe-virtual-dom-2lp6"&gt;Nix + pnpm + Parcel + lydell/elm-safe-virtual-dom&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/dwayne/making-todomvc-work-with-dwayneelm2nix-5dal"&gt;Making TodoMVC work with dwayne/elm2nix&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Contact me for Nix training and support
&lt;/h2&gt;

&lt;p&gt;If you're interested in learning Nix or adding Nix to your workflow don't hesitate to reach out to me via the &lt;a href="https://buttondown.com/elmwithdwayne" rel="noopener noreferrer"&gt;newsletter&lt;/a&gt;. I'd be delighted to help you achieve your goals.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subscribe to my newsletter
&lt;/h2&gt;

&lt;p&gt;If you're interested in improving your skills with Elm then I invite you to subscribe to my newsletter, &lt;a href="https://buttondown.com/elmwithdwayne" rel="noopener noreferrer"&gt;Elm with Dwayne&lt;/a&gt;. To learn more about it, please read this &lt;a href="https://dev.to/dwayne/announcing-my-newsletter-elm-with-dwayne-hoi"&gt;announcement&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>elm</category>
      <category>nix</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Nix + pnpm + Parcel + lydell/elm-safe-virtual-dom</title>
      <dc:creator>Dwayne Crooks</dc:creator>
      <pubDate>Thu, 29 Jan 2026 12:53:30 +0000</pubDate>
      <link>https://dev.to/dwayne/nix-pnpm-parcel-lydellelm-safe-virtual-dom-2lp6</link>
      <guid>https://dev.to/dwayne/nix-pnpm-parcel-lydellelm-safe-virtual-dom-2lp6</guid>
      <description>&lt;p&gt;On January 12th, &lt;a href="https://flaviocorpa.com/" rel="noopener noreferrer"&gt;Flavio Corpa&lt;/a&gt; (&lt;code&gt;kvothe&lt;/code&gt;) &lt;a href="https://elmlang.slack.com/archives/C09LSPN17RA/p1768212686260249" rel="noopener noreferrer"&gt;reached out&lt;/a&gt; on the &lt;code&gt;#nix&lt;/code&gt; channel of the Elm Slack asking for help to make Nix and Parcel work together since he really wanted to use Simon Lydell's &lt;a href="https://github.com/lydell/elm-safe-virtual-dom/" rel="noopener noreferrer"&gt;&lt;code&gt;elm-safe-virtual-dom&lt;/code&gt;&lt;/a&gt; in his projects. Presumably, he read &lt;a href="https://github.com/omnibs" rel="noopener noreferrer"&gt;Juliano Solanho&lt;/a&gt;'s (&lt;code&gt;omnibs&lt;/code&gt;) &lt;a href="https://blog.noredink.com/post/800011916366020608/adopting-elm-safe-virtual-dom" rel="noopener noreferrer"&gt;post&lt;/a&gt;, on NoRedInk's blog, about adopting &lt;code&gt;lydell/elm-safe-virtual-dom&lt;/code&gt; that got him excited about the prospects. He had some issues with browsers and auto translation of content that &lt;code&gt;lydell/elm-safe-virtual-dom&lt;/code&gt; might be able to fix.&lt;/p&gt;

&lt;p&gt;I had recently &lt;a href="https://dev.to/dwayne/announcing-dwayneelm2nix-46pc"&gt;finished rewriting &lt;code&gt;elm2nix&lt;/code&gt;&lt;/a&gt; and was on the lookout for opportunities to apply my freshly acquired knowledge so I felt it was fortuitous timing for me that his request was made when I was most able to help.&lt;/p&gt;

&lt;p&gt;I approached my solution in two parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/dwayne/elm-countries-quiz/pull/1" rel="noopener noreferrer"&gt;Rewriting the project to use &lt;code&gt;dwayne/elm2nix&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/dwayne/elm-countries-quiz/pull/2" rel="noopener noreferrer"&gt;Adopting &lt;code&gt;lydell/elm-safe-virtual-dom&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you have Nix installed, you can try out the final result by executing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nix run github:dwayne/elm-countries-quiz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's get into it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rewriting the project to use &lt;code&gt;dwayne/elm2nix&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;In the PR, for this part, I highlighted &lt;a href="https://github.com/dwayne/elm-countries-quiz/pull/1" rel="noopener noreferrer"&gt;all the noteworthy changes&lt;/a&gt; so I won't rehash them here but I'd make a few comments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Name your development shell
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;nix develop&lt;/code&gt; doesn't give you any indication that you're in your development shell so to improve the situation you can give the shell a name and use it in your primary prompt string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;devShells&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;default&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;mkShell&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"elm-countries-quiz"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nv"&gt;shellHook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;''&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    export PS1="($name)\n$PS1"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;  ''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, when you enter your development shell the prompt changes and there's visual indication that you're in a different environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use the programming language's package manager
&lt;/h3&gt;

&lt;p&gt;In your development environment I found it's best to use Nix to install the programming language's package management tools and then use those tools to manage the project. In this way Nix becomes a way to manage your project's system dependencies while you continue to use the tools that are familiar to you from your programming language's ecosystem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;devShells&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;default&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;mkShell&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;packages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nv"&gt;elm2nix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;system&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;default&lt;/span&gt;
    &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;elmPackages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;elm&lt;/span&gt;
    &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;elmPackages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;elm-format&lt;/span&gt;
    &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;elmPackages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;elm-json&lt;/span&gt;
    &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;elmPackages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;elm-test-rs&lt;/span&gt;
    &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;elmPackages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;elm-review&lt;/span&gt;
    &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;nodejs_24&lt;/span&gt;
    &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;pnpm&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="nv"&gt;shellHook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;''&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    pnpm install --silent&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;  ''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For e.g. anyone entering the development shell above gets access to &lt;code&gt;elm2nix&lt;/code&gt;, &lt;code&gt;elm&lt;/code&gt;, &lt;code&gt;elm-format&lt;/code&gt;, &lt;code&gt;elm-json&lt;/code&gt;, &lt;code&gt;elm-test-rs&lt;/code&gt;, &lt;code&gt;elm-review&lt;/code&gt;, &lt;code&gt;node&lt;/code&gt;, &lt;code&gt;npm&lt;/code&gt;, and &lt;code&gt;pnpm&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In particular, you can now use &lt;code&gt;pnpm&lt;/code&gt; to manage your project's dependencies like you would if you installed it in the traditional way. As a bonus, I run &lt;code&gt;pnpm install --silent&lt;/code&gt; whenever you enter the development shell so that's one less thing you need to do to start working on the project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use aliases/functions to make it easier to perform common tasks
&lt;/h3&gt;

&lt;p&gt;In your &lt;code&gt;shellHook&lt;/code&gt; you can create &lt;a href="https://www.gnu.org/software/bash/manual/html_node/Aliases.html" rel="noopener noreferrer"&gt;Bash aliases&lt;/a&gt; or define &lt;a href="https://www.gnu.org/software/bash/manual/html_node/Shell-Functions.html" rel="noopener noreferrer"&gt;Bash functions&lt;/a&gt; to provide shortcuts to the common tasks you find yourself repeating as you work on your project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;devShells&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;default&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;mkShell&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;shellHook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;''&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    export PROJECT_ROOT="$PWD"&lt;/span&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="s2"&gt;    f () {&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;      elm-format                   \&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;        "$PROJECT_ROOT"/src        \&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;        "$PROJECT_ROOT"/review/src \&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;        "$PROJECT_ROOT"/tests      \&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;        --yes&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    }&lt;/span&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="s2"&gt;    r () {&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;      elm-review&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    }&lt;/span&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="s2"&gt;    t () {&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;      elm-test-rs "$PROJECT_ROOT"/tests/*.elm&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    }&lt;/span&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="s2"&gt;    c () {&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;      rm -rf "$PROJECT_ROOT"/{.parcel-cache,dist,elm-stuff,node_modules}&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    }&lt;/span&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="s2"&gt;    s () {&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;      pnpm start&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    }&lt;/span&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="s2"&gt;    echo "Type 'f' to run elm-format"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    echo "Type 'r' to run elm-review"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    echo "Type 't' to run elm-test-rs"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    echo "Type 'c' to remove build artifacts"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    echo "Type 's' to start the development server"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;  ''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;N.B.&lt;/strong&gt; &lt;em&gt;I used to use aliases but after learning that for almost every purpose, shell functions are preferable to aliases I've switched to functions in my recent projects.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Building with pnpm and Parcel in Nix
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://pnpm.io/" rel="noopener noreferrer"&gt;pnpm&lt;/a&gt; and &lt;a href="https://parceljs.org/" rel="noopener noreferrer"&gt;Parcel&lt;/a&gt; are used to build the application in &lt;a href="https://github.com/dwayne/elm-countries-quiz/blob/use-elm2nix/nix/app.nix" rel="noopener noreferrer"&gt;&lt;code&gt;nix/app.nix&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The file is structured using the conventional &lt;a href="https://nix.dev/tutorials/callpackage" rel="noopener noreferrer"&gt;&lt;code&gt;callPackage&lt;/code&gt; pattern&lt;/a&gt;. It uses &lt;code&gt;stdenv.mkDerivation&lt;/code&gt; with specific attributes to be able to use &lt;code&gt;pnpm&lt;/code&gt; and &lt;code&gt;parcel&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I use &lt;a href="https://nix.dev/tutorials/working-with-local-files" rel="noopener noreferrer"&gt;file sets&lt;/a&gt; to specify &lt;a href="https://github.com/dwayne/elm-countries-quiz/blob/use-elm2nix/nix/app.nix#L25-L31" rel="noopener noreferrer"&gt;the minimum files needed&lt;/a&gt; for the build to work. This ensures that rebuilds are only ever considered when those files change.&lt;/p&gt;

&lt;p&gt;To work with &lt;code&gt;pnpm&lt;/code&gt; I followed the advice in &lt;a href="https://nixos.org/manual/nixpkgs/stable/#javascript-pnpm" rel="noopener noreferrer"&gt;the &lt;code&gt;pnpm&lt;/code&gt; section of the Nixpkgs Reference Manual&lt;/a&gt;. In particular, I used the &lt;code&gt;fetchPnpmDeps&lt;/code&gt; function to create the pnpm store derivation corresponding to the project's &lt;code&gt;pnpm-lock.yaml&lt;/code&gt;. The setup hook &lt;code&gt;pnpmConfigHook&lt;/code&gt; prepares the build environment to install the pre-fetched dependencies.&lt;/p&gt;

&lt;p&gt;To ensure the Elm compiler could work fully offline I use &lt;a href="https://github.com/dwayne/elm2nix/blob/a599d6a93d106dbd2ca33e82114d05191834c79b/nix/prepare-elm-home-script.nix" rel="noopener noreferrer"&gt;the &lt;code&gt;prepareElmHomeScript&lt;/code&gt; function&lt;/a&gt;, from &lt;code&gt;dwayne/elm2nix&lt;/code&gt;, to &lt;a href="https://github.com/dwayne/elm-countries-quiz/blob/use-elm2nix/nix/app.nix#L50" rel="noopener noreferrer"&gt;create a local Elm cache&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Finally, &lt;code&gt;pnpm build&lt;/code&gt; invokes &lt;code&gt;parcel build index.html&lt;/code&gt; which builds the project and places the generated files in the &lt;code&gt;dist&lt;/code&gt; directory which I copy over to the current derivation's output directory to successfully complete the process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adopting &lt;code&gt;lydell/elm-safe-virtual-dom&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Simon Lydell does an excellent job explaining his project in its &lt;a href="https://github.com/lydell/elm-safe-virtual-dom/blob/900029432ff4f8e990636008ef7542052908d012/README.md" rel="noopener noreferrer"&gt;README&lt;/a&gt;. The tl;dr is that it's a robust virtual DOM for Elm that makes your application, among other things, resilient against browser extensions that mutate the DOM independent of Elm. One of the things this enables is browser-based translations, the very feature that &lt;code&gt;kvothe&lt;/code&gt; wanted working with his project.&lt;/p&gt;

&lt;p&gt;To adopt &lt;code&gt;lydell/elm-safe-virtual-dom&lt;/code&gt; you have to patch core Elm libraries and get the Elm compiler to use the patched versions.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;p&gt;The first major thing I did was to create derivations of the patched core Elm libraries whose output directories were structured in a very specific way.&lt;/p&gt;

&lt;p&gt;For e.g. &lt;code&gt;lydell/browser&lt;/code&gt; is supposed to replace &lt;code&gt;elm/browser 1.0.2&lt;/code&gt; so the output produced by &lt;a href="https://github.com/dwayne/elm-countries-quiz/blob/adopt-elm-safe-virtual-dom/nix/elm-safe-virtual-dom/lydell-browser.nix" rel="noopener noreferrer"&gt;&lt;code&gt;nix/elm-safe-virtual-dom/lydell-browser.nix&lt;/code&gt;&lt;/a&gt; looks as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;nix build github:dwayne/elm-countries-quiz#lydellBrowser &lt;span class="nt"&gt;-L&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;tree result
result
└── elm
    └── browser
        └── 1.0.2
            ├── elm.json
            └── src
                ├── Browser
                │   ├── AnimationManager.elm
                │   ├── Dom.elm
                │   ├── Events.elm
                │   └── Navigation.elm
                ├── Browser.elm
                ├── Debugger
                │   ├── Expando.elm
                │   ├── History.elm
                │   ├── Main.elm
                │   ├── Metadata.elm
                │   ├── Overlay.elm
                │   └── Report.elm
                └── Elm
                    └── Kernel
                        ├── Browser.js
                        ├── Browser.server.js
                        └── Debugger.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;i.e. it gets mapped to the core library it replaces and the directory structure is exactly what you'd find in the Elm cache under &lt;code&gt;$ELM_HOME/0.19.1/packages&lt;/code&gt;. But, the contents are precisely that of &lt;a href="https://github.com/lydell/browser" rel="noopener noreferrer"&gt;&lt;code&gt;lydell/browser&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/dwayne/elm-countries-quiz/blob/adopt-elm-safe-virtual-dom/nix/elm-safe-virtual-dom/lydell-virtual-dom.nix" rel="noopener noreferrer"&gt;&lt;code&gt;nix/elm-safe-virtual-dom/lydell-virtual-dom.nix&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/dwayne/elm-countries-quiz/blob/adopt-elm-safe-virtual-dom/nix/elm-safe-virtual-dom/lydell-html.nix" rel="noopener noreferrer"&gt;&lt;code&gt;nix/elm-safe-virtual-dom/lydell-html.nix&lt;/code&gt;&lt;/a&gt; work in a similar way. The pattern they all use was abstracted into a &lt;code&gt;mkPatch&lt;/code&gt; builder in &lt;a href="https://github.com/dwayne/elm-countries-quiz/blob/adopt-elm-safe-virtual-dom/nix/elm-safe-virtual-dom/patch.nix" rel="noopener noreferrer"&gt;&lt;code&gt;nix/elm-safe-virtual-dom/patch.nix&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you have derivations with outputs structured in that way and you know where to find the Elm cache it's a simple matter to remove the old package and replace it with the patched version in the Elm cache. That's what &lt;a href="https://github.com/dwayne/elm-countries-quiz/blob/adopt-elm-safe-virtual-dom/nix/elm-safe-virtual-dom/install-patch-script.nix" rel="noopener noreferrer"&gt;&lt;code&gt;nix/elm-safe-virtual-dom/install-patch-script.nix&lt;/code&gt;&lt;/a&gt; does. One interesting thing to note is how &lt;a href="https://github.com/dwayne/elm-countries-quiz/blob/adopt-elm-safe-virtual-dom/nix/elm-safe-virtual-dom/patch.nix#L31-L33" rel="noopener noreferrer"&gt;I used the patched derivation's &lt;code&gt;passthru&lt;/code&gt; attribute&lt;/a&gt; to get a hold of the &lt;code&gt;path&lt;/code&gt; in which the package would be stored in the Elm cache. I use the &lt;code&gt;path&lt;/code&gt; to &lt;a href="https://github.com/dwayne/elm-countries-quiz/blob/adopt-elm-safe-virtual-dom/nix/elm-safe-virtual-dom/install-patch-script.nix#L11-L12" rel="noopener noreferrer"&gt;remove the old package and to copy in the patched version&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/dwayne/elm-countries-quiz/blob/adopt-elm-safe-virtual-dom/nix/elm-safe-virtual-dom/patches.nix" rel="noopener noreferrer"&gt;&lt;code&gt;nix/elm-safe-virtual-dom/patches.nix&lt;/code&gt;&lt;/a&gt; installs all the required patches. This is used by both &lt;a href="https://github.com/dwayne/elm-countries-quiz/blob/adopt-elm-safe-virtual-dom/nix/app.nix#L54" rel="noopener noreferrer"&gt;&lt;code&gt;nix/app.nix&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/dwayne/elm-countries-quiz/blob/adopt-elm-safe-virtual-dom/nix/elm.nix#L40" rel="noopener noreferrer"&gt;&lt;code&gt;nix/elm.nix&lt;/code&gt;&lt;/a&gt;, after the local Elm cache has been created, to install the patched versions of the packages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;N.B.&lt;/strong&gt; &lt;em&gt;The full details are available in this &lt;a href="https://github.com/dwayne/elm-countries-quiz/pull/2" rel="noopener noreferrer"&gt;PR&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As you can imagine, this technique can be generalized to patch any published Elm package in any way you desire but I'm getting ahead of myself and will probably say more on that in a future post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.noredink.com/post/800011916366020608/adopting-elm-safe-virtual-dom" rel="noopener noreferrer"&gt;Adopting elm-safe-virtual-dom&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/lydell/elm-safe-virtual-dom/blob/900029432ff4f8e990636008ef7542052908d012/README.md" rel="noopener noreferrer"&gt;&lt;code&gt;lydell/elm-safe-virtual-dom&lt;/code&gt; README&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/dwayne/announcing-dwayneelm2nix-46pc"&gt;Announcing &lt;code&gt;dwayne/elm2nix&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Subscribe to my newsletter
&lt;/h2&gt;

&lt;p&gt;If you're interested in improving your skills with Elm then I invite you to subscribe to my newsletter, &lt;a href="https://buttondown.com/elmwithdwayne" rel="noopener noreferrer"&gt;Elm with Dwayne&lt;/a&gt;. To learn more about it, please read this &lt;a href="https://dev.to/dwayne/announcing-my-newsletter-elm-with-dwayne-hoi"&gt;announcement&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>nix</category>
      <category>pnpm</category>
      <category>parcel</category>
      <category>elm</category>
    </item>
    <item>
      <title>Making TodoMVC work with dwayne/elm2nix</title>
      <dc:creator>Dwayne Crooks</dc:creator>
      <pubDate>Thu, 22 Jan 2026 11:25:54 +0000</pubDate>
      <link>https://dev.to/dwayne/making-todomvc-work-with-dwayneelm2nix-5dal</link>
      <guid>https://dev.to/dwayne/making-todomvc-work-with-dwayneelm2nix-5dal</guid>
      <description>&lt;p&gt;The &lt;a href="https://todomvc.com/" rel="noopener noreferrer"&gt;TodoMVC&lt;/a&gt; web application is one of the first web applications I ever implemented. The first time I coded it up was when I was learning Backbone.js and the second time was when I was new to Elm. Throughout the years I've iterated on it multiple times to explore a variety of ideas. Now I'm at it again trying out &lt;a href="https://github.com/dwayne/elm2nix/tree/a599d6a93d106dbd2ca33e82114d05191834c79b" rel="noopener noreferrer"&gt;&lt;code&gt;dwayne/elm2nix&lt;/code&gt;&lt;/a&gt; on my &lt;a href="https://github.com/dwayne/elm-todos/tree/497f13b71f9d4eb0ee2466241c33c5ab1698e007" rel="noopener noreferrer"&gt;&lt;code&gt;dwayne/elm-todos&lt;/code&gt;&lt;/a&gt; project.&lt;/p&gt;

&lt;p&gt;There were two major highlights for me after rewriting &lt;code&gt;elm-todos&lt;/code&gt; to use &lt;code&gt;elm2nix&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Having the ability to run the web application from any machine with Nix installed.&lt;/li&gt;
&lt;li&gt;Making CI more reliable by being able to use Nix with a Nix-aware caching solution.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Below I'll explain these two points and then I'd go over the interesting details of the changes that were made.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run the web application from any machine with Nix installed
&lt;/h2&gt;

&lt;p&gt;If you have Nix installed you can run the development version of the application using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nix run github:dwayne/elm-todos
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, you can run the production version using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nix run github:dwayne/elm-todos#prod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don't have Nix installed, no worries, there are multiple ways to do it. The way I recommend is to use the &lt;a href="https://determinate.systems/nix-installer/" rel="noopener noreferrer"&gt;Determinate Nix Installer&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reliable CI
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://discourse.elm-lang.org/t/towards-more-reliable-ci/10503" rel="noopener noreferrer"&gt;Towards more reliable CI&lt;/a&gt;, Evan (&lt;code&gt;evancz&lt;/code&gt;) explained how to make CI much more reliable and Christian Ekrem (&lt;code&gt;cekrem&lt;/code&gt;) created a repository, &lt;a href="https://github.com/cekrem/elm-ci-setup" rel="noopener noreferrer"&gt;&lt;code&gt;cekrem/elm-ci-setup&lt;/code&gt;&lt;/a&gt;, that shows how to properly cache Elm dependencies in CI/CD pipelines. If you're not using Nix then you should definitely follow the approach they outline.&lt;/p&gt;

&lt;p&gt;Marek Fajkus (&lt;code&gt;turboMaCk&lt;/code&gt;), is one of the main people responsible for getting all the existing community tooling for the Elm language working with Nix with his, now defunct, &lt;a href="https://github.com/turboMaCk/nix-elm-tools" rel="noopener noreferrer"&gt;&lt;code&gt;turboMaCk/nix-elm-tools&lt;/code&gt;&lt;/a&gt; project. He &lt;a href="https://github.com/cekrem/elm-ci-setup/issues/1" rel="noopener noreferrer"&gt;recommends using &lt;code&gt;nix&lt;/code&gt; as a build tool&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In the end the best option I know about is to use &lt;code&gt;nix&lt;/code&gt; as a build tool. Nix uses immutable structure for everything and can do very granular caching because of that. By far the most robust system is to package the code using nix and instead of CI cache rely on nix-store caching or things like &lt;a href="https://www.cachix.org/" rel="noopener noreferrer"&gt;cachix&lt;/a&gt; (built using Elm btw) to immutable hash based granular caching. But it's hardly something we should be recommending to everyone.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Another Elm community member, Matt McHenry (&lt;code&gt;jerith666&lt;/code&gt;), also &lt;a href="https://discourse.elm-lang.org/t/towards-more-reliable-ci/10503/4" rel="noopener noreferrer"&gt;shared his thoughts on Nix&lt;/a&gt; which I've partially quoted below:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I think it’s worth mentioning &lt;a href="https://nixos.org/" rel="noopener noreferrer"&gt;Nix&lt;/a&gt; in this context as well. Nix builds run in a sandbox without any network access. So when you use Nix to build your Elm code, preventing network access at build time becomes a necessity rather than a “nice to have” for added stability and performance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I mention all this to share that once I was able get &lt;code&gt;dwayne/elm-todos&lt;/code&gt; building with Nix using &lt;code&gt;dwayne/elm2nix&lt;/code&gt; it was relatively easy to set up a GitHub workflow that did quality assurance on my code on every push.&lt;/p&gt;

&lt;p&gt;Here's the workflow, &lt;a href="https://github.com/dwayne/elm-todos/blob/497f13b71f9d4eb0ee2466241c33c5ab1698e007/.github/workflows/check.yml" rel="noopener noreferrer"&gt;&lt;code&gt;check.yml&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;push&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;check&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v6&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DeterminateSystems/nix-installer-action@main&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DeterminateSystems/magic-nix-cache-action@main&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nix flake check -L&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here are &lt;a href="https://github.com/dwayne/elm-todos/actions/workflows/check.yml" rel="noopener noreferrer"&gt;several runs&lt;/a&gt;. Here's &lt;a href="https://github.com/dwayne/elm-todos/actions/runs/20951633702/job/60206207952" rel="noopener noreferrer"&gt;a run where the cache needed to be warmed up&lt;/a&gt;. And, here's &lt;a href="https://github.com/dwayne/elm-todos/actions/runs/21010939361/job/60405347682" rel="noopener noreferrer"&gt;a run where the cache was used&lt;/a&gt;. In the last example, you'd notice that the flake checks didn't even need to be re-run since the derivations didn't change.&lt;/p&gt;

&lt;p&gt;I was going to use &lt;a href="https://www.cachix.org/" rel="noopener noreferrer"&gt;Cachix&lt;/a&gt;, but I stumbled upon the &lt;a href="https://determinate.systems/blog/magic-nix-cache/" rel="noopener noreferrer"&gt;Magic Nix Cache&lt;/a&gt; and decided to give it a try. With the Magic Nix Cache there's no signup, setup, or cost and you can use it by adding just one line to your workflow. For my simple public open source project the Magic Nix Cache seems to be a good fit. For other use cases Cachix might be the better service but this is not the post to go into comparisons between them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The changes
&lt;/h2&gt;

&lt;p&gt;I started &lt;a href="https://github.com/dwayne/elm-todos/tree/383664d3d2c3c883bf5ebf586808033f895953c5" rel="noopener noreferrer"&gt;here&lt;/a&gt; with the following project layout of key files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public/
    _redirects
    index.css
    index.html
src/
    Main.elm
.gitignore
Caddyfile
elm.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and after several iterations, see &lt;a href="https://github.com/dwayne/elm-todos/pull/4/commits" rel="noopener noreferrer"&gt;PR: Use &lt;code&gt;elm2nix&lt;/code&gt;&lt;/a&gt;, I ended &lt;a href="https://github.com/dwayne/elm-todos/tree/497f13b71f9d4eb0ee2466241c33c5ab1698e007" rel="noopener noreferrer"&gt;here&lt;/a&gt; with the following layout of key files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.github/workflows/
    check.yml
nix/
    build-css.nix
    build-html.nix
    build.nix
    htmlnano.nix
    serve.nix
public/
    _redirects
    index.css
    index.html
src/
    Main.elm
.gitignore
Caddyfile
elm.json
elm.lock
flake.lock
flake.nix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;N.B.&lt;/strong&gt; &lt;em&gt;All the &lt;code&gt;.nix&lt;/code&gt; files in the &lt;code&gt;nix/&lt;/code&gt; directory follow &lt;a href="https://nix.dev/tutorials/callpackage" rel="noopener noreferrer"&gt;the &lt;code&gt;callPackage&lt;/code&gt; convention&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  HTML
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/dwayne/elm-todos/blob/497f13b71f9d4eb0ee2466241c33c5ab1698e007/nix/build-html.nix" rel="noopener noreferrer"&gt;&lt;code&gt;nix/build-html.nix&lt;/code&gt;&lt;/a&gt; returns a function that allows you to decide whether or not you want to minify the HTML. The minification is done with &lt;a href="https://htmlnano.netlify.app/" rel="noopener noreferrer"&gt;&lt;code&gt;htmlnano&lt;/code&gt;&lt;/a&gt;, which Parcel used before switching to its own built-in minifier.&lt;/p&gt;

&lt;p&gt;The HTML is only ever built the first time and whenever &lt;code&gt;public/index.html&lt;/code&gt; changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  CSS
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/dwayne/elm-todos/blob/497f13b71f9d4eb0ee2466241c33c5ab1698e007/nix/build-css.nix" rel="noopener noreferrer"&gt;&lt;code&gt;nix/build-css.nix&lt;/code&gt;&lt;/a&gt; returns a function that allows you to decide whether or not you want to minify the CSS. The minification is done with &lt;a href="https://lightningcss.dev/" rel="noopener noreferrer"&gt;&lt;code&gt;lightningcss&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The CSS is only ever built the first time and whenever &lt;code&gt;public/index.css&lt;/code&gt; changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Elm
&lt;/h3&gt;

&lt;p&gt;The Elm application is &lt;a href="https://github.com/dwayne/elm-todos/blob/497f13b71f9d4eb0ee2466241c33c5ab1698e007/nix/build.nix#L25-L33" rel="noopener noreferrer"&gt;built using the &lt;code&gt;buildElmApplication&lt;/code&gt; builder&lt;/a&gt; from &lt;code&gt;dwayne/elm2nix&lt;/code&gt;. Its &lt;code&gt;src&lt;/code&gt; is filtered to include only the files required to build the application and nothing more.&lt;/p&gt;

&lt;h3&gt;
  
  
  Web app
&lt;/h3&gt;

&lt;p&gt;The entire web application is put together in &lt;a href="https://github.com/dwayne/elm-todos/blob/497f13b71f9d4eb0ee2466241c33c5ab1698e007/nix/build.nix" rel="noopener noreferrer"&gt;&lt;code&gt;nix/build.nix&lt;/code&gt;&lt;/a&gt;. It returns a function that allows you to configure how the HTML, CSS, and Elm files are built.&lt;/p&gt;

&lt;p&gt;If compression is enabled it compresses your HTML, CSS, and JavaScript output using &lt;code&gt;brotli&lt;/code&gt; and &lt;code&gt;zopflli&lt;/code&gt;. The &lt;a href="https://github.com/dwayne/elm-todos/blob/497f13b71f9d4eb0ee2466241c33c5ab1698e007/Caddyfile#L9" rel="noopener noreferrer"&gt;&lt;code&gt;Caddyfile&lt;/code&gt; is already configured to serve these compressed files&lt;/a&gt; locally so you can test that it works as expected.&lt;/p&gt;

&lt;p&gt;You have the option to include Netlify redirects, &lt;a href="https://github.com/dwayne/elm-todos/blob/497f13b71f9d4eb0ee2466241c33c5ab1698e007/public/_redirects" rel="noopener noreferrer"&gt;&lt;code&gt;public/_redirects&lt;/code&gt;&lt;/a&gt;, for proper handling of &lt;a href="https://dev.to/dwayne/how-to-host-browserapplication-projects-2mk#clientside-routing"&gt;client-side routing&lt;/a&gt; when you're building to deploy to Netlify.&lt;/p&gt;

&lt;h3&gt;
  
  
  Serving locally
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/dwayne/elm-todos/blob/497f13b71f9d4eb0ee2466241c33c5ab1698e007/nix/serve.nix" rel="noopener noreferrer"&gt;&lt;code&gt;nix/serve.nix&lt;/code&gt;&lt;/a&gt; returns a function that creates a shell script that runs Caddy with a derivation that contains the files to be served. The &lt;code&gt;flake.nix&lt;/code&gt; exposes these serve-based derivations as the &lt;code&gt;dev&lt;/code&gt; and &lt;code&gt;prod&lt;/code&gt; apps with &lt;code&gt;dev&lt;/code&gt; being the default. See &lt;a href="https://github.com/dwayne/elm-todos/blob/497f13b71f9d4eb0ee2466241c33c5ab1698e007/flake.nix#L94-L97" rel="noopener noreferrer"&gt;here&lt;/a&gt;. That's how the &lt;a href="https://nix.dev/manual/nix/2.28/command-ref/new-cli/nix3-run.html" rel="noopener noreferrer"&gt;&lt;code&gt;nix run&lt;/code&gt;&lt;/a&gt; functionality becomes available.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quality assurance
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;.github/workflows/check.yml&lt;/code&gt; contains the workflow that runs &lt;code&gt;nix flake check -L&lt;/code&gt; on every push. &lt;a href="https://nix.dev/manual/nix/2.28/command-ref/new-cli/nix3-flake-check.html" rel="noopener noreferrer"&gt;&lt;code&gt;nix flake check&lt;/code&gt;&lt;/a&gt; is a command to verify whether the flake can be evaluated successfully and that the derivations specified by &lt;a href="https://github.com/dwayne/elm-todos/blob/497f13b71f9d4eb0ee2466241c33c5ab1698e007/flake.nix#L102-L104" rel="noopener noreferrer"&gt;the flake's &lt;code&gt;checks&lt;/code&gt; output&lt;/a&gt; can be built successfully.&lt;/p&gt;

&lt;p&gt;I check that both the development and production versions of the Elm web application can be built successfully. Thanks to the &lt;a href="[Magic%20Nix%20Cache](https://determinate.systems/blog/magic-nix-cache/)"&gt;Magic Nix Cache&lt;/a&gt; and the way I filter the dependent files I pass to the HTML, CSS, and Elm derivations I'm able to minimize what needs to rebuilt on every push.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Firstly, you can run your Elm web application from any machine with Nix installed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nix run github:dwayne/elm-todos#prod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Secondly, Nix + &lt;a href="https://github.com/dwayne/elm2nix" rel="noopener noreferrer"&gt;&lt;code&gt;dwayne/elm2nix&lt;/code&gt;&lt;/a&gt; + a Nix-aware caching solution leads to reliable CI for Elm web applications.&lt;/p&gt;

&lt;p&gt;Finally, I hope I was able to convey the benefit of using Nix to build your Elm web application. TodoMVC may be a toy application but the ideas I shared do scale to larger applications with more complicated configurations. I plan to show you more interesting projects in future posts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/dwayne/elm-todos/tree/383664d3d2c3c883bf5ebf586808033f895953c5" rel="noopener noreferrer"&gt;&lt;code&gt;dwayne/elm-todos&lt;/code&gt; before&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dwayne/elm-todos/pull/4/commits" rel="noopener noreferrer"&gt;PR: Use &lt;code&gt;elm2nix&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dwayne/elm-todos/tree/497f13b71f9d4eb0ee2466241c33c5ab1698e007" rel="noopener noreferrer"&gt;&lt;code&gt;dwayne/elm-todos&lt;/code&gt; after&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/dwayne/announcing-dwayneelm2nix-46pc"&gt;Announcing &lt;code&gt;dwayne/elm2nix&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://determinate.systems/blog/magic-nix-cache/" rel="noopener noreferrer"&gt;Introducing the Magic Nix Cache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cachix.org/" rel="noopener noreferrer"&gt;Cachix - Nix binary cache hosting&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Subscribe to my newsletter
&lt;/h2&gt;

&lt;p&gt;If you're interested in improving your skills with Elm then I invite you to subscribe to my newsletter, &lt;a href="https://buttondown.com/elmwithdwayne" rel="noopener noreferrer"&gt;Elm with Dwayne&lt;/a&gt;. To learn more about it, please read this &lt;a href="https://dev.to/dwayne/announcing-my-newsletter-elm-with-dwayne-hoi"&gt;announcement&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>elm</category>
      <category>nix</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Announcing dwayne/elm2nix</title>
      <dc:creator>Dwayne Crooks</dc:creator>
      <pubDate>Thu, 08 Jan 2026 10:43:55 +0000</pubDate>
      <link>https://dev.to/dwayne/announcing-dwayneelm2nix-46pc</link>
      <guid>https://dev.to/dwayne/announcing-dwayneelm2nix-46pc</guid>
      <description>&lt;p&gt;Last year I worked a lot with Nix at my consulting job and I started to love it even more. I finally ascended its steep learning curve and began to enjoy using it for my development environments, CI setups, and VM configurations.&lt;/p&gt;

&lt;p&gt;I wasn't doing much Elm or Haskell in my day-to-day so I was longing for a practical and interesting project to work on that might be beneficial to either the Elm or Haskell community, while at the same time, would extend my skills within functional programming.&lt;/p&gt;

&lt;p&gt;When I researched &lt;a href="https://github.com/cachix/elm2nix" rel="noopener noreferrer"&gt;&lt;code&gt;cachix/elm2nix&lt;/code&gt;&lt;/a&gt; it seemed to fit perfectly with my goals because it combined all three technologies that I love, &lt;strong&gt;Elm&lt;/strong&gt;, &lt;strong&gt;Haskell&lt;/strong&gt;, and &lt;strong&gt;Nix&lt;/strong&gt;, to produce a useful tool for Elm developers. On reading the code and seeing how Nix and Haskell were being used I immediately saw opportunities for growing my skills while making improvements to the tool.&lt;/p&gt;

&lt;p&gt;Fast forward by about four months and I'm happy I took the plunge. I learned so much about each technology that I didn't know before and I was also able to find little ways to positively improve upon the project.&lt;/p&gt;

&lt;p&gt;It is with great delight that I announce &lt;a href="https://github.com/dwayne/elm2nix" rel="noopener noreferrer"&gt;&lt;code&gt;dwayne/elm2nix&lt;/code&gt;&lt;/a&gt;: A rewrite of &lt;code&gt;cachix/elm2nix&lt;/code&gt; with a few changes and improvements.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is &lt;code&gt;dwayne/elm2nix&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;Among other things, it is primarily a tool that helps you compile your Elm web application within a Nix build environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use &lt;code&gt;dwayne/elm2nix&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;In the folder containing your Elm web application's &lt;code&gt;elm.json&lt;/code&gt; you use&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;elm2nix lock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to generate an &lt;code&gt;elm.lock&lt;/code&gt; lock file.&lt;/p&gt;

&lt;p&gt;The lock file is used by a custom Nix build helper, called &lt;code&gt;buildElmApplication&lt;/code&gt;, that builds your Elm web application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;myApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;buildElmApplication&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-app"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;./.&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;elmLock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;./elm.lock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have &lt;code&gt;nix&lt;/code&gt; installed on your machine you can build the &lt;a href="https://github.com/dwayne/elm2nix/tree/a599d6a93d106dbd2ca33e82114d05191834c79b/example" rel="noopener noreferrer"&gt;example Elm web application&lt;/a&gt; as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nix build github:dwayne/elm2nix?dir&lt;span class="o"&gt;=&lt;/span&gt;example
less result/elm.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, that's basically it. For more details please read &lt;a href="https://github.com/dwayne/elm2nix/tree/a599d6a93d106dbd2ca33e82114d05191834c79b?tab=readme-ov-file#usage" rel="noopener noreferrer"&gt;the usage section of the README&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's changed or improved?
&lt;/h2&gt;

&lt;p&gt;Several things actually. Let's go through them one by one.&lt;/p&gt;

&lt;h2&gt;
  
  
  You can input one or more &lt;code&gt;elm.json&lt;/code&gt; files to create the lock file
&lt;/h2&gt;

&lt;p&gt;The lock file is a JSON file that has support for multiple versions of the same package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"elm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"package"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"sha256"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1m045b6cixyqygll4cjf9d9s3k1lj839kxc3gbid0fpqc4g10i6b"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"elm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"package"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.1.3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"sha256"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1hx986yqw1v2bpkrh6brszl8n8awwg1s8zi7v5qg0p1rqwvjlicz"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"elm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"package"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.1.4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"sha256"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0niq7id7r78ckvgap4psq0xdlnhcv1dz2j81lrnhl4fsgn0awvs8"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"elm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"package"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"parser"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"sha256"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"06xx29rmagc5r45qfpvrd393lz83ylngidfp08432f1qc8y6r3lh"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It acts as the source of truth for your project's Elm dependencies and it contains all the information Nix needs in order to prepare your &lt;code&gt;ELM_HOME&lt;/code&gt; with everything the Elm compiler requires to compile your project offline.&lt;/p&gt;

&lt;p&gt;Why would you want to pass multiple &lt;code&gt;elm.json&lt;/code&gt; files to &lt;code&gt;elm2nix lock&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;One use case I came across that wasn't handled well by &lt;code&gt;cachix/elm2nix&lt;/code&gt; was being able to run &lt;code&gt;elm-review&lt;/code&gt; within a Nix build. Your &lt;code&gt;review&lt;/code&gt; folder contains an &lt;code&gt;elm.json&lt;/code&gt; file that contains depedencies used by &lt;code&gt;elm-review&lt;/code&gt; but not by your Elm web application. However, to prepare the cache, to allow &lt;code&gt;elm-review&lt;/code&gt; to run offline, those dependencies need to be considered.&lt;/p&gt;

&lt;p&gt;As an example, the example project's &lt;a href="https://github.com/dwayne/elm2nix/tree/a599d6a93d106dbd2ca33e82114d05191834c79b/example/elm.lock" rel="noopener noreferrer"&gt;&lt;code&gt;elm.lock&lt;/code&gt;&lt;/a&gt; lock file was generated using&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;elm2nix lock elm.json review/elm.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;code&gt;registry.dat&lt;/code&gt; is automatically generated for you
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/dwayne/elm2nix/blob/a599d6a93d106dbd2ca33e82114d05191834c79b/nix/build-elm-application.nix#L72" rel="noopener noreferrer"&gt;&lt;code&gt;buildElmApplication&lt;/code&gt; generates the &lt;code&gt;registry.dat&lt;/code&gt; file&lt;/a&gt; for you on the fly. That's one less file you have to consider managing.&lt;/p&gt;

&lt;h2&gt;
  
  
  You can display any &lt;code&gt;registry.dat&lt;/code&gt; file as JSON
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;registry.dat&lt;/code&gt; file is a binary file that encodes all the Elm packages in your cache. The &lt;code&gt;registry&lt;/code&gt; subcommand of &lt;code&gt;elm2nix&lt;/code&gt; has a &lt;code&gt;view&lt;/code&gt; subcommand that allows you to display any valid &lt;code&gt;registry.dat&lt;/code&gt; file as JSON.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;elm2nix registry view
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can certainly use a tool like &lt;code&gt;xxd&lt;/code&gt; to get a sense of its contents but I found that having a human-readable view was a huge benefit for debugging purposes.&lt;/p&gt;

&lt;p&gt;In fact, if you're curious and want to understand the binary format used by the &lt;code&gt;registry.dat&lt;/code&gt; file you can read the &lt;a href="https://github.com/dwayne/elm2nix/blob/a599d6a93d106dbd2ca33e82114d05191834c79b/test/Test/Elm2Nix/Data/RegistryDat.hs#L76-L122" rel="noopener noreferrer"&gt;&lt;code&gt;binarySerializationSpec&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;buildElmApplication&lt;/code&gt; build helper allows you to build an Elm web application in a variety of ways by setting options
&lt;/h2&gt;

&lt;p&gt;All the typical tasks you'd want to perform during a build of your Elm web application are possible by changing a few options.&lt;/p&gt;

&lt;h3&gt;
  
  
  Turn on the time-travelling debugger
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;debuggedMyApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;myApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;override&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;enableDebugger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"debugged.js"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fail the build if your Elm source code is improperly formatted
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;formattingCheckedMyApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;myApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;override&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;doElmFormat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;elmFormatSourceFiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"review/src"&lt;/span&gt; &lt;span class="s2"&gt;"src"&lt;/span&gt; &lt;span class="s2"&gt;"tests"&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nv"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"formatting-checked.js"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It runs &lt;code&gt;elm-format --validate&lt;/code&gt; on the Elm source code it finds in the given directories.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fail the build if your Elm tests fail
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;testedMyApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;formattingCheckedMyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;override&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;doElmTest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tested.js"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It runs your Elm tests.&lt;/p&gt;

&lt;p&gt;BTW, since I overrode &lt;code&gt;formattingCheckedMyApp&lt;/code&gt;, the &lt;code&gt;testedMyApp&lt;/code&gt; derivation will also fail for improper formatting reasons. So, you can override &lt;code&gt;buildElmApplication&lt;/code&gt;-based derivations in the standard Nix ways and it will work as expected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fail the build if &lt;code&gt;elm-review&lt;/code&gt; fails
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;reviewedMyApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;testedMyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;override&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;doElmReview&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"reviewed.js"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It runs &lt;code&gt;elm-review&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Thank you to &lt;a href="https://github.com/jfmengels" rel="noopener noreferrer"&gt;Jeroen Engels&lt;/a&gt; for all his help debugging the &lt;a href="https://github.com/jfmengels/node-elm-review/issues/390" rel="noopener noreferrer"&gt;various&lt;/a&gt; &lt;a href="https://github.com/jfmengels/node-elm-review/issues/399" rel="noopener noreferrer"&gt;issues&lt;/a&gt; I had getting &lt;code&gt;elm-review&lt;/code&gt; working in a Nix build environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Turn on optimizations
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;optimizedMyApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;reviewedMyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;override&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;enableOptimizations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"optimized.js"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are three optimization levels. By default, it uses the first level which compiles your Elm code with &lt;code&gt;elm make --optimize&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;optimized2MyApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;optimizedMyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;override&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;optimizeLevel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"optimized2.js"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Levels 2 and 3 both use &lt;a href="https://github.com/mdgriffith/elm-optimize-level-2" rel="noopener noreferrer"&gt;&lt;code&gt;elm-optimize-level-2&lt;/code&gt;&lt;/a&gt;. The only difference is that level 3 provides the &lt;code&gt;--optimize-speed&lt;/code&gt; flag to &lt;code&gt;elm-optimize-level-2&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Combine multiple Elm web applications into one JavaScript file
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;combinedMyApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;optimizedMyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;override&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"src/App1.elm"&lt;/span&gt; &lt;span class="s2"&gt;"src/App2.elm"&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nv"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"combined.js"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Elm compiler provides support for combining multiple web applications into one JavaScript file and &lt;code&gt;buildElmApplication&lt;/code&gt; supports that use case as well through the &lt;code&gt;entry&lt;/code&gt; option.&lt;/p&gt;

&lt;p&gt;However, it must be noted that since &lt;code&gt;elm-optimize-level-2&lt;/code&gt; doesn't support that use case, you will not be able to combine multiple Elm web applications when level 2 or 3 optimization is turned on. In fact, this situation is correctly detected by &lt;code&gt;buildElmApplication&lt;/code&gt; and explicitly disallowed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enable minification with UglifyJS or Terser
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;minifiedMyApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;optimized2MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;override&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;doMinification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;useTerser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"minified.js"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default it uses &lt;a href="https://github.com/mishoo/UglifyJS" rel="noopener noreferrer"&gt;UglifyJS&lt;/a&gt; as recommended by &lt;a href="https://discourse.elm-lang.org/t/what-i-ve-learned-about-minifying-elm-code/7632" rel="noopener noreferrer"&gt;What I've learned about minifying Elm code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Minification is done in a separate minification phase, called &lt;code&gt;minificationPhase&lt;/code&gt;, that's easily customizable. What this means is that you can configure it to do anything that &lt;code&gt;lydell&lt;/code&gt; suggested in his Elm Discourse post.&lt;/p&gt;

&lt;p&gt;In fact, everything I've shown you up to this point and will show you afterwards happens in independent phases so it's all easily customizable once you understand how &lt;a href="https://nixos.org/manual/nixpkgs/stable/#sec-stdenv-phases" rel="noopener noreferrer"&gt;phases&lt;/a&gt; work in Nix.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enable compression with gzip and brotli
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;compressedMyApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;minifiedMyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;override&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;doCompression&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"compressed.js"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It compresses the JavaScript file using both &lt;code&gt;gzip&lt;/code&gt; and &lt;code&gt;brotli&lt;/code&gt;. By default, it passes the &lt;code&gt;-9&lt;/code&gt; flag to &lt;code&gt;gzip&lt;/code&gt; and the &lt;code&gt;-Z&lt;/code&gt; flag to &lt;code&gt;brotli&lt;/code&gt;. You can configure both by passing the relevant flags to the &lt;code&gt;gzipFlags&lt;/code&gt; and &lt;code&gt;brotliFlags&lt;/code&gt; options as you see fit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Show a report about the changes in your file size due to minification and compression
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;reportedMyApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;compressedMyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;override&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;doReporting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"reported.js"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you turn on reporting and build the derivation using &lt;code&gt;nix build -L&lt;/code&gt; you will see a report detailing the impact that minification and compression had on the output size of the JavaScript that's produced. If you forget to build with &lt;code&gt;-L&lt;/code&gt; then you can always view the logs generated by a build of your derivation using &lt;code&gt;nix log&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The report is similar to the one used in the &lt;a href="https://guide.elm-lang.org/optimization/asset_size#scripts" rel="noopener noreferrer"&gt;Asset Size&lt;/a&gt; section of the Elm Guide.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enable content hashing for cache busting purposes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;hashedMyApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;reportedMyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;override&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;doContentHashing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashed.js"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Content hashing or fingerprinting takes your &lt;code&gt;hashed.js&lt;/code&gt;, finds its &lt;code&gt;sha256sum&lt;/code&gt;, and rewrites the name of the file to be something like &lt;code&gt;hashed.abcd1234.js&lt;/code&gt;. This is useful for &lt;a href="https://csswizardry.com/2019/03/cache-control-for-civilians/#cache-busting" rel="noopener noreferrer"&gt;cache busting&lt;/a&gt; purposes.&lt;/p&gt;

&lt;p&gt;By default, it uses a hash length of 8 and it removes the files with no hash in their names. Use the &lt;code&gt;hashLength&lt;/code&gt; option to change the hash length and set &lt;code&gt;keepFilesWithNoHashInFilenames&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; so as not to remove the original files.&lt;/p&gt;

&lt;p&gt;In addition, a &lt;code&gt;manifest.json&lt;/code&gt; is generated to help you map the original filenames to their hashed equivalents when referring to them in your HTML or other resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  All at once
&lt;/h3&gt;

&lt;p&gt;Up to this point, I presented the possibilities by overriding previous derivations. However, you can definitely just configure it all at once.&lt;/p&gt;

&lt;p&gt;As an example, the &lt;code&gt;hashedMyApp&lt;/code&gt; derivation that we ended with is equivalent to the following &lt;code&gt;finalMyApp&lt;/code&gt; derivation which configures everything in one go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;finalMyApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;buildElmApplication&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-app"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;./.&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;elmLock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;./elm.lock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nv"&gt;doElmFormat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;elmFormatSourceFiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"review/src"&lt;/span&gt; &lt;span class="s2"&gt;"src"&lt;/span&gt; &lt;span class="s2"&gt;"tests"&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="nv"&gt;doElmTest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;doElmReview&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nv"&gt;enableOptimizations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;optimizeLevel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nv"&gt;doMinification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;useTerser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nv"&gt;doCompression&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;doReporting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;doContentHashing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nv"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashed.js"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my opinion, the best way to understand the extent of what's possible is to &lt;a href="https://github.com/dwayne/elm2nix/blob/a599d6a93d106dbd2ca33e82114d05191834c79b/nix/build-elm-application.nix" rel="noopener noreferrer"&gt;read the Nix code for &lt;code&gt;buildElmApplication&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced: The derivations and scripts that make &lt;code&gt;buildElmApplication&lt;/code&gt; work are readily available for you to use for your own purposes
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;elm2nix&lt;/code&gt; flake gives you access to several derivations and scripts which you can access as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="kn"&gt;inherit&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;elm2nix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;elm2nix&lt;/span&gt; &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;buildElmApplication&lt;/span&gt;
    &lt;span class="nv"&gt;generateRegistryDat&lt;/span&gt;
    &lt;span class="nv"&gt;prepareElmHomeScript&lt;/span&gt;
    &lt;span class="nv"&gt;dotElmLinks&lt;/span&gt;
    &lt;span class="nv"&gt;symbolicLinksToPackagesScript&lt;/span&gt;
    &lt;span class="nv"&gt;fetchElmPackage&lt;/span&gt;
    &lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You've already seen &lt;code&gt;buildElmApplication&lt;/code&gt;. Let's talk about &lt;code&gt;prepareElmHomeScript&lt;/code&gt; and &lt;code&gt;dotElmLinks&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;prepareElmHomeScript&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;elmLock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;./elm.lock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;registryDat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;generateRegistryDat&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;inherit&lt;/span&gt; &lt;span class="nv"&gt;elmLock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nv"&gt;examplePrepareElmHomeScript&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;prepareElmHomeScript&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;inherit&lt;/span&gt; &lt;span class="nv"&gt;elmLock&lt;/span&gt; &lt;span class="nv"&gt;registryDat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It returns a string of Bash commands that will be used to configure &lt;code&gt;ELM_HOME&lt;/code&gt;. If you have &lt;code&gt;nix&lt;/code&gt; installed on your machine you can view the script from the &lt;a href="https://github.com/dwayne/elm2nix/tree/a599d6a93d106dbd2ca33e82114d05191834c79b/example" rel="noopener noreferrer"&gt;example Elm web application&lt;/a&gt; as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nix repl
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; :lf github:dwayne/elm2nix?dir&lt;span class="o"&gt;=&lt;/span&gt;example
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; :p outputs.scripts.x86_64-linux.examplePrepareElmHomeScript
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Prepare .elm and set ELM_HOME=.elm"&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-LR&lt;/span&gt; &lt;span class="s2"&gt;"/nix/store/wr561wqfhpr98xvji2q1g72y5ylgmq3p-dot-elm-links"&lt;/span&gt; .elm
&lt;span class="nb"&gt;chmod&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; +w .elm
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ELM_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;/.elm"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;N.B.&lt;/strong&gt; &lt;em&gt;The above example assumes an &lt;code&gt;x86_64-linux&lt;/code&gt; system but there is also support for &lt;code&gt;aarch64-darwin&lt;/code&gt;, &lt;code&gt;aarch64-linux&lt;/code&gt;, and &lt;code&gt;x86_64-darwin&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;dotElmLinks&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;exampleDotElmLinks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;dotElmLinks&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;inherit&lt;/span&gt; &lt;span class="nv"&gt;elmLock&lt;/span&gt; &lt;span class="nv"&gt;registryDat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The derivation creates an Elm cache containing precisely the dependencies in your &lt;code&gt;elm.lock&lt;/code&gt; lock file. If you have &lt;code&gt;nix&lt;/code&gt; installed on your machine you can view the output of the &lt;code&gt;exampleDotElmLinks&lt;/code&gt; derivation from the &lt;a href="https://github.com/dwayne/elm2nix/tree/a599d6a93d106dbd2ca33e82114d05191834c79b/example" rel="noopener noreferrer"&gt;example Elm web application&lt;/a&gt; as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nix build github:dwayne/elm2nix?dir=example#exampleDotElmLinks -L
tree result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Recently, there's been a lot of &lt;a href="https://discourse.elm-lang.org/t/elm-connection-timeout-while-downloading-packages/10506" rel="noopener noreferrer"&gt;interesting&lt;/a&gt; &lt;a href="https://discourse.elm-lang.org/t/towards-more-reliable-ci/10503" rel="noopener noreferrer"&gt;discussions&lt;/a&gt; in the Elm community about issues compiling Elm web applications on external machines, for example in CI or on Digital Ocean VMs. If all you need is an Elm cache of your application's dependencies, then &lt;code&gt;dotElmLinks&lt;/code&gt; can be used to prepare it for you. Either way, if you're using &lt;code&gt;elm2nix&lt;/code&gt; then those issues won't be issues for you at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Nix may have a steep learning curve but I'm convinced it's an immensely valuable tool to have in your toolbox. &lt;code&gt;dwayne/elm2nix&lt;/code&gt; brings the power of Nix to Elm and doesn't compromise on the versatility of Nix. The possibilities are endless and this is just the beginning. I hope we can all get to see the full power of Nix applied to the Elm ecosystem.&lt;/p&gt;

&lt;p&gt;Finally, thank you to &lt;a href="https://github.com/domenkozar" rel="noopener noreferrer"&gt;Domen Kožar&lt;/a&gt; for sharing his ideas, back in 2016, that's almost 10 years ago, on how &lt;code&gt;elm2nix&lt;/code&gt; could work in the first place (&lt;a href="https://blog.hercules-ci.com/elm/2019/01/03/elm2nix-0.1/" rel="noopener noreferrer"&gt;elm2nix 0.1&lt;/a&gt;). Over the years a lot of developers have contributed to molding &lt;code&gt;cachix/elm2nix&lt;/code&gt; to what it is today and it is their work that I've been able to build upon and improve, so I'm immensely greatful to all the contributors of that project as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subscribe to my newsletter
&lt;/h2&gt;

&lt;p&gt;If you're interested in improving your skills with Elm then I invite you to subscribe to my newsletter, &lt;a href="https://buttondown.com/elmwithdwayne" rel="noopener noreferrer"&gt;Elm with Dwayne&lt;/a&gt;. To learn more about it, please read this &lt;a href="https://dev.to/dwayne/announcing-my-newsletter-elm-with-dwayne-hoi"&gt;announcement&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>elm</category>
      <category>nix</category>
      <category>haskell</category>
    </item>
    <item>
      <title>Frontend Mentor's Contact form challenge built with Elm</title>
      <dc:creator>Dwayne Crooks</dc:creator>
      <pubDate>Mon, 29 Sep 2025 12:23:52 +0000</pubDate>
      <link>https://dev.to/dwayne/frontend-mentors-contact-form-challenge-built-with-elm-47ig</link>
      <guid>https://dev.to/dwayne/frontend-mentors-contact-form-challenge-built-with-elm-47ig</guid>
      <description>&lt;p&gt;My motivation for completing &lt;a href="https://www.frontendmentor.io/" rel="noopener noreferrer"&gt;Frontend Mentor&lt;/a&gt;'s &lt;a href="https://www.frontendmentor.io/challenges/contact-form--G-hYlqKJj" rel="noopener noreferrer"&gt;Contact form challenge&lt;/a&gt; was to test-drive my &lt;a href="https://github.com/dwayne/elm-field" rel="noopener noreferrer"&gt;field&lt;/a&gt; and &lt;a href="https://github.com/dwayne/elm-form" rel="noopener noreferrer"&gt;form&lt;/a&gt; packages. I also recently started using and enjoying &lt;a href="https://astro.build/" rel="noopener noreferrer"&gt;Astro&lt;/a&gt; so I wanted to explore what it would be like to use it as my &lt;a href="https://bradfrost.com/blog/post/a-frontend-workshop-environment/" rel="noopener noreferrer"&gt;frontend workshop environment&lt;/a&gt;. I even ended up experimenting with Makefiles, Nushell, and Nix flakes within this project. Overall, I learned a lot and gained some new skills. In this post I'm sharing the highlights of my experience.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/Tmje5FRkQto"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;h2&gt;
  
  
  How I built the form
&lt;/h2&gt;

&lt;p&gt;I followed the typical process I've used for all my Elm web applications. In general, I build the UI first using just HTML/CSS, then I translate the UI to &lt;code&gt;elm/html&lt;/code&gt;, then I implement the business logic, and finally I combine the UI and business logic.&lt;/p&gt;

&lt;p&gt;For this project, here's the specific steps I followed:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I implemented the UI, using HTML and Sass, in a custom Astro frontend workshop environment.&lt;/li&gt;
&lt;li&gt;I translated the UI into &lt;code&gt;elm/html&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;I used my &lt;a href="https://github.com/dwayne/elm-field" rel="noopener noreferrer"&gt;field&lt;/a&gt; and &lt;a href="https://github.com/dwayne/elm-form" rel="noopener noreferrer"&gt;form&lt;/a&gt; packages to implement the form's logic.&lt;/li&gt;
&lt;li&gt;And finally, I wrote trivial glue code to connect the UI and form logic.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The UI
&lt;/h3&gt;

&lt;p&gt;The UI was implemented in the &lt;a href="https://github.com/dwayne/elm-fm-contact-form/tree/88dbde8ee6cf7fc8ff87c0881aa48b5330bfd9ea/workshop" rel="noopener noreferrer"&gt;&lt;code&gt;workshop&lt;/code&gt;&lt;/a&gt; directory using Astro. I structured the HTML and Sass using a combination of BEM, data attributes, and utility classes. Each block was implemented using an &lt;a href="https://docs.astro.build/en/basics/astro-components/" rel="noopener noreferrer"&gt;Astro component&lt;/a&gt;. Pages were assembled to test out colors, typography, each individual block, combinations of blocks, and entire page states.&lt;/p&gt;

&lt;p&gt;The workshop acted as the single source of truth for the HTML and CSS. For this project, the artifacts were a CSS file and a font file (&lt;code&gt;.ttf&lt;/code&gt;).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://css-tricks.com/building-a-scalable-css-architecture-with-bem-and-utility-classes/" rel="noopener noreferrer"&gt;Building a Scalable CSS Architecture with BEM and Utility Classes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://elisehe.in/2022/10/16/attribute-selectors" rel="noopener noreferrer"&gt;The wasted potential of CSS attribute selectors&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;elm/html&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Based on the HTML structure and CSS styles developed in the frontend workshop environment I built the corresponding &lt;a href="https://github.com/dwayne/elm-fm-contact-form/tree/88dbde8ee6cf7fc8ff87c0881aa48b5330bfd9ea/app/src/elm/View" rel="noopener noreferrer"&gt;Elm views&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As an example, let's take a look at the path to get to the &lt;code&gt;LabelledRadio&lt;/code&gt; component.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Astro component&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
import Radio from "@/components/Radio.astro";

const { labelProps, text = "Text", ...rest } = Astro.props;
---
&amp;lt;label class:list={["labelled-radio", labelProps?.class]}&amp;gt;
  &amp;lt;Radio {...rest} /&amp;gt;
  {text}
&amp;lt;/label&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Sass&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@use&lt;/span&gt; &lt;span class="s2"&gt;"colors"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@use&lt;/span&gt; &lt;span class="s2"&gt;"spaces"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@use&lt;/span&gt; &lt;span class="s2"&gt;"typography"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;.labelled-radio&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="na"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;spaces&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get-space&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;150&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nl"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fit-content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get-color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"grey-500"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;spaces&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get-space&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;spaces&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get-space&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;150&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;spaces&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get-space&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;300&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get-color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"white"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get-color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"grey-900"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;typography&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body-m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get-color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"green-600"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"radio"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nd"&gt;:checked&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get-color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"green-600"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get-color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"green-200"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get-color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"white"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Elm view&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;LabelledRadio&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ViewOptions&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Attributes&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Lib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;View&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Generic&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Generic&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Radio&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Radio&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;ViewOptions&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;radioAttrs&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Attribute&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ViewOptions&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Attribute&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;
&lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;radioAttrs&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Generic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;preAttrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;labelled-radio"&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;postAttrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;attrs&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;Radio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="n"&gt;radioAttrs&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I even wrote &lt;a href="https://github.com/dwayne/elm-fm-contact-form/tree/88dbde8ee6cf7fc8ff87c0881aa48b5330bfd9ea/app/tests/Test/View" rel="noopener noreferrer"&gt;unit tests for all the Elm views&lt;/a&gt;. Here's the test for the &lt;code&gt;LabelledRadio&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="kt"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;View&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;LabelledRadio&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;suite&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Attributes&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Test&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;describe&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Query&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Query&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Selector&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Sel&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;LabelledRadio&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;LabelledRadio&lt;/span&gt;


&lt;span class="n"&gt;suite&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Test&lt;/span&gt;
&lt;span class="n"&gt;suite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;View.LabelledRadio"&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;general enquiry"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt;
                &lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="kt"&gt;LabelledRadio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;
                        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;General Enquiry"&lt;/span&gt;
                        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;radioAttrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                            &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;query-type"&lt;/span&gt;
                            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checked&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
                            &lt;span class="p"&gt;]&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;
                        &lt;span class="p"&gt;[]&lt;/span&gt;
            &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;label"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt;
                &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="n"&gt;view&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromHtml&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;has&lt;/span&gt;
                            &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;Sel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;label"&lt;/span&gt;
                            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Sel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exactClassName&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;labelled-radio"&lt;/span&gt;
                            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Sel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exactText&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;General Enquiry"&lt;/span&gt;
                            &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;radio"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt;
                &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="n"&gt;view&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromHtml&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;
                            &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;Sel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type_&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;radio"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                            &lt;span class="p"&gt;]&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;has&lt;/span&gt;
                            &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;Sel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;query-type"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Sel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checked&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
                            &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two most interesting views to test were actually &lt;a href="https://github.com/dwayne/elm-fm-contact-form/blob/88dbde8ee6cf7fc8ff87c0881aa48b5330bfd9ea/app/tests/Test/View/Field.elm" rel="noopener noreferrer"&gt;&lt;code&gt;View.Field&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/dwayne/elm-fm-contact-form/blob/88dbde8ee6cf7fc8ff87c0881aa48b5330bfd9ea/app/tests/Test/View/QueryType.elm" rel="noopener noreferrer"&gt;&lt;code&gt;View.QueryType&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Form logic
&lt;/h3&gt;

&lt;p&gt;The form's logic is independent of the form's view. It was implemented in &lt;a href="https://github.com/dwayne/elm-fm-contact-form/blob/88dbde8ee6cf7fc8ff87c0881aa48b5330bfd9ea/app/src/elm/Data/Contact/Form.elm" rel="noopener noreferrer"&gt;&lt;code&gt;app/src/elm/Data/Contact/Form.elm&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Contact&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Form&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Accessors&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Output&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Contact&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Bool&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Contact&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Email&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Email&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Contact&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;QueryType&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;QueryType&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;QueryType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Contact&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Text&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Text&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Advanced&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Validation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Accessor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;



&lt;span class="c1"&gt;-- FORM&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Form&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt; &lt;span class="kt"&gt;Accessors&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="kt"&gt;Output&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;firstName&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="kt"&gt;Text&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lastName&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="kt"&gt;Text&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Email&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="kt"&gt;Email&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;queryType&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;QueryType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="kt"&gt;QueryType&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="kt"&gt;Text&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;consent&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Accessors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;firstName&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Accessor&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lastName&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Accessor&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Accessor&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Email&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="kt"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;queryType&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Accessor&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;QueryType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="kt"&gt;QueryType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Accessor&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;consent&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Accessor&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;FirstNameError&lt;/span&gt; &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;LastNameError&lt;/span&gt; &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;EmailError&lt;/span&gt; &lt;span class="kt"&gt;Email&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;QueryTypeError&lt;/span&gt; &lt;span class="kt"&gt;QueryType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;MessageError&lt;/span&gt; &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;ConsentError&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;firstName&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Text&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lastName&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Text&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Email&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;queryType&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;QueryType&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Text&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;
&lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;accessors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;accessors&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;



&lt;span class="c1"&gt;-- INIT&lt;/span&gt;


&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt;
&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;firstName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fieldType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;min&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lastName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fieldType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;min&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="kt"&gt;Email&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fieldType&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;queryType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="kt"&gt;QueryType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fieldType&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fieldType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;min&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;consent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;



&lt;span class="c1"&gt;-- ACCESSSORS&lt;/span&gt;


&lt;span class="n"&gt;accessors&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Accessors&lt;/span&gt;
&lt;span class="n"&gt;accessors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;firstName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;modify&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;firstName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lastName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lastName&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;modify&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;lastName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lastName&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;modify&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;queryType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queryType&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;modify&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;queryType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queryType&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;modify&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;consent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;consent&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;modify&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;consent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;consent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;



&lt;span class="c1"&gt;-- VALIDATE&lt;/span&gt;


&lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Validation&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="kt"&gt;Output&lt;/span&gt;
&lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;always&lt;/span&gt; &lt;span class="kt"&gt;Output&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;succeed&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;applyValidation&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;consent&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapError&lt;/span&gt; &lt;span class="kt"&gt;ConsentError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;applyValidation&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapError&lt;/span&gt; &lt;span class="kt"&gt;FirstNameError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;applyValidation&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lastName&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapError&lt;/span&gt; &lt;span class="kt"&gt;LastNameError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;applyValidation&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapError&lt;/span&gt; &lt;span class="kt"&gt;EmailError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;applyValidation&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queryType&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapError&lt;/span&gt; &lt;span class="kt"&gt;QueryTypeError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;applyValidation&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapError&lt;/span&gt; &lt;span class="kt"&gt;MessageError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, extensively tested in &lt;a href="https://github.com/dwayne/elm-fm-contact-form/blob/88dbde8ee6cf7fc8ff87c0881aa48b5330bfd9ea/app/tests/Test/Data/Contact/Form.elm" rel="noopener noreferrer"&gt;&lt;code&gt;app/tests/Test/Data/Contact/Form.elm&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="kt"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Contact&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Form&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;suite&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Contact&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Form&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Contact&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Contact&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Text&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Text&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Expect&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Advanced&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Fuzz&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Test&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;describe&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fuzz&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;suite&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Test&lt;/span&gt;
&lt;span class="n"&gt;suite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Data.Contact.Form"&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;all fields are clean and empty"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt;
            &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt;
                    &lt;span class="n"&gt;apply2&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                        &lt;span class="kt"&gt;Expect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;equal&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;in&lt;/span&gt;
                &lt;span class="kt"&gt;Contact&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;
                    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toState&lt;/span&gt;
                    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Expect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;
                        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;apply2&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isClean&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isEmpty&lt;/span&gt;
                        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lastName&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;apply2&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isClean&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isEmpty&lt;/span&gt;
                        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;apply2&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isClean&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isEmpty&lt;/span&gt;
                        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queryType&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;apply2&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isClean&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isEmpty&lt;/span&gt;
                        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;apply2&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isClean&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isEmpty&lt;/span&gt;
                        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;consent&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;apply2&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isClean&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isEmpty&lt;/span&gt;
                        &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firstName"&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;fuzz&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Fuzz&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;intRange&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;it is required and must be less than 25 characters long"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt;
                &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="k"&gt;let&lt;/span&gt;
                        &lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                            &lt;span class="kt"&gt;Contact&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;
                                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;modify&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFromString&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;repeat&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;f"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                    &lt;span class="k"&gt;in&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
                        &lt;span class="n"&gt;form&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toResult&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Expect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;equal&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Err&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blankError&lt;/span&gt; &lt;span class="p"&gt;])&lt;/span&gt;

                    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
                        &lt;span class="n"&gt;form&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toResult&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Expect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;equal&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Err&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customError&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;TooLong&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;])&lt;/span&gt;

                    &lt;span class="k"&gt;else&lt;/span&gt;
                        &lt;span class="n"&gt;form&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isValid&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Expect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;equal&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lastName"&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;fuzz&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Fuzz&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;intRange&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;it is required and must be less than 25 characters long"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt;
                &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="k"&gt;let&lt;/span&gt;
                        &lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                            &lt;span class="kt"&gt;Contact&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;
                                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;modify&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lastName&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFromString&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;repeat&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;l"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                    &lt;span class="k"&gt;in&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
                        &lt;span class="n"&gt;form&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lastName&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toResult&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Expect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;equal&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Err&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blankError&lt;/span&gt; &lt;span class="p"&gt;])&lt;/span&gt;

                    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
                        &lt;span class="n"&gt;form&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lastName&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toResult&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Expect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;equal&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Err&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customError&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;TooLong&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;])&lt;/span&gt;

                    &lt;span class="k"&gt;else&lt;/span&gt;
                        &lt;span class="n"&gt;form&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lastName&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isValid&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Expect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;equal&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email"&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;it is required"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt;
                &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="kt"&gt;Contact&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;modify&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFromString&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toResult&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Expect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;equal&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Err&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blankError&lt;/span&gt; &lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;it must contain @"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt;
                &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="kt"&gt;Contact&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;modify&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFromString&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ab.c"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toResult&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Expect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;equal&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Err&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;syntaxError&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ab.c"&lt;/span&gt; &lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;queryType"&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;it is required"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt;
                &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="kt"&gt;Contact&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;modify&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queryType&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFromString&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queryType&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toResult&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Expect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;equal&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Err&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blankError&lt;/span&gt; &lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message"&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;fuzz&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Fuzz&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;intRange&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;350&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;it is required and must be less than 300 characters long"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt;
                &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="k"&gt;let&lt;/span&gt;
                        &lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                            &lt;span class="kt"&gt;Contact&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;
                                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;modify&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFromString&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;repeat&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;m"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                    &lt;span class="k"&gt;in&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
                        &lt;span class="n"&gt;form&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toResult&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Expect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;equal&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Err&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blankError&lt;/span&gt; &lt;span class="p"&gt;])&lt;/span&gt;

                    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
                        &lt;span class="n"&gt;form&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toResult&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Expect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;equal&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Err&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customError&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;TooLong&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;])&lt;/span&gt;

                    &lt;span class="k"&gt;else&lt;/span&gt;
                        &lt;span class="n"&gt;form&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isValid&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Expect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;equal&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;consent"&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;it is required"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt;
                &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="kt"&gt;Contact&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;modify&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;consent&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFromString&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;consent&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toResult&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Expect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;equal&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Err&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blankError&lt;/span&gt; &lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;it cannot be false"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt;
                &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="kt"&gt;Contact&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;modify&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;consent&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFromValue&lt;/span&gt; &lt;span class="kt"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;consent&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toResult&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Expect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;equal&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Err&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validationError&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;false"&lt;/span&gt; &lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;it must be true"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt;
                &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="kt"&gt;Contact&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;modify&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;consent&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFromValue&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;consent&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isValid&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Expect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;equal&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Glue code
&lt;/h3&gt;

&lt;p&gt;The code that connects the UI to the form's logic lives in &lt;a href="https://github.com/dwayne/elm-fm-contact-form/blob/88dbde8ee6cf7fc8ff87c0881aa48b5330bfd9ea/app/src/elm/Main.elm" rel="noopener noreferrer"&gt;&lt;code&gt;app/src/elm/Main.elm&lt;/code&gt;&lt;/a&gt; and is implemented like any other Elm web application that uses TEA. There's no use of nested TEA. There's nothing special about it. For me, that's the point. The glue code loosely couples the UI and form logic and that's it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Astro as a frontend workshop environment
&lt;/h2&gt;

&lt;p&gt;I was never able to take a web design and directly implement it in a web application in one go. I always needed somewhere where I could experiment with ideas for how I wanted to write the HTML and CSS before I settled on a final solution. In this regard, I would always have a folder in my project where these experiements lived. For e.g. when I was building &lt;a href="https://github.com/dwayne/elm-2048" rel="noopener noreferrer"&gt;2048&lt;/a&gt; I had both an &lt;a href="https://github.com/dwayne/elm-2048/tree/5c2fddc4139b2e710b0ed7e1634b1e4b2277d9fe/experiments" rel="noopener noreferrer"&gt;experiments&lt;/a&gt; and a &lt;a href="https://github.com/dwayne/elm-2048/tree/5c2fddc4139b2e710b0ed7e1634b1e4b2277d9fe/prototype" rel="noopener noreferrer"&gt;prototype&lt;/a&gt; folder that served the purpose.&lt;/p&gt;

&lt;p&gt;I called my process &lt;a href="https://calculator.tutorial.elmwithdwayne.dev/prototype/index.html" rel="noopener noreferrer"&gt;prototyping&lt;/a&gt; and the idea behind the experiments and prototype folders for UI development has helped my work tremendously. More examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/dwayne/elm-7guis/tree/b5614514a81e0e0b854971fbb644179dbf451806/prototype" rel="noopener noreferrer"&gt;7GUIs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dwayne/elm-conduit/tree/69b1cf41d38e34c4bd8b61490d28e224a0481ac2/prototype" rel="noopener noreferrer"&gt;Conduit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dwayne/elm-calculator/tree/aefb50e58a227a95663ff1191bf7cae2726606ae/prototype" rel="noopener noreferrer"&gt;Calculator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dwayne/elm-wordle/tree/64dafa1d51891cc46b3843709924f1b2a5066d50/prototype" rel="noopener noreferrer"&gt;Wordle&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's only recently that I learned about Brad Frost's &lt;a href="https://bradfrost.com/blog/post/a-frontend-workshop-environment/" rel="noopener noreferrer"&gt;frontend workshop environment&lt;/a&gt; idea and how &lt;a href="https://storybook.js.org/" rel="noopener noreferrer"&gt;Storybook&lt;/a&gt; is a specific instance of that idea. These other ideas and tools have only reinforced my belief that there's enormous value in having this extra prototyping step in my process.&lt;/p&gt;

&lt;p&gt;Astro components are relatively easy to make and it doesn't enforce any particular workflow on you. Since I'm still experimenting with my workflow I value the freedom Astro gives me over Storybook. I can use Astro as a foundation to tailor the features of my frontend workshop environment to my needs and the needs of the particular project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project structure
&lt;/h2&gt;

&lt;p&gt;For this project I also experimented with a new project structure.&lt;/p&gt;

&lt;p&gt;I used two top-level directories called &lt;code&gt;app&lt;/code&gt; and &lt;code&gt;workshop&lt;/code&gt;. &lt;code&gt;app&lt;/code&gt; contained the main Elm web application and &lt;code&gt;workshop&lt;/code&gt; contained the frontend workshop environment.&lt;/p&gt;

&lt;p&gt;I worked within &lt;code&gt;workshop&lt;/code&gt; to build the UI and its artifacts (a CSS file and a font file) became inputs into &lt;code&gt;app&lt;/code&gt;. The connection can be seen from the &lt;a href="https://github.com/dwayne/elm-fm-contact-form/blob/88dbde8ee6cf7fc8ff87c0881aa48b5330bfd9ea/bin/app#L3-L18" rel="noopener noreferrer"&gt;&lt;code&gt;app prepare&lt;/code&gt;&lt;/a&gt; script.&lt;/p&gt;

&lt;p&gt;To build the Elm web application you build the workshop, copy the artifacts over to the app's public directory and then build the app. All of this is handled by &lt;code&gt;make&lt;/code&gt; (or equivalently &lt;a href="https://github.com/dwayne/elm-fm-contact-form/blob/88dbde8ee6cf7fc8ff87c0881aa48b5330bfd9ea/Makefile#L14" rel="noopener noreferrer"&gt;&lt;code&gt;make build&lt;/code&gt;&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Miscellaneous
&lt;/h2&gt;

&lt;p&gt;I used &lt;a href="https://www.nushell.sh/" rel="noopener noreferrer"&gt;Nushell&lt;/a&gt; for the &lt;a href="https://github.com/dwayne/elm-fm-contact-form/blob/88dbde8ee6cf7fc8ff87c0881aa48b5330bfd9ea/bin/app" rel="noopener noreferrer"&gt;&lt;code&gt;bin/app&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/dwayne/elm-fm-contact-form/blob/88dbde8ee6cf7fc8ff87c0881aa48b5330bfd9ea/bin/workshop" rel="noopener noreferrer"&gt;&lt;code&gt;bin/workshop&lt;/code&gt;&lt;/a&gt; scripts. I have to use it some more to say whether or not it's worth it. I did like how easy it was to write a script with subcommands, document the subcommands, and wrap pre-exisiting scripts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=TgQZz2kGysk" rel="noopener noreferrer"&gt;How to Create Custom CLIs for Internal Developer Platforms with Nushell&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I packaged &lt;a href="https://github.com/dwayne/deploy/blob/e6b015c6ce926275db378e6370429f60e649fc9d/deploy.sh" rel="noopener noreferrer"&gt;my deployment script&lt;/a&gt; with &lt;a href="https://nixos.org/" rel="noopener noreferrer"&gt;Nix&lt;/a&gt; and &lt;a href="https://zero-to-nix.com/concepts/flakes/" rel="noopener noreferrer"&gt;Nix flakes&lt;/a&gt; then added it as a dependency in my &lt;a href="https://github.com/dwayne/elm-fm-contact-form/blob/88dbde8ee6cf7fc8ff87c0881aa48b5330bfd9ea/devbox.json#L3" rel="noopener noreferrer"&gt;&lt;code&gt;devbox.json&lt;/code&gt;&lt;/a&gt;. When you enter the developer environment you have access to the &lt;code&gt;deploy&lt;/code&gt; Bash script which I then wrapped up into &lt;a href="https://github.com/dwayne/elm-fm-contact-form/blob/88dbde8ee6cf7fc8ff87c0881aa48b5330bfd9ea/bin/app#L46-L48" rel="noopener noreferrer"&gt;&lt;code&gt;app deploy&lt;/code&gt;&lt;/a&gt;. Previously, I would copy and paste all the Bash scripts I needed from past projects into my current project but this approach was much nicer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subscribe to my newsletter
&lt;/h2&gt;

&lt;p&gt;If you're interested in improving your skills with Elm then I invite you to subscribe to my newsletter, &lt;a href="https://buttondown.com/elmwithdwayne" rel="noopener noreferrer"&gt;Elm with Dwayne&lt;/a&gt;. To learn more about it, please read this &lt;a href="https://dev.to/dwayne/announcing-my-newsletter-elm-with-dwayne-hoi"&gt;announcement&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>elm</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Announcing elm-field</title>
      <dc:creator>Dwayne Crooks</dc:creator>
      <pubDate>Mon, 18 Aug 2025 12:36:14 +0000</pubDate>
      <link>https://dev.to/dwayne/announcing-elm-field-opb</link>
      <guid>https://dev.to/dwayne/announcing-elm-field-opb</guid>
      <description>&lt;p&gt;I'm happy to announce the release of &lt;a href="https://github.com/dwayne/elm-field/tree/1.0.0" rel="noopener noreferrer"&gt;&lt;code&gt;dwayne/elm-field&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dwayne/elm-field&lt;/code&gt; is an Elm package for constructing valid data from unreliable string inputs. It provides a &lt;a href="https://package.elm-lang.org/packages/dwayne/elm-field/1.0.0/Field#Field" rel="noopener noreferrer"&gt;&lt;code&gt;Field&lt;/code&gt;&lt;/a&gt; data structure that models the non-UI related aspects of an input field. I extracted this package from my work on &lt;a href="https://dwayne.github.io/elm-l-system-studio/" rel="noopener noreferrer"&gt;L-System Studio&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Think about a &lt;code&gt;Field&lt;/code&gt; in a similar way as you'd think about &lt;code&gt;Maybe&lt;/code&gt;, &lt;code&gt;Result&lt;/code&gt;, and &lt;a href="https://package.elm-lang.org/packages/dwayne/elm-validation/latest/Validation" rel="noopener noreferrer"&gt;&lt;code&gt;Validation&lt;/code&gt;&lt;/a&gt;. Suppose you're parsing a &lt;code&gt;String&lt;/code&gt; into a value of some type, say &lt;code&gt;a&lt;/code&gt;. There's the potential for one or more errors to occur. If you just want to know that an error occurred but you don't care about the specific error that occurred you'd use &lt;code&gt;Maybe&lt;/code&gt;. If you do care to track the specific errors then you'd switch to &lt;code&gt;Result&lt;/code&gt;. Furthermore, if you care to accumulate multiple specific errors you'd change to a &lt;code&gt;Validation&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;Field&lt;/code&gt; is like a &lt;code&gt;Validation&lt;/code&gt; in that it allows you to accumulate multiple specific errors. But, it also does a little bit more.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It remembers the original raw &lt;code&gt;String&lt;/code&gt; you were trying to parse.&lt;/li&gt;
&lt;li&gt;It remembers whether or not it's the first time you're changing the &lt;code&gt;String&lt;/code&gt; to be parsed, i.e. whether or not your field is clean or dirty.&lt;/li&gt;
&lt;li&gt;It keeps track of the function used to parse the &lt;code&gt;String&lt;/code&gt;. And,&lt;/li&gt;
&lt;li&gt;It keeps track of the function used to convert the value back to a &lt;code&gt;String&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The last two constitute the &lt;a href="https://package.elm-lang.org/packages/dwayne/elm-field/1.0.0/Field#Type" rel="noopener noreferrer"&gt;&lt;code&gt;Type&lt;/code&gt;&lt;/a&gt; of a &lt;code&gt;Field&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The package provides two modules: &lt;a href="https://package.elm-lang.org/packages/dwayne/elm-field/1.0.0/Field" rel="noopener noreferrer"&gt;&lt;code&gt;Field&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://package.elm-lang.org/packages/dwayne/elm-field/1.0.0/Field-Advanced" rel="noopener noreferrer"&gt;&lt;code&gt;Field.Advanced&lt;/code&gt;&lt;/a&gt;. The difference between them is that &lt;code&gt;Field.Advanced&lt;/code&gt; allows you to use a custom type to track your errors whereas &lt;code&gt;Field&lt;/code&gt; uses strings for errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  An example
&lt;/h2&gt;

&lt;p&gt;Suppose you need to accept input from a user for a positive integer, i.e. an integer greater than or equal to 1. Here's what that might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;
&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;positiveInt&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Input&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;
&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Input&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFromString&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
&lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type_&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text"&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toRawString&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;HE&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;onInput&lt;/span&gt; &lt;span class="kt"&gt;Input&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In general, you can easily create a field out of any of Elm's primitive types: &lt;code&gt;Int&lt;/code&gt;, &lt;code&gt;Float&lt;/code&gt;, &lt;code&gt;Bool&lt;/code&gt;, &lt;code&gt;Char&lt;/code&gt;, and &lt;code&gt;String&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For many more examples you can read the &lt;a href="https://github.com/dwayne/elm-field/blob/1.0.0/README.md" rel="noopener noreferrer"&gt;README&lt;/a&gt;, play with the &lt;a href="https://github.com/dwayne/elm-field/blob/1.0.0/examples/README.md" rel="noopener noreferrer"&gt;demos&lt;/a&gt;, and examine the &lt;a href="https://github.com/dwayne/elm-field/tree/1.0.0/tests/Test" rel="noopener noreferrer"&gt;unit tests&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  No UI
&lt;/h2&gt;

&lt;p&gt;I want to stress that a &lt;code&gt;Field&lt;/code&gt; data structure models the non-UI related aspects of an input field. This means you can implement the business logic for a form and reuse that logic to render your form in infinitely many ways. Take the &lt;a href="https://github.com/dwayne/elm-field/blob/1.0.0/examples/src/Dillon/SignUp.elm" rel="noopener noreferrer"&gt;sign up form&lt;/a&gt; example. The &lt;a href="https://dwayne.github.io/elm-field/dillon/simple-sign-up-view/" rel="noopener noreferrer"&gt;simple view&lt;/a&gt; and the &lt;a href="https://dwayne.github.io/elm-field/dillon/complex-sign-up-view/" rel="noopener noreferrer"&gt;complex view&lt;/a&gt; are different renderings of the same underlying form. You have complete control over the UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Richard's comment
&lt;/h2&gt;

&lt;p&gt;If you resonate with &lt;a href="https://discourse.elm-lang.org/t/what-is-the-elm-way-to-validate-form-fields/9689/9?u=dwayne" rel="noopener noreferrer"&gt;Richard's comment&lt;/a&gt; then my package attempts to provide a better tool for working with forms as programs. Give it a try and let me know what you think.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subscribe to my newsletter
&lt;/h2&gt;

&lt;p&gt;If you're interested in improving your skills with Elm then I invite you to subscribe to my newsletter, &lt;a href="https://buttondown.com/elmwithdwayne" rel="noopener noreferrer"&gt;Elm with Dwayne&lt;/a&gt;. To learn more about it, please read this &lt;a href="https://dev.to/dwayne/announcing-my-newsletter-elm-with-dwayne-hoi"&gt;announcement&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>elm</category>
      <category>form</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Diary of an Elm Developer - From fields to forms</title>
      <dc:creator>Dwayne Crooks</dc:creator>
      <pubDate>Thu, 10 Jul 2025 12:21:33 +0000</pubDate>
      <link>https://dev.to/dwayne/diary-of-an-elm-developer-from-fields-to-forms-5aab</link>
      <guid>https://dev.to/dwayne/diary-of-an-elm-developer-from-fields-to-forms-5aab</guid>
      <description>&lt;p&gt;When &lt;a href="https://discourse.elm-lang.org/t/introducing-dillonkearns-elm-form-a-new-approach-to-forms-in-elm/9085" rel="noopener noreferrer"&gt;Dillon introduced his form library&lt;/a&gt; he shared &lt;a href="https://ellie-app.com/myVVqSVC2QZa1" rel="noopener noreferrer"&gt;an Ellie demo containing a sign up form and a check in form&lt;/a&gt; in order to illustrate the capabilities of his library. The forms featured validation on blur, validations that depend on multiple form fields and the ability to reuse your form definitions.&lt;/p&gt;

&lt;p&gt;I tried to recreate his demo starting from my &lt;code&gt;Field&lt;/code&gt; type and discovered several beautiful (to me at least) abstractions along the way, &lt;code&gt;Form&lt;/code&gt; and &lt;code&gt;InteractionTracker&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's start by implementing the sign up form.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sign Up form
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Initialize
&lt;/h3&gt;

&lt;p&gt;The sign up form consists of four form fields: username, password, password confirmation, and role.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Password&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmation&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;PasswordConfirmation&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Role&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The username field is initialized to the string &lt;code&gt;"dillon"&lt;/code&gt;, the role field is initializied to the role &lt;code&gt;SuperAdmin&lt;/code&gt; and the password fields are left empty.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt;
&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromString&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fieldType&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dillon"&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="kt"&gt;Password&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fieldType&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="kt"&gt;PasswordConfirmation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fieldType&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromValue&lt;/span&gt; &lt;span class="kt"&gt;Role&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fieldType&lt;/span&gt; &lt;span class="kt"&gt;Role&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;SuperAdmin&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update
&lt;/h3&gt;

&lt;p&gt;We need to control how the fields are set so that we can enforce the rules of the form. We do it by providing functions to set the fields.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Setters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;setUsername&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setPassword&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setPasswordConfirmation&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setRole&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Role&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;setters&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Setters&lt;/span&gt;
&lt;span class="n"&gt;setters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;setUsername&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFromString&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setPassword&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt;
                &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFromString&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;
            &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;updatePasswordConfirmation&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;passwordConfirmation&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setPasswordConfirmation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt;
                &lt;span class="n"&gt;passwordConfirmation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFromString&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;passwordConfirmation&lt;/span&gt;
            &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;updatePasswordConfirmation&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmation&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setRole&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFromValue&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;updatePasswordConfirmation&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Password&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;PasswordConfirmation&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;PasswordConfirmation&lt;/span&gt;
&lt;span class="n"&gt;updatePasswordConfirmation&lt;/span&gt; &lt;span class="n"&gt;passwordField&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmationField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmation&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kt"&gt;Password&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toString&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;PasswordConfirmation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toString&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmation&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
            &lt;span class="n"&gt;passwordConfirmationField&lt;/span&gt;

        &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setError&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The password confirmation does not match."&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmationField&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;passwordField&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmationField&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;withDefault&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmationField&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All the fields validate their own values like before, see &lt;a href="https://dev.to/dwayne/diary-of-an-elm-developer-exploring-an-api-for-form-fields-146e"&gt;Diary of an Elm Developer - Exploring an API for form fields&lt;/a&gt;, but notice how the password confirmation matching validation is handled. When you set the password or the password confirmation we check to see whether or not they match. If they don't match we set a validation error.&lt;/p&gt;

&lt;p&gt;In this example, custom field errors are of type &lt;code&gt;String&lt;/code&gt; but you have the freedom to use any error type you desire. For e.g. in another example not shown here I used &lt;code&gt;F.setError PasswordConfirmation.Mismatch passwordConfirmationField&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Validate
&lt;/h3&gt;

&lt;p&gt;Finally, we need to say how to validate the form and parse the user's input into an output data structure of our choosing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UsernameError&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;PasswordError&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;PasswordConfirmationError&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;RoleError&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Password&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Role&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Validation&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="kt"&gt;Output&lt;/span&gt;
&lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="kt"&gt;Output&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapError&lt;/span&gt; &lt;span class="kt"&gt;UsernameError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapError&lt;/span&gt; &lt;span class="kt"&gt;PasswordError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;passwordConfirmation&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapError&lt;/span&gt; &lt;span class="kt"&gt;PasswordConfirmationError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapError&lt;/span&gt; &lt;span class="kt"&gt;RoleError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Putting it all together
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="kt"&gt;Dillon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;SignUp&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Output&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Setters&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;SignUp&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;SignUp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Form&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt; &lt;span class="kt"&gt;Setters&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="kt"&gt;Output&lt;/span&gt;

&lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;SignUp&lt;/span&gt;
&lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;setters&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's take a step back to understand what we've achieved. All the requirements of the sign up form has been encapsulated into a reusable module, &lt;code&gt;Dillon.SignUp&lt;/code&gt;, such that there is no way for a consumer of our form module to mess up the requirements of the form. At the same time anyone using the module is free to render the form and provide whatever UX they deem necessary.&lt;/p&gt;

&lt;p&gt;If you don't believe me then let me show you by building a simple view and then extending it to work just like Dillon's sign up form demo.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple view of the Sign Up form
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Model
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;signUp&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;SignUp&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maybeOutput&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="kt"&gt;SignUp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Output&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;
&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;signUp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SignUp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maybeOutput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Nothing&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InputUsername&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;InputPassword&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;InputPasswordConfirmation&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;InputRole&lt;/span&gt; &lt;span class="kt"&gt;Role&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Submit&lt;/span&gt;

&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;
&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;InputUsername&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;signUp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setUsername&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signUp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kt"&gt;InputPassword&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;signUp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setPassword&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signUp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kt"&gt;InputPasswordConfirmation&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;signUp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setPasswordConfirmation&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signUp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kt"&gt;InputRole&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;signUp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setRole&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signUp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kt"&gt;Submit&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;maybeOutput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validateAsMaybe&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signUp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  View
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
&lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;signUp&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maybeOutput&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt;
        &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toFields&lt;/span&gt; &lt;span class="n"&gt;signUp&lt;/span&gt;
    &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;h2&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sign Up"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;novalidate&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;HE&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;onSubmit&lt;/span&gt; &lt;span class="kt"&gt;Submit&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;viewInput&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;username"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Username"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errorToString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errorToString&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isRequired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isDisabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;False&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;autofocus&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InputUsername&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;viewInput&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Password"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errorToString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Password&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errorToString&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isRequired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isDisabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;False&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InputPassword&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;viewInput&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;passwordConfirmation"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Password Confirmation"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;passwordConfirmation&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errorToString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;PasswordConfirmation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errorToString&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isRequired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isDisabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;False&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InputPasswordConfirmation&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;viewSelect&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;role"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Role"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toMaybe&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
                        &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                            &lt;span class="kt"&gt;Selection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;select&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="n"&gt;defaultRoleOptions&lt;/span&gt;

                        &lt;span class="kt"&gt;Nothing&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                            &lt;span class="n"&gt;defaultRoleOptions&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toOption&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;roleToString&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errorToString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Role&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errorToString&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isRequired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isDisabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;False&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InputRole&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
                &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;
                    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isInvalid&lt;/span&gt; &lt;span class="n"&gt;signUp&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sign Up"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;maybeOutput&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
            &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
                    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;h2&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Output"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Username: "&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toString&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Password: "&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="kt"&gt;Password&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toString&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Role: "&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="n"&gt;roleToString&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="p"&gt;]&lt;/span&gt;

            &lt;span class="kt"&gt;Nothing&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;defaultRoleOptions&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Selection&lt;/span&gt; &lt;span class="kt"&gt;Role&lt;/span&gt;
&lt;span class="n"&gt;defaultRoleOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Selection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromList&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="kt"&gt;Role&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Admin&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;Role&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;SuperAdmin&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Role&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Regular&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;roleToString&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Role&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
&lt;span class="n"&gt;roleToString&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Role&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Regular&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Regular"&lt;/span&gt;

        &lt;span class="kt"&gt;Role&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Admin&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Admin"&lt;/span&gt;

        &lt;span class="kt"&gt;Role&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;SuperAdmin&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Super Admin"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. The form abstraction fits quite nicely into TEA such that the view code you write remains very similar to what you would have written without the abstractions.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/gvIOBdVJNLg"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;But, that's a simple view. What about adding blur on validation? Well, let's see.&lt;/p&gt;

&lt;h2&gt;
  
  
  A complex view of the Sign Up form
&lt;/h2&gt;

&lt;p&gt;Blur on validation is a UI concern so the changes SHOULD show up in the UI part of the code. If they show up anywhere else then something is wrong with the abstraction.&lt;/p&gt;

&lt;h3&gt;
  
  
  Model
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;signUp&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;SignUp&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usernameTracker&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwordTracker&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmationTracker&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Timer&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isSubmitting&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maybeOutput&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="kt"&gt;SignUp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Output&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;signUp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SignUp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;
      &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usernameTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
      &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwordTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
      &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmationTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
      &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
      &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isSubmitting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;False&lt;/span&gt;
      &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maybeOutput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Nothing&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;timerConfig&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Config&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
&lt;span class="n"&gt;timerConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;wait&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onExpire&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ExpiredTimer&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ChangedTimer&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I added five new record fields to the model.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;usernameTracker&lt;/code&gt;, &lt;code&gt;passwordTracker&lt;/code&gt;, and &lt;code&gt;passwordConfirmationTracker&lt;/code&gt; keeps track of the UI state (Not visited, Focused, Changed, and Blurred) of the corresponding form fields.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;timer&lt;/code&gt; and &lt;code&gt;isSubmitting&lt;/code&gt; are used to simulate submitting a form to a backend that takes some time, about 5 seconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update
&lt;/h3&gt;

&lt;p&gt;We have a few new messages to handle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
    &lt;span class="c1"&gt;-- The old messages remain the same&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InputUsername&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;InputPassword&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;InputPasswordConfirmation&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;InputRole&lt;/span&gt; &lt;span class="kt"&gt;Role&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Submit&lt;/span&gt;
    &lt;span class="c1"&gt;-- The new messages&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;ChangedUsernameTracker&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;ChangedPasswordTracker&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;ChangedPasswordConfirmationTracker&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;ExpiredTimer&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;ChangedTimer&lt;/span&gt; &lt;span class="kt"&gt;Timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Msg&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Except for &lt;code&gt;Submit&lt;/code&gt; the old message implementations in &lt;code&gt;update&lt;/code&gt; remain the same.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="c1"&gt;-- The old messages remain the same except for Submit&lt;/span&gt;
        &lt;span class="c1"&gt;-- ...&lt;/span&gt;

        &lt;span class="kt"&gt;Submit&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="kt"&gt;Timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setTimeout&lt;/span&gt; &lt;span class="n"&gt;timerConfig&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timer&lt;/span&gt;
            &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isSubmitting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;-- The new messages&lt;/span&gt;

        &lt;span class="kt"&gt;ChangedUsernameTracker&lt;/span&gt; &lt;span class="n"&gt;subMsg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;usernameTracker&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="n"&gt;subMsg&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;usernameTracker&lt;/span&gt;
            &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;usernameTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;usernameTracker&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;ChangedPasswordTracker&lt;/span&gt; &lt;span class="n"&gt;subMsg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;passwordTracker&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="n"&gt;subMsg&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;passwordTracker&lt;/span&gt;
            &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;passwordTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;passwordTracker&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;ChangedPasswordConfirmationTracker&lt;/span&gt; &lt;span class="n"&gt;subMsg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmationTracker&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="n"&gt;subMsg&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;passwordConfirmationTracker&lt;/span&gt;
            &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmationTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmationTracker&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;ExpiredTimer&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;
                &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;signUp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SignUp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usernameTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwordTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmationTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isSubmitting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;False&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maybeOutput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validateAsMaybe&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signUp&lt;/span&gt;
              &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;ChangedTimer&lt;/span&gt; &lt;span class="n"&gt;subMsg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="n"&gt;timerConfig&lt;/span&gt; &lt;span class="n"&gt;subMsg&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timer&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  View
&lt;/h3&gt;

&lt;p&gt;We have replaced &lt;code&gt;viewInput&lt;/code&gt; with &lt;code&gt;viewInteractiveInput&lt;/code&gt; which takes two extra fields: &lt;code&gt;tracker&lt;/code&gt; and &lt;code&gt;onChange&lt;/code&gt;. Everything else remains the same.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
&lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;signUp&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usernameTracker&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwordTracker&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmationTracker&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isSubmitting&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maybeOutput&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt;
        &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toFields&lt;/span&gt; &lt;span class="n"&gt;signUp&lt;/span&gt;
    &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;h2&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sign Up"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;novalidate&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;HE&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;onSubmit&lt;/span&gt; &lt;span class="kt"&gt;Submit&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;viewInteractiveInput&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;username"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Username"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;usernameTracker&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errorToString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errorToString&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isRequired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isDisabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isSubmitting&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;autofocus&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InputUsername&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ChangedUsernameTracker&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;viewInteractiveInput&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Password"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;passwordTracker&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errorToString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Password&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errorToString&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isRequired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isDisabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isSubmitting&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InputPassword&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ChangedPasswordTracker&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;viewInteractiveInput&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;passwordConfirmation"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Password Confirmation"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;passwordConfirmation&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmationTracker&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errorToString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;PasswordConfirmation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errorToString&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isRequired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isDisabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isSubmitting&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InputPasswordConfirmation&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ChangedPasswordConfirmationTracker&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;viewSelect&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;role"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Role"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toMaybe&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
                        &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                            &lt;span class="kt"&gt;Selection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;select&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="n"&gt;defaultRoleOptions&lt;/span&gt;

                        &lt;span class="kt"&gt;Nothing&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                            &lt;span class="n"&gt;defaultRoleOptions&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toOption&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;roleToString&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errorToString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Role&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errorToString&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isRequired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isDisabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isSubmitting&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InputRole&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
                &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;
                    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;disabled&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isInvalid&lt;/span&gt; &lt;span class="n"&gt;signUp&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;isSubmitting&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt;
                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;isSubmitting&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
                            &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Signing Up..."&lt;/span&gt;

                        &lt;span class="k"&gt;else&lt;/span&gt;
                            &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sign Up"&lt;/span&gt;
                    &lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;maybeOutput&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
            &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
                    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;h2&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Output"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Username: "&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toString&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Password: "&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="kt"&gt;Password&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toString&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Role: "&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="n"&gt;roleToString&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="p"&gt;]&lt;/span&gt;

            &lt;span class="kt"&gt;Nothing&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  A few points to note
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We didn't have to touch &lt;code&gt;Dillon.SignUp&lt;/code&gt; at all. It's truly reusable.&lt;/li&gt;
&lt;li&gt;The UI is an orthogonal concern and changes to the UI led to corrresponding changes in the view code as we had hoped.&lt;/li&gt;
&lt;li&gt;A moderate increase in complexity in the UI led to a moderate increase in complexity in the view code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These things seem significant and promising from my point of view since they were sources of contention anytime I had to write Elm forms in the past.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/_TJ17STME44"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Let's move on to the check in form.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check In form
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Initialize
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Date&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkInTime&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Time&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkOut&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Date&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subscribe&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt;
&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromString&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nonBlankString&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dillon"&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="kt"&gt;Date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fieldType&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkInTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="kt"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fieldType&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkOut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="kt"&gt;Date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fieldType&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subscribe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromValue&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I use the &lt;code&gt;String&lt;/code&gt; type for name but I validate it as a non-blank string such that the empty string and strings of whitespace are not valid values for the field. However, I created custom types for &lt;code&gt;Date&lt;/code&gt; and &lt;code&gt;Time&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Setters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;setName&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setCheckIn&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;today&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Date&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setCheckInTime&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setCheckOut&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setSubscribe&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how setting the check in date depends on today's date and it's explicit in the type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;setters&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Setters&lt;/span&gt;
&lt;span class="n"&gt;setters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;setName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFromString&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setCheckIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;today&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="c1"&gt;--&lt;/span&gt;
            &lt;span class="c1"&gt;-- N.B. You can pass in whatever data you need from the outside via a tuple or record.&lt;/span&gt;
            &lt;span class="c1"&gt;--&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt;
                &lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFromString&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkIn&lt;/span&gt;
            &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toMaybe&lt;/span&gt; &lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
                &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isAfter&lt;/span&gt; &lt;span class="n"&gt;today&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
                        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

                    &lt;span class="k"&gt;else&lt;/span&gt;
                        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setError&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You must check in after today."&lt;/span&gt; &lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="kt"&gt;Nothing&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setCheckInTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt;
                &lt;span class="n"&gt;checkInTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFromString&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkInTime&lt;/span&gt;
            &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toMaybe&lt;/span&gt; &lt;span class="n"&gt;checkInTime&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
                &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isBetween&lt;/span&gt; &lt;span class="kt"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nineAm&lt;/span&gt; &lt;span class="kt"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fivePm&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
                        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;checkInTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;checkInTime&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

                    &lt;span class="k"&gt;else&lt;/span&gt;
                        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;checkInTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setError&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You must check in between the hours of 9am to 5pm."&lt;/span&gt; &lt;span class="n"&gt;checkInTime&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="kt"&gt;Nothing&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;checkInTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;checkInTime&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setCheckOut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt;
                &lt;span class="n"&gt;checkOut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFromString&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkOut&lt;/span&gt;

                &lt;span class="n"&gt;maybeCheckOutIsAfterCheckIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="kt"&gt;Date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isAfter&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkIn&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;checkOut&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;andMaybe&lt;/span&gt;
            &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;maybeCheckOutIsAfterCheckIn&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
                &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="n"&gt;checkOutIsAfterCheckIn&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;checkOutIsAfterCheckIn&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
                        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;checkOut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;checkOut&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

                    &lt;span class="k"&gt;else&lt;/span&gt;
                        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;checkOut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setError&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You must check out after you've checked in."&lt;/span&gt; &lt;span class="n"&gt;checkOut&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="kt"&gt;Nothing&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;checkOut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;checkOut&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setSubscribe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;subscribe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFromValue&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribe&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take note of how the validation dependencies are handled.&lt;/p&gt;

&lt;h3&gt;
  
  
  Validate
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;NameError&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;CheckInError&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;CheckInTimeError&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;CheckOutError&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;SubscribeError&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stay&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Stay&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isSubscribed&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Stay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Date&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Time&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nights&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Validation&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="kt"&gt;Output&lt;/span&gt;
&lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="n"&gt;checkInTime&lt;/span&gt; &lt;span class="n"&gt;checkOut&lt;/span&gt; &lt;span class="n"&gt;subscribe&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="kt"&gt;Output&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Stay&lt;/span&gt; &lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="n"&gt;checkInTime&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nights&lt;/span&gt; &lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="n"&gt;checkOut&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;subscribe&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapError&lt;/span&gt; &lt;span class="kt"&gt;NameError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapError&lt;/span&gt; &lt;span class="kt"&gt;CheckInError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkInTime&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapError&lt;/span&gt; &lt;span class="kt"&gt;CheckInTimeError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkOut&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapError&lt;/span&gt; &lt;span class="kt"&gt;CheckOutError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribe&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapError&lt;/span&gt; &lt;span class="kt"&gt;SubscribeError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Putting it all together
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="kt"&gt;Dillon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;CheckIn&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;CheckIn&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Output&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Setters&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;CheckIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Form&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt; &lt;span class="kt"&gt;Setters&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="kt"&gt;Output&lt;/span&gt;

&lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CheckIn&lt;/span&gt;
&lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;setters&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A view of the Check In form
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Model
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;today&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Date&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CheckIn&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nameTracker&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkInTracker&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkInTimeTracker&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkOutTracker&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subscribeTracker&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Timer&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isSubmitting&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maybeOutput&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="kt"&gt;CheckIn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Output&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;today&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jul1st2025&lt;/span&gt;
      &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;CheckIn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;
      &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nameTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
      &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkInTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
      &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkInTimeTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
      &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkOutTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
      &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subscribeTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
      &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
      &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isSubmitting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;False&lt;/span&gt;
      &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maybeOutput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Nothing&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;today&lt;/span&gt; &lt;span class="kt"&gt;GotDate&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;timerConfig&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Config&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
&lt;span class="n"&gt;timerConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;wait&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onExpire&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ExpiredTimer&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ChangedTimer&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;GotDate&lt;/span&gt; &lt;span class="kt"&gt;Date&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;InputName&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;ChangedNameTracker&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;InputCheckIn&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;ChangedCheckInTracker&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;InputCheckInTime&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;ChangedCheckInTimeTracker&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;InputCheckOut&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;ChangedCheckOutTracker&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;ToggleSubscribe&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;ChangedSubscribeTracker&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Submit&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;ExpiredTimer&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;ChangedTimer&lt;/span&gt; &lt;span class="kt"&gt;Timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Msg&lt;/span&gt;


&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;GotDate&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;today&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;InputName&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setName&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;ChangedNameTracker&lt;/span&gt; &lt;span class="n"&gt;subMsg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;nameTracker&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="n"&gt;subMsg&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nameTracker&lt;/span&gt;
            &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;nameTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nameTracker&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;InputCheckIn&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setCheckIn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;today&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;today&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;ChangedCheckInTracker&lt;/span&gt; &lt;span class="n"&gt;subMsg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;checkInTracker&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="n"&gt;subMsg&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkInTracker&lt;/span&gt;
            &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;checkInTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;checkInTracker&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;InputCheckInTime&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setCheckInTime&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;ChangedCheckInTimeTracker&lt;/span&gt; &lt;span class="n"&gt;subMsg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;checkInTimeTracker&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="n"&gt;subMsg&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkInTimeTracker&lt;/span&gt;
            &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;checkInTimeTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;checkInTimeTracker&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;InputCheckOut&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setCheckOut&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;ChangedCheckOutTracker&lt;/span&gt; &lt;span class="n"&gt;subMsg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;checkOutTracker&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="n"&gt;subMsg&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkOutTracker&lt;/span&gt;
            &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;checkOutTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;checkOutTracker&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;ToggleSubscribe&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;subscribe&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toFields&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkIn&lt;/span&gt;

                &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="n"&gt;subscribe&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toMaybe&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;not&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;withDefault&lt;/span&gt; &lt;span class="kt"&gt;False&lt;/span&gt;
            &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setSubscribe&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;ChangedSubscribeTracker&lt;/span&gt; &lt;span class="n"&gt;subMsg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;subscribeTracker&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="n"&gt;subMsg&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribeTracker&lt;/span&gt;
            &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;subscribeTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subscribeTracker&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;Submit&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="kt"&gt;Timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setTimeout&lt;/span&gt; &lt;span class="n"&gt;timerConfig&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timer&lt;/span&gt;
            &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isSubmitting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;ExpiredTimer&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;
                &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;CheckIn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nameTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkInTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkInTimeTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkOutTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isSubmitting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;False&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maybeOutput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validateAsMaybe&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkIn&lt;/span&gt;
              &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;ChangedTimer&lt;/span&gt; &lt;span class="n"&gt;subMsg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="n"&gt;timerConfig&lt;/span&gt; &lt;span class="n"&gt;subMsg&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timer&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  View
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
&lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;today&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkIn&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nameTracker&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkInTracker&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkInTimeTracker&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkOutTracker&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subscribeTracker&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isSubmitting&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maybeOutput&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt;
        &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toFields&lt;/span&gt; &lt;span class="n"&gt;checkIn&lt;/span&gt;
    &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;h2&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Check In"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;novalidate&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;HE&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;onSubmit&lt;/span&gt; &lt;span class="kt"&gt;Submit&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;viewInteractiveInput&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Name"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nameTracker&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errorToString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;CheckIn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nameErrorToString&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isRequired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isDisabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isSubmitting&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;autofocus&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InputName&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ChangedNameTracker&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;viewInteractiveInput&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checkIn"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Check In"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;date"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkIn&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;checkInTracker&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errorToString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errorToString&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;check in"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isRequired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isDisabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isSubmitting&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="k"&gt;let&lt;/span&gt;
                        &lt;span class="n"&gt;maybeMax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                            &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkOut&lt;/span&gt;
                                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toMaybe&lt;/span&gt;
                                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toString&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="kt"&gt;Date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="k"&gt;in&lt;/span&gt;
                    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toString&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="kt"&gt;Date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;today&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;]&lt;/span&gt;
                        &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;maybeMax&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
                                &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                                    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;

                                &lt;span class="kt"&gt;Nothing&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                                    &lt;span class="p"&gt;[]&lt;/span&gt;
                           &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InputCheckIn&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ChangedCheckInTracker&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;viewInteractiveInput&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checkInTime"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Check In Time"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;time"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkInTime&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;checkInTimeTracker&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errorToString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errorToString&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;check in"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isRequired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isDisabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isSubmitting&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InputCheckInTime&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ChangedCheckInTimeTracker&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;viewInteractiveInput&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checkOut"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Check Out"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;date"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkOut&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;checkOutTracker&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errorToString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errorToString&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;check out"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isRequired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isDisabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isSubmitting&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkIn&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toMaybe&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;withDefault&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;today&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toString&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;
                    &lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InputCheckOut&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ChangedCheckOutTracker&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;viewCheckbox&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;subscribe"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sign Up for Email Updates"&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribe&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subscribeTracker&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isRequired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isDisabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isSubmitting&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onToggle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ToggleSubscribe&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ChangedSubscribeTracker&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
                &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;
                    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;disabled&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isInvalid&lt;/span&gt; &lt;span class="n"&gt;checkIn&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;isSubmitting&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt;
                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;isSubmitting&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
                            &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Checking In..."&lt;/span&gt;

                        &lt;span class="k"&gt;else&lt;/span&gt;
                            &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Check In"&lt;/span&gt;
                    &lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;maybeOutput&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
            &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stay&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isSubscribed&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
                    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;h2&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Output"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Name: "&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Nights: "&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromInt&lt;/span&gt; &lt;span class="n"&gt;stay&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nights&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email Updates: "&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="n"&gt;boolToString&lt;/span&gt; &lt;span class="n"&gt;isSubscribed&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="p"&gt;]&lt;/span&gt;

            &lt;span class="kt"&gt;Nothing&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how the &lt;code&gt;min&lt;/code&gt; and &lt;code&gt;max&lt;/code&gt; attributes of the &lt;code&gt;date&lt;/code&gt; input fields are set. Everything else is pretty much similar to how the complex sign up view was implemented.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/y9Y-u2qvcaw"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: &lt;code&gt;InteractionTracker&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;I didn't want to worry about which form control you needed to track. I figured the logic was independent of all that. After several refactoring sessions I realized I just needed a way to hijack the event handlers you placed on your controls so that I could hook into them and do my tracking.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="kt"&gt;Lib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;InteractionTracker&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Messages&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toMessages&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toStatus&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Lib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Task&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Task&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  State
&lt;/h3&gt;

&lt;p&gt;The internal state I need to know about over time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Status&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Status&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;NotVisited&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Focused&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Changed&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Blurred&lt;/span&gt;


&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt;
&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;State&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;NotVisited&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;toStatus&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Status&lt;/span&gt;
&lt;span class="n"&gt;toStatus&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Focus&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Input&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Blur&lt;/span&gt;


&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Focus&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;Blurred&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;else&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Focused&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;Input&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;Blurred&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
                &lt;span class="kt"&gt;State&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;

              &lt;span class="k"&gt;else&lt;/span&gt;
                &lt;span class="kt"&gt;State&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Changed&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dispatch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;onInput&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;Blur&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Blurred&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Messages
&lt;/h3&gt;

&lt;p&gt;I will give you the messages you can use on your controls if you want to track the user's interactions with your widget. I can't know ahead of time which HTML elements they'd need to be placed on, that's for you to decide.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Messages&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;focus&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blur&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;toMessages&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Messages&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;
&lt;span class="n"&gt;toMessages&lt;/span&gt; &lt;span class="n"&gt;onChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt;
        &lt;span class="n"&gt;onInput&lt;/span&gt; &lt;span class="n"&gt;toMsg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="n"&gt;onChange&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="kt"&gt;Input&lt;/span&gt; &lt;span class="n"&gt;toMsg&lt;/span&gt;
    &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;focus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;onChange&lt;/span&gt; &lt;span class="kt"&gt;Focus&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;onChange&lt;/span&gt; &lt;span class="kt"&gt;Blur&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So how do we reuse this?&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter &lt;code&gt;InteractiveInput&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;InteractiveInput&lt;/code&gt; is a reusable view for tracking user interactions. It doesn't care what control you're tracking.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="kt"&gt;Lib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;InteractiveInput&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;InputOptions&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;ViewOptions&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Attributes&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Lib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;InteractionTracker&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;ViewOptions&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errorToString&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;debug&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toInput&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;InputOptions&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onChange&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;InputOptions&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;focus&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blur&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ViewOptions&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;
&lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errorToString&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toInput&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onChange&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt;
        &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toStatus&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt;

        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;focus&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blur&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toMessages&lt;/span&gt; &lt;span class="n"&gt;onChange&lt;/span&gt;
    &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;for&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;: "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; "&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toInput&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;focus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;focus&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;blur&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;debug&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
                    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; "&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;span&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="n"&gt;statusToString&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="p"&gt;]&lt;/span&gt;

                &lt;span class="k"&gt;else&lt;/span&gt;
                    &lt;span class="p"&gt;[]&lt;/span&gt;
               &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Blurred&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
                    &lt;span class="n"&gt;field&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;allErrors&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;
                            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                                &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;li&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;style&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;color"&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;red"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="n"&gt;errorToString&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
                            &lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ul&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

                 &lt;span class="k"&gt;else&lt;/span&gt;
                    &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
               &lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="n"&gt;statusToString&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
&lt;span class="n"&gt;statusToString&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;NotVisited&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Not visited"&lt;/span&gt;

        &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Focused&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Focused"&lt;/span&gt;

        &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Changed&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Changed"&lt;/span&gt;

        &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Blurred&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Blurred"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;viewInteractiveInput&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;viewInteractiveInput&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type_&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errorToString&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isRequired&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isDisabled&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Attribute&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onChange&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;InteractionTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;
&lt;span class="n"&gt;viewInteractiveInput&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type_&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errorToString&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isRequired&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isDisabled&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onChange&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;InteractiveInput&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errorToString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;errorToString&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;debug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;False&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="n"&gt;toFieldInput&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;isRequired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isRequired&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isDisabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isDisabled&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type_&lt;/span&gt; &lt;span class="n"&gt;type_&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;onChange&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;toFieldInput&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;isRequired&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isDisabled&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Attribute&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;InteractiveInput&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;InputOptions&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;
&lt;span class="n"&gt;toFieldInput&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;isRequired&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isDisabled&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;focus&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blur&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isRequired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isRequired&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isDisabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isDisabled&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="n"&gt;attrs&lt;/span&gt;
                &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;
                   &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;HE&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;onFocus&lt;/span&gt; &lt;span class="n"&gt;focus&lt;/span&gt;
                   &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;HE&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;onBlur&lt;/span&gt; &lt;span class="n"&gt;blur&lt;/span&gt;
                   &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;After completing this exercise I'm getting more confident that my field abstraction which builds on &lt;a href="https://package.elm-lang.org/packages/dwayne/elm-validation/latest/" rel="noopener noreferrer"&gt;&lt;code&gt;dwayne/elm-validation&lt;/code&gt;&lt;/a&gt; and a few other ideas is a good starting point for a form abstraction. It seems to be allowing me to achieve the separation of concerns that I want in a form library. I want to be able to write down the requirements of my forms once and for all and then I want to be able to view that form however I please with any UX I desire.&lt;/p&gt;

&lt;p&gt;I didn't even get to talk about the unit testing story. Suffice it to say that's it's easy to test your forms and you don't have to use &lt;a href="https://package.elm-lang.org/packages/avh4/elm-program-test/latest/" rel="noopener noreferrer"&gt;&lt;code&gt;avh4/elm-program-test&lt;/code&gt;&lt;/a&gt; to test your form logic. See this &lt;a href="https://discourse.elm-lang.org/t/introducing-dillonkearns-elm-form-a-new-approach-to-forms-in-elm/9085/3?u=dwayne" rel="noopener noreferrer"&gt;comment&lt;/a&gt; to understand what I'm referring to.&lt;/p&gt;

&lt;p&gt;I plan to find more examples to rebuild with my abstractions in order to see where, if at all, my abstractions fall apart. It will be a while before I release anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Request for feedback
&lt;/h2&gt;

&lt;p&gt;In the meantime, I'd be happy to receive stories or examples of forms you found quite challenging to build with the current form abstractions that the Elm ecosystem provides.&lt;/p&gt;

&lt;p&gt;What do you think is difficult about forms that I may have overlooked?&lt;/p&gt;

&lt;h2&gt;
  
  
  Subscribe to my newsletter
&lt;/h2&gt;

&lt;p&gt;If you're interested in improving your skills with Elm then I invite you to subscribe to my newsletter, &lt;a href="https://buttondown.com/elmwithdwayne" rel="noopener noreferrer"&gt;Elm with Dwayne&lt;/a&gt;. To learn more about it, please read this &lt;a href="https://dev.to/dwayne/announcing-my-newsletter-elm-with-dwayne-hoi"&gt;announcement&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>elm</category>
      <category>forms</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Diary of an Elm Developer - Exploring debounced input</title>
      <dc:creator>Dwayne Crooks</dc:creator>
      <pubDate>Thu, 03 Jul 2025 12:14:12 +0000</pubDate>
      <link>https://dev.to/dwayne/diary-of-an-elm-developer-exploring-debounced-input-1ll</link>
      <guid>https://dev.to/dwayne/diary-of-an-elm-developer-exploring-debounced-input-1ll</guid>
      <description>&lt;p&gt;Debouncing in general is about taking control of when a given action is performed.&lt;/p&gt;

&lt;p&gt;If a user is entering their username and you want to check if their username is available via an API call then you don't want to make that check everytime they type a letter. You want to give them ample time to enter their username before making the check. Debouncing allows you to control when you make that check.&lt;/p&gt;

&lt;p&gt;There are many more use cases for debouncing and a related idea called throttling which you can explore further in &lt;a href="https://dwayne.github.io/elm-debouncer/" rel="noopener noreferrer"&gt;elm-debouncer Examples&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Today I'm going to share the work I'm doing to integrate debouncing (&lt;a href="https://github.com/dwayne/elm-debouncer" rel="noopener noreferrer"&gt;dwayne/elm-debouncer&lt;/a&gt;) with form fields (not yet released).&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;Lib.DebouncedField&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The idea that came naturally to me was to bundle the field and the debouncer and create a new type of field called a &lt;code&gt;DebouncedField&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;DebouncedField&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;DebouncedField&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ready&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;debouncer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Debouncer&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I encountered several problems doing it this way.&lt;/p&gt;

&lt;p&gt;Firstly, a debounced field isn't really a field. It contains a field but it's not a field. To operate on the field I had to provide specific functions for working on the field. After seeing how many functions I needed in order to reclaim unfettered access to the field I caved and decided I needed the following API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;fromField&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;DebouncedField&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;changeField&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;DebouncedField&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;DebouncedField&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;toField&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;DebouncedField&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that API, &lt;code&gt;DebouncedField&lt;/code&gt; has access to the field to do what it needs and you have access to do what you need. A win-win. However, the nagging issue became which field is going to be the source of truth. I decided to let it slide for the time being.&lt;/p&gt;

&lt;p&gt;But I couldn't overlook the following change to my signup form.&lt;/p&gt;

&lt;p&gt;Before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Email&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Password&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmation&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;PasswordConfirmation&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;DebouncedField&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Email&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Password&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmation&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;PasswordConfirmation&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;N.B.&lt;/strong&gt; &lt;em&gt;The error type &lt;code&gt;e&lt;/code&gt; has been specialized to &lt;code&gt;String&lt;/code&gt; and elided by using a type alias in these examples.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;What that meant was that every function in my sign up form module that touched &lt;code&gt;username&lt;/code&gt; had to be changed.&lt;/p&gt;

&lt;p&gt;You may be wondering why that's such a huge problem. So let me explain.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://dev.to/dwayne/diary-of-an-elm-developer-exploring-an-api-for-form-fields-146e#what-lies-ahead"&gt;sign up form module&lt;/a&gt; without &lt;code&gt;DebouncedField&lt;/code&gt; captures all the requirements of the sign up form regardless of how you eventually choose to display it. That's fantastic because it means we can write form modules that capture the data requirements of all our forms and reuse them over several applications and render them differently in each one. So currently the form logic is independent of the way the form looks and independent of how you will be able to interact with it.&lt;/p&gt;

&lt;p&gt;When I made &lt;code&gt;username&lt;/code&gt; a &lt;code&gt;DebouncedField&lt;/code&gt; all that went out the window because I was committing to the &lt;code&gt;username&lt;/code&gt; field being debounced when rendered. At the same time I was also committing to &lt;code&gt;email&lt;/code&gt;, &lt;code&gt;password&lt;/code&gt;, and &lt;code&gt;passwordConfirmation&lt;/code&gt; not being debounced now and in the future.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A good architecture makes the system easy to change, in all ways that it must change, by leaving options open.&lt;/p&gt;

&lt;p&gt;A good architect will want to separate the UI portions of a use case from the business rule portions in such a way that they can be changed independently of each other, while keeping those use cases visible and clear.&lt;/p&gt;

&lt;p&gt;The business rules should be the most independent and reusable code in the system.&lt;/p&gt;

&lt;p&gt;Depend on things that have few to no reasons to change.&lt;/p&gt;

&lt;p&gt;Clean Architecture: A Craftsman's Guide to Software Structure and Design&lt;br&gt;
by Robert C. Martin&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What I've come to realize in hindsight is that deboucing is a UI concern. My sign up form module captures the business rules of the application. As a result, my sign up form module should NEVER depend on whether or not you decide you want to debounce the &lt;code&gt;username&lt;/code&gt; field.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter &lt;code&gt;Lib.DebouncedInput&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;What I want instead is a reusable view for rendering a field whose input I want to debounce. My sign up form remains unchanged and reusable. Great! And, I get to decide when I'm building the UI if I want to add debouncing to any of the fields.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;DebouncedInput&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;DebouncedInput&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;debouncer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Debouncer&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ready&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's how I use it in a deboucing example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Status&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usernameDebouncedInput&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;DebouncedInput&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Timer&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Normal&lt;/span&gt;
      &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fieldType&lt;/span&gt;
      &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usernameDebouncedInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;DebouncedInput&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
      &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;InputUsername&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;
                &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Normal&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFromString&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
                        &lt;span class="kt"&gt;Checking&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                            &lt;span class="kt"&gt;Timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancel&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timer&lt;/span&gt;

                        &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                            &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timer&lt;/span&gt;
              &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;ReadyUsername&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt;
                &lt;span class="n"&gt;usernameField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFromString&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;

                &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="kt"&gt;Timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setTimeout&lt;/span&gt; &lt;span class="n"&gt;timerConfig&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timer&lt;/span&gt;

                &lt;span class="n"&gt;newModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;usernameField&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toMaybe&lt;/span&gt; &lt;span class="n"&gt;usernameField&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
                &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;newModel&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Checking&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="kt"&gt;Nothing&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="n"&gt;newModel&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;FocusUsername&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;ChangedUsernameDebouncedInput&lt;/span&gt; &lt;span class="n"&gt;subMsg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;usernameDebouncedInput&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="kt"&gt;DebouncedInput&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="n"&gt;usernameDebouncedInputConfig&lt;/span&gt; &lt;span class="n"&gt;subMsg&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;usernameDebouncedInput&lt;/span&gt;
            &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;usernameDebouncedInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;usernameDebouncedInput&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;TimerExpired&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;generate&lt;/span&gt; &lt;span class="kt"&gt;GotResult&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt;
                &lt;span class="kt"&gt;Random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weighted&lt;/span&gt;
                    &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;False&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;ChangedTimer&lt;/span&gt; &lt;span class="n"&gt;timerMsg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="n"&gt;timerConfig&lt;/span&gt; &lt;span class="n"&gt;timerMsg&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timer&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;GotResult&lt;/span&gt; &lt;span class="n"&gt;isFree&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
                &lt;span class="kt"&gt;Checking&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;
                        &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;isFree&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
                                &lt;span class="kt"&gt;Success&lt;/span&gt;

                            &lt;span class="k"&gt;else&lt;/span&gt;
                                &lt;span class="kt"&gt;Error&lt;/span&gt;
                      &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;focus&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;username"&lt;/span&gt; &lt;span class="kt"&gt;FocusUsername&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
&lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;style&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;margin"&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;10px"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;for&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;username"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Username: "&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;DebouncedInput&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;debouncedInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;usernameDebouncedInput&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isRequired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isDisabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
                    &lt;span class="kt"&gt;Checking&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                        &lt;span class="kt"&gt;True&lt;/span&gt;

                    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                        &lt;span class="kt"&gt;False&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;usernameDebouncedInputConfig&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;autofocus&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;username"&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
                &lt;span class="kt"&gt;Normal&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

                &lt;span class="kt"&gt;Checking&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;em&lt;/span&gt;
                        &lt;span class="p"&gt;[]&lt;/span&gt;
                        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Checking if &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toString&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; is free..."&lt;/span&gt;
                        &lt;span class="p"&gt;]&lt;/span&gt;

                &lt;span class="kt"&gt;Success&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;span&lt;/span&gt;
                        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;style&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;color"&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;green"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
                        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The username is available."&lt;/span&gt;
                        &lt;span class="p"&gt;]&lt;/span&gt;

                &lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;span&lt;/span&gt;
                        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;style&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;color"&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;red"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
                        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The username is already taken."&lt;/span&gt;
                        &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;usernameDebouncedInput&lt;/code&gt; tracks the state required for debouncing. It is decoupled from the field, explicit in the code, and doesn't need to be there if later on we decide we want to remove debouncing.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/TGvrUqxlVb4"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The outcome of this exploration is that debouncing is a UI concern and as a result it will not be present in my field library. I will probably have to write a separate library for a reusable debounced input view.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/Clean-Architecture-Craftsmans-Software-Structure/dp/0134494164" rel="noopener noreferrer"&gt;Clean Architecture: A Craftsman's Guide to Software Structure and Design&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Subscribe to my newsletter
&lt;/h2&gt;

&lt;p&gt;If you're interested in improving your skills with Elm then I invite you to subscribe to my newsletter, &lt;a href="https://buttondown.com/elmwithdwayne" rel="noopener noreferrer"&gt;Elm with Dwayne&lt;/a&gt;. To learn more about it, please read this &lt;a href="https://dev.to/dwayne/announcing-my-newsletter-elm-with-dwayne-hoi"&gt;announcement&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>elm</category>
      <category>forms</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Diary of an Elm Developer - Exploring an API for form fields</title>
      <dc:creator>Dwayne Crooks</dc:creator>
      <pubDate>Tue, 24 Jun 2025 13:27:13 +0000</pubDate>
      <link>https://dev.to/dwayne/diary-of-an-elm-developer-exploring-an-api-for-form-fields-146e</link>
      <guid>https://dev.to/dwayne/diary-of-an-elm-developer-exploring-an-api-for-form-fields-146e</guid>
      <description>&lt;p&gt;My implementation of &lt;a href="https://dwayne.github.io/elm-l-system-studio/" rel="noopener noreferrer"&gt;L-System Studio&lt;/a&gt; required me to use a lot of form fields in interesting ways.&lt;/p&gt;

&lt;p&gt;It has &lt;a href="https://github.com/dwayne/elm-l-system-studio/blob/b4249084fe90d255ed6e3d8e62f03d697c2f3d54/src/Main.elm#L75-L87" rel="noopener noreferrer"&gt;input fields&lt;/a&gt; that take non-empty strings, non-negative integers, bounded integers, non-negative floats, bounded floats and angles in degrees.&lt;/p&gt;

&lt;p&gt;The ability to zoom in depends on the value of zoom increment and window size. For e.g. if the zoom increment is &lt;code&gt;100&lt;/code&gt; but the window size is &lt;code&gt;50&lt;/code&gt; then the user shouldn't be allowed to zoom in. The user is only allowed to zoom in &lt;a href="https://github.com/dwayne/elm-l-system-studio/blob/b4249084fe90d255ed6e3d8e62f03d697c2f3d54/src/Main.elm#L156-L173" rel="noopener noreferrer"&gt;if the window size is at least twice the value of the zoom increment&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I found that my field abstraction allowed me to implement these requirements quite simply and because of this I've decided to explore these ideas even further.&lt;/p&gt;

&lt;h2&gt;
  
  
  More examples of usage
&lt;/h2&gt;

&lt;p&gt;These are the fields I keep in the model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;preset&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Preset&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;axiom&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;iterations&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;startHeading&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Angle&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lineLength&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Float&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lineLengthScaleFactor&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Float&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;turningAngle&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Angle&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;windowPositionX&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Float&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;windowPositionY&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Float&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;windowSize&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Float&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fps&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ipf&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;panIncrement&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Float&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;zoomIncrement&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Float&lt;/span&gt;
    &lt;span class="c1"&gt;-- ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is how I initialize the fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;preset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromValue&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;preset&lt;/span&gt; &lt;span class="n"&gt;isInitial&lt;/span&gt; &lt;span class="n"&gt;preset&lt;/span&gt;
&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;axiom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromString&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nonEmptyString&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;axiom&lt;/span&gt;
&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;iterations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromValue&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nonNegativeInt&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iterations&lt;/span&gt;
&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;startHeading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromValue&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startHeading&lt;/span&gt;
&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lineLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromValue&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nonNegativeFloat&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lineLength&lt;/span&gt;
&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lineLengthScaleFactor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromValue&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nonNegativeFloat&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lineLengthScaleFactor&lt;/span&gt;
&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;turningAngle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromValue&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;turningAngle&lt;/span&gt;
&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;windowPositionX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromValue&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;windowPosition&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;windowPositionY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromValue&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;windowPosition&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;
&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;windowSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromValue&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nonNegativeFloat&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;windowSize&lt;/span&gt;
&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromValue&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fps&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fps&lt;/span&gt;
&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ipf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromValue&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ipf&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ipf&lt;/span&gt;
&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;panIncrement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromValue&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;panIncrement&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;zoomIncrement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromValue&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zoomIncrement&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="c1"&gt;-- ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is how I validate the fields before rendering:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt;
        &lt;span class="n"&gt;oldSettings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;

        &lt;span class="n"&gt;resultNewSettings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;axiom&lt;/span&gt; &lt;span class="n"&gt;iterations&lt;/span&gt; &lt;span class="n"&gt;startHeading&lt;/span&gt; &lt;span class="n"&gt;lineLength&lt;/span&gt; &lt;span class="n"&gt;lineLengthScaleFactor&lt;/span&gt; &lt;span class="n"&gt;turningAngle&lt;/span&gt; &lt;span class="n"&gt;windowPositionX&lt;/span&gt; &lt;span class="n"&gt;windowPositionY&lt;/span&gt; &lt;span class="n"&gt;windowSize&lt;/span&gt; &lt;span class="n"&gt;fps&lt;/span&gt; &lt;span class="n"&gt;ipf&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;oldSettings&lt;/span&gt;
                    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Rules&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toValue&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rules&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;axiom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;axiom&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;iterations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;iterations&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;startHeading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;startHeading&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lineLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lineLength&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lineLengthScaleFactor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lineLengthScaleFactor&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;turningAngle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;turningAngle&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;windowPosition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;windowPositionX&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;windowPositionY&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;windowSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;windowSize&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fps&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ipf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ipf&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;axiom&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iterations&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startHeading&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lineLength&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lineLengthScaleFactor&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;turningAngle&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;windowPositionX&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;windowPositionY&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;windowSize&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fps&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ipf&lt;/span&gt;
    &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;resultNewSettings&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Ok&lt;/span&gt; &lt;span class="n"&gt;newSettings&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newSettings&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;renderer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;initRenderer&lt;/span&gt; &lt;span class="n"&gt;newSettings&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clear&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;Err&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;
            &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is how I pan to the left:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="kt"&gt;ClickedLeft&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;apply2&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;windowPositionX&lt;/span&gt; &lt;span class="n"&gt;panIncrement&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;windowPositionX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromValue&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float&lt;/span&gt; &lt;span class="kt"&gt;False&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;windowPositionX&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;panIncrement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;windowPositionX&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;panIncrement&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;withDefault&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is how I zoom out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="kt"&gt;ClickedOut&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt;
        &lt;span class="n"&gt;resultWindow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;apply4&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="n"&gt;inc&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="k"&gt;let&lt;/span&gt;
                        &lt;span class="n"&gt;inc2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                            &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;inc&lt;/span&gt;
                    &lt;span class="k"&gt;in&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;inc&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;inc&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;inc2&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;windowPositionX&lt;/span&gt;
                &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;windowPositionY&lt;/span&gt;
                &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;windowSize&lt;/span&gt;
                &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zoomIncrement&lt;/span&gt;
    &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;resultWindow&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Ok&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;render&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;
                    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;windowPositionX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromValue&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float&lt;/span&gt; &lt;span class="kt"&gt;False&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;windowPositionY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromValue&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float&lt;/span&gt; &lt;span class="kt"&gt;False&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
                    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;windowSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromValue&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nonNegativeFloat&lt;/span&gt; &lt;span class="kt"&gt;False&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kt"&gt;Err&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is how I decide whether or not to disable the zoom in button:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type_&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button"&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;isValidZoomIncrement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;isZoomingIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;windowSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;windowSize&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;zoomIncrement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zoomIncrement&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="kt"&gt;HE&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;onClick&lt;/span&gt; &lt;span class="kt"&gt;ClickedIn&lt;/span&gt;

      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="kt"&gt;HA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;disabled&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;H&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;In"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Fields keep track of the raw string that the user entered.&lt;/li&gt;
&lt;li&gt;When you first create the field it's considered clean but if you make changes then it's considered dirty.&lt;/li&gt;
&lt;li&gt;Fields keep track of the processed value.&lt;/li&gt;
&lt;li&gt;The processed value could be anything you want. In the examples above it was &lt;code&gt;Int&lt;/code&gt;, &lt;code&gt;Float&lt;/code&gt;, &lt;code&gt;String&lt;/code&gt;, &lt;code&gt;Angle&lt;/code&gt;, and &lt;code&gt;Preset&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The processed value is stored in a &lt;code&gt;Result&lt;/code&gt; so that we can track errors.&lt;/li&gt;
&lt;li&gt;There's a concept of a field type, &lt;code&gt;Type&lt;/code&gt;. A field type can be thought of as an interface of functions that concrete types, like &lt;code&gt;Int&lt;/code&gt;, &lt;code&gt;String&lt;/code&gt;, or &lt;code&gt;Angle&lt;/code&gt;, implement in order to be used as fields. If you're familiar with Haskell, you might make &lt;code&gt;Type&lt;/code&gt; a type class. If you're familiar with Rust, you might make &lt;code&gt;Type&lt;/code&gt; a trait.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The main types used to achieve the features above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;State&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Raw&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;processed&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Result&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Raw&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Initial&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Dirty&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Required&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;ParseError&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;ValidationError&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Type&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;toString&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toValue&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Result&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Result&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's &lt;a href="https://github.com/dwayne/elm-l-system-studio/blob/b4249084fe90d255ed6e3d8e62f03d697c2f3d54/src/Lib/Field.elm" rel="noopener noreferrer"&gt;the full implementation of &lt;code&gt;Field&lt;/code&gt;&lt;/a&gt; as I envisioned it at the time for L-System Studio.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I made &lt;code&gt;Angle&lt;/code&gt; into a field
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/dwayne/elm-l-system-studio/blob/b4249084fe90d255ed6e3d8e62f03d697c2f3d54/src/Data/Angle.elm" rel="noopener noreferrer"&gt;&lt;code&gt;Data.Angle&lt;/code&gt;&lt;/a&gt; was implemented with no regards for fields. But, when I was ready to accept angle input all I had to do was implement &lt;code&gt;toString&lt;/code&gt;, &lt;code&gt;toValue&lt;/code&gt;, and &lt;code&gt;validate&lt;/code&gt; from the &lt;code&gt;Type&lt;/code&gt; interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Type&lt;/span&gt; &lt;span class="kt"&gt;Angle&lt;/span&gt;
&lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;toString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Angle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toDegrees&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromFloat&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trim&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;andThen&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toFloat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Ok&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="kt"&gt;Angle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromDegrees&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;withDefault&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validationError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Ok&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's &lt;a href="https://github.com/dwayne/elm-l-system-studio/blob/b4249084fe90d255ed6e3d8e62f03d697c2f3d54/src/Data/Field.elm" rel="noopener noreferrer"&gt;the implementation of all the custom fields not directly provided by the &lt;code&gt;Field&lt;/code&gt; module&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What lies ahead?
&lt;/h2&gt;

&lt;p&gt;I'm currently still exploring the space and working on more diverse examples in order to figure out a good API for everything I'm going to need but the work I'm doing is looking really promising.&lt;/p&gt;

&lt;p&gt;Most recently I finished a sign up form example. Here's what the code looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="kt"&gt;Example1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;SignUp&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;SignUp&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Submission&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errorToString&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setEmail&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setPassword&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setPasswordConfirmation&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setUsername&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;submit&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toFields&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Example1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Email&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Email&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Example1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Password&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Password&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Example1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;SignUp&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SignUp&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Email&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Password&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmation&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Password&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;SignUp&lt;/span&gt;
&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;SignUp&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fieldType&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="kt"&gt;Email&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fieldType&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="kt"&gt;Password&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fieldType&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="kt"&gt;Password&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fieldType&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;setUsername&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;SignUp&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;SignUp&lt;/span&gt;
&lt;span class="n"&gt;setUsername&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;SignUp&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;SignUp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFromString&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;setEmail&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;SignUp&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;SignUp&lt;/span&gt;
&lt;span class="n"&gt;setEmail&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;SignUp&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;SignUp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFromString&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;setPassword&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;SignUp&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;SignUp&lt;/span&gt;
&lt;span class="n"&gt;setPassword&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;SignUp&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt;
        &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFromString&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;
    &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="kt"&gt;SignUp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;updatePasswordConfirmation&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;passwordConfirmation&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;setPasswordConfirmation&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;SignUp&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;SignUp&lt;/span&gt;
&lt;span class="n"&gt;setPasswordConfirmation&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;SignUp&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt;
        &lt;span class="n"&gt;passwordConfirmation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFromString&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;passwordConfirmation&lt;/span&gt;
    &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="kt"&gt;SignUp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;updatePasswordConfirmation&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmation&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;updatePasswordConfirmation&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Password&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Password&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Password&lt;/span&gt;
&lt;span class="n"&gt;updatePasswordConfirmation&lt;/span&gt; &lt;span class="n"&gt;passwordField&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmationField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmation&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmation&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
            &lt;span class="n"&gt;passwordConfirmationField&lt;/span&gt;

        &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fail&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customError&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The password confirmation does not match."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmationField&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;passwordField&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmationField&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;withDefault&lt;/span&gt; &lt;span class="n"&gt;passwordConfirmationField&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UsernameError&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;EmailError&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;PasswordError&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;PasswordConfirmationError&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Submission&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Email&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Password&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;submit&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;SignUp&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Result&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;Submission&lt;/span&gt;
&lt;span class="n"&gt;submit&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;SignUp&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="kt"&gt;Submission&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapError&lt;/span&gt; &lt;span class="kt"&gt;UsernameError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapError&lt;/span&gt; &lt;span class="kt"&gt;EmailError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapError&lt;/span&gt; &lt;span class="kt"&gt;PasswordError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;passwordConfirmation&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapError&lt;/span&gt; &lt;span class="kt"&gt;PasswordConfirmationError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;andResult&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapError&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;errorToString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;errorToString&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
&lt;span class="n"&gt;errorToString&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;UsernameError&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="kt"&gt;Username&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errorToString&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;

        &lt;span class="kt"&gt;EmailError&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="kt"&gt;Email&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errorToString&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;

        &lt;span class="kt"&gt;PasswordError&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="kt"&gt;Password&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errorToString&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password"&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;

        &lt;span class="kt"&gt;PasswordConfirmationError&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="kt"&gt;Password&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errorToString&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password confirmation"&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;


&lt;span class="n"&gt;toFields&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;SignUp&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Fields&lt;/span&gt;
&lt;span class="n"&gt;toFields&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;SignUp&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;fields&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's what it looks like to use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Example1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;SignUp&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;

&lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setEmail&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ab.c"&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;submit&lt;/span&gt;
&lt;span class="c1"&gt;-- Err ["The username is required.","The email is not valid.","The password is required.","The password confirmation is required."]&lt;/span&gt;

&lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setEmail&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a@b.c"&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;submit&lt;/span&gt;
&lt;span class="c1"&gt;-- Err ["The username is required.","The password is required.","The password confirmation is required."]&lt;/span&gt;

&lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setEmail&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a@b.c"&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setUsername&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dw"&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;submit&lt;/span&gt;
&lt;span class="c1"&gt;-- Err ["The username must have at least 3 characters.","The password is required.","The password confirmation is required."]&lt;/span&gt;

&lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setEmail&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a@b.c"&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setUsername&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dwayne aaaaaaaaaaaaaaaaaaaaaaaaa"&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;submit&lt;/span&gt;
&lt;span class="c1"&gt;-- Err ["The username must have at most 25 characters.","The password is required.","The password confirmation is required."]&lt;/span&gt;

&lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setEmail&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a@b.c"&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setUsername&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dwayne"&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;submit&lt;/span&gt;
&lt;span class="c1"&gt;-- Err ["The password is required.","The password confirmation is required."]&lt;/span&gt;

&lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setEmail&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a@b.c"&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setUsername&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dwayne"&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setPassword&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1234"&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;submit&lt;/span&gt;
&lt;span class="c1"&gt;-- Err ["The password must have at least 8 characters.","The password confirmation is required."]&lt;/span&gt;

&lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setEmail&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a@b.c"&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setUsername&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dwayne"&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setPassword&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;12345678"&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;submit&lt;/span&gt;
&lt;span class="c1"&gt;-- Err ["The password must contain at least 1 of each of the following: a lowercase character, an uppercase character, a number, and a special character in the set \"(!@#$%^&amp;amp;*)\".","The password confirmation is required."]&lt;/span&gt;

&lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setEmail&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a@b.c"&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setUsername&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dwayne"&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setPassword&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;12345678aB$"&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;submit&lt;/span&gt;
&lt;span class="c1"&gt;-- Err ["The password confirmation is required."]&lt;/span&gt;

&lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setEmail&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a@b.c"&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setUsername&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dwayne"&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setPassword&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;12345678aB$"&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setPasswordConfirmation&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;12345678aB$%"&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;submit&lt;/span&gt;
&lt;span class="c1"&gt;-- Err["The password confirmation does not match."]&lt;/span&gt;

&lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setEmail&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a@b.c"&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setUsername&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dwayne"&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setPassword&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;12345678aB$"&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setPasswordConfirmation&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;12345678aB$"&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;submit&lt;/span&gt;
&lt;span class="c1"&gt;-- Ok { email = Email "a@b.c", password = Password "12345678aB$", username = Username "dwayne" }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It has a nice unit testing story.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Field&lt;/code&gt; and &lt;code&gt;Field.Advanced&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;I've also decided there's going to be two modules, &lt;code&gt;Field&lt;/code&gt; and &lt;code&gt;Field.Advanced&lt;/code&gt;. The main difference being that &lt;code&gt;Field&lt;/code&gt; would use &lt;code&gt;String&lt;/code&gt; for custom errors but with &lt;code&gt;Field.Advanced&lt;/code&gt; you're able to use your custom error types to delay converting to a string representation for as long as you need.&lt;/p&gt;

&lt;h3&gt;
  
  
  Debounced input and async fields
&lt;/h3&gt;

&lt;p&gt;Nothing to talk about here as yet. I'll be looking into this soon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Request for feedback
&lt;/h2&gt;

&lt;p&gt;How has your experience been with forms in Elm? Do you have examples of where current Elm libraries fall short? What would you like to see in a form library for Elm?&lt;/p&gt;

&lt;h2&gt;
  
  
  Subscribe to my newsletter
&lt;/h2&gt;

&lt;p&gt;If you're interested in improving your skills with Elm then I invite you to subscribe to my newsletter, &lt;a href="https://buttondown.com/elmwithdwayne" rel="noopener noreferrer"&gt;Elm with Dwayne&lt;/a&gt;. To learn more about it, please read this &lt;a href="https://dev.to/dwayne/announcing-my-newsletter-elm-with-dwayne-hoi"&gt;announcement&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>elm</category>
    </item>
    <item>
      <title>Diary of an Elm Developer - Improving Rules.lookup</title>
      <dc:creator>Dwayne Crooks</dc:creator>
      <pubDate>Mon, 19 May 2025 11:33:36 +0000</pubDate>
      <link>https://dev.to/dwayne/diary-of-an-elm-developer-improving-ruleslookup-dni</link>
      <guid>https://dev.to/dwayne/diary-of-an-elm-developer-improving-ruleslookup-dni</guid>
      <description>&lt;p&gt;In the previous post, I &lt;a href="https://dev.to/dwayne/diary-of-an-elm-developer-lazy-l-system-generation-2k7j#-raw-rules-endraw-"&gt;mentioned&lt;/a&gt; that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The only issue I have with &lt;code&gt;lookup&lt;/code&gt; is that for missing keys it creates a new singleton sequence each time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://jfmengels.net/" rel="noopener noreferrer"&gt;Jeroen&lt;/a&gt; also pointed out, and rightly so, that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It's even worse than that: you &lt;strong&gt;always&lt;/strong&gt; create this singleton, because &lt;code&gt;Maybe.withDefault&lt;/code&gt; is eager. The fix is simple for this part: use a case expression instead.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;lookup&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Rules&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt;
&lt;span class="n"&gt;lookup&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Rules&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="kt"&gt;Dict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="n"&gt;replacement&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;replacement&lt;/span&gt;

        &lt;span class="kt"&gt;Nothing&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="kt"&gt;Sequence&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;singleton&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But now we're back to square one and I still don't have a fix for my original issue with &lt;code&gt;lookup&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are my options?
&lt;/h2&gt;

&lt;p&gt;I considered two options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Have &lt;code&gt;lookup&lt;/code&gt; return a &lt;code&gt;Maybe (Sequence Char)&lt;/code&gt; and deal with it downstream.&lt;/li&gt;
&lt;li&gt;Have &lt;code&gt;lookup&lt;/code&gt; return &lt;code&gt;(Sequence Char, Rules)&lt;/code&gt; so that we can update the rules with the missing replacement such that the next time we'd find it in rules and return it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But there were problems with both approaches.&lt;/p&gt;

&lt;p&gt;With the first approach, it just puts off the inevitable, i.e. we'd still be recreating the singleton every time just somewhere else downstream.&lt;/p&gt;

&lt;p&gt;With the second approach, we'd have to keep track of the changes to rules within &lt;code&gt;Sequence.concatMap&lt;/code&gt;. It could kinda work but I didn't like the API changes that needed to occur for it to happen so I abandoned this idea.&lt;/p&gt;

&lt;p&gt;After taking a break and a nice long walk another option revealed itself. Why not determine all the characters that would be looked up ahead of time? L-system generation uses both the rules and axiom. Every character that appears in the final generated sequence comes from one of these values.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter &lt;code&gt;Rules.build&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Rules.build&lt;/code&gt; replaces &lt;code&gt;Rules.fromList&lt;/code&gt;. It takes both the rules and axiom and builds a dictionary that contains a key for every possible character that could occur in the final generated sequence.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Dict&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Set&lt;/span&gt;


&lt;span class="n"&gt;build&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Rules&lt;/span&gt;
&lt;span class="n"&gt;build&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt; &lt;span class="n"&gt;axiom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt;
        &lt;span class="n"&gt;identity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="n"&gt;buildIdentity&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt; &lt;span class="n"&gt;axiom&lt;/span&gt;
    &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="n"&gt;rules&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Tuple&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapSecond&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Dict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromList&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Dict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;union&lt;/span&gt; &lt;span class="n"&gt;identity&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Rules&lt;/span&gt;


&lt;span class="n"&gt;buildIdentity&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Dict&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;buildIdentity&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt; &lt;span class="n"&gt;axiom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt;
        &lt;span class="n"&gt;axiomSet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="n"&gt;axiom&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toList&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromList&lt;/span&gt;

        &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;keySet&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valueSet&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="n"&gt;rules&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foldl&lt;/span&gt;
                    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;replacement&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;prevKeySet&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prevValueSet&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                        &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="n"&gt;prevKeySet&lt;/span&gt;
                        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;replacement&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toList&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromList&lt;/span&gt;
                            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;union&lt;/span&gt; &lt;span class="n"&gt;prevValueSet&lt;/span&gt;
                        &lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;identityKeySet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;union&lt;/span&gt; &lt;span class="n"&gt;axiomSet&lt;/span&gt; &lt;span class="n"&gt;valueSet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;keySet&lt;/span&gt;
    &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="n"&gt;identityKeySet&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foldl&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="kt"&gt;Dict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Sequence&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;singleton&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="kt"&gt;Dict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;buildIdentity&lt;/code&gt; is where the main action happens. It builds a dictionary of characters mapped to their singleton sequences. It's easier to understand how it works via an example.&lt;/p&gt;

&lt;h3&gt;
  
  
  An example
&lt;/h3&gt;

&lt;p&gt;Input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;rules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="sc"&gt;'F'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;F+F-F-FF+F+F-F"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;axiom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;F+F+F+F"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;axiomSet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromList&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="sc"&gt;'F'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'+'&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;keySet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromList&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="sc"&gt;'F'&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;valueSet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromList&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="sc"&gt;'F'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'+'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'-'&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since &lt;code&gt;identityKeySet = Set.diff (Set.union axiomSet valueSet) keySet&lt;/code&gt; we have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;identityKeySet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromList&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="sc"&gt;'+'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'-'&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Therefore, the dictionary that &lt;code&gt;buildIdentity&lt;/code&gt; returns is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="kt"&gt;Dict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromList&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="sc"&gt;'+'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;singleton&lt;/span&gt; &lt;span class="sc"&gt;'+'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="sc"&gt;'-'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;singleton&lt;/span&gt; &lt;span class="sc"&gt;'-'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This dictionary is united with the dictionary we build from the rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="kt"&gt;Dict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromList&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="sc"&gt;'F'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromString&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;F+F-F-FF+F+F-F"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to produce the final dictionary. This allows us to change our implementation of &lt;code&gt;lookup&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The new &lt;code&gt;lookup&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;lookup&lt;/code&gt; doesn't need to create singleton sequences anymore because of how the &lt;code&gt;Rules&lt;/code&gt; data structure is built and subsequently used. So it changes to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;lookup&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Rules&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt;
&lt;span class="n"&gt;lookup&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Rules&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="kt"&gt;Dict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="n"&gt;replacement&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;replacement&lt;/span&gt;

        &lt;span class="kt"&gt;Nothing&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="c1"&gt;--&lt;/span&gt;
            &lt;span class="c1"&gt;-- This branch would actually never be called by the generation&lt;/span&gt;
            &lt;span class="c1"&gt;-- algorithm but since Elm requires total functions we have to&lt;/span&gt;
            &lt;span class="c1"&gt;-- include it.&lt;/span&gt;
            &lt;span class="c1"&gt;--&lt;/span&gt;
            &lt;span class="kt"&gt;Sequence&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Reflections
&lt;/h2&gt;

&lt;p&gt;It's interesting how to improve &lt;code&gt;lookup&lt;/code&gt; we actually didn't have to change it much. But it makes sense. &lt;code&gt;build&lt;/code&gt; is used once. &lt;code&gt;lookup&lt;/code&gt; is used for every character in the sequence, which could be in the millions and billions. So pushing more processing into &lt;code&gt;build&lt;/code&gt; in order to improve &lt;code&gt;lookup&lt;/code&gt; ever so slightly is a good tradeoff in hindsight.&lt;/p&gt;

</description>
      <category>elm</category>
    </item>
    <item>
      <title>Diary of an Elm Developer - Lazy L-System generation</title>
      <dc:creator>Dwayne Crooks</dc:creator>
      <pubDate>Fri, 16 May 2025 13:58:06 +0000</pubDate>
      <link>https://dev.to/dwayne/diary-of-an-elm-developer-lazy-l-system-generation-2k7j</link>
      <guid>https://dev.to/dwayne/diary-of-an-elm-developer-lazy-l-system-generation-2k7j</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/hunorg/L-System-Studio" rel="noopener noreferrer"&gt;L-System Studio&lt;/a&gt; is an environment for creating and exploring &lt;a href="https://en.wikipedia.org/wiki/L-system" rel="noopener noreferrer"&gt;L-Systems&lt;/a&gt;. It was built with Elm by Hunor Geréd. I learnt about the project when he decided to submit it to &lt;a href="https://www.builtwithelm.co/" rel="noopener noreferrer"&gt;Built with Elm&lt;/a&gt; back in April 2023. It's a fun application for exploring L-Systems but it can be quite resource intensive. My browser has even crashed on several occasions. Definitely not a good look for Elm. Could the performance be improved whilst using Elm or do we have to resort to mutable generation in JavaScript? That was the nagging question.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's the problem?
&lt;/h2&gt;

&lt;p&gt;The first thing that caught my attention was the huge number of SVG nodes that were being generated in the DOM. Those SVG nodes are created by looping over the array of characters returned by &lt;a href="https://github.com/hunorg/L-System-Studio/blob/323ff158054bd986be9dced121db48d62821a940/src/LSys.elm#L13-L27" rel="noopener noreferrer"&gt;&lt;code&gt;LSys.generateSequence&lt;/code&gt;&lt;/a&gt;. It's not one to one but the number of SVG nodes that are added to the DOM can be very close to the number of characters in the array. And that array gets really large really quickly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; import LSys
&amp;gt; import Array

&amp;gt; LSys.generateSequence 4 "F+F+F+F" [('F', String.toList "F+F-F-FF+F+F-F")] |&amp;gt; Array.length
30427 : Int

&amp;gt; LSys.generateSequence 5 "F+F+F+F" [('F', String.toList "F+F-F-FF+F+F-F")] |&amp;gt; Array.length
243419 : Int

&amp;gt; LSys.generateSequence 6 "F+F+F+F" [('F', String.toList "F+F-F-FF+F+F-F")] |&amp;gt; Array.length
1947355 : Int

&amp;gt; LSys.generateSequence 7 "F+F+F+F" [('F', String.toList "F+F-F-FF+F+F-F")] |&amp;gt; Array.length

&amp;lt;--- Last few GCs ---&amp;gt;

[17075:0x2724f250]    11351 ms: Scavenge 2022.8 (2062.0) -&amp;gt; 2022.6 (2073.0) MB, 3.52 / 0.00 ms  (average mu = 0.173, current mu = 0.111) allocation failure; 
[17075:0x2724f250]    11359 ms: Scavenge 2029.6 (2073.0) -&amp;gt; 2030.4 (2073.7) MB, 5.33 / 0.00 ms  (average mu = 0.173, current mu = 0.111) allocation failure; 
[17075:0x2724f250]    11668 ms: Scavenge 2030.4 (2073.7) -&amp;gt; 2029.6 (2096.7) MB, 309.40 / 0.00 ms  (average mu = 0.173, current mu = 0.111) allocation failure; 


&amp;lt;--- JS stacktrace ---&amp;gt;

FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
----- Native stack trace -----

 1: 0xab10e4 node::OOMErrorHandler(char const*, v8::OOMDetails const&amp;amp;) [/nix/store/dcdc33kdjdhjnzg6rkmd0cx4kpwl8cac-nodejs-20.17.0/bin/node]
 2: 0xe6de60 v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, v8::OOMDetails const&amp;amp;) [/nix/store/dcdc33kdjdhjnzg6rkmd0cx4kpwl8cac-nodejs-20.17.0/bin/node]
 3: 0xe6e244 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, v8::OOMDetails const&amp;amp;) [/nix/store/dcdc33kdjdhjnzg6rkmd0cx4kpwl8cac-nodejs-20.17.0/bin/node]
 4: 0x109d907  [/nix/store/dcdc33kdjdhjnzg6rkmd0cx4kpwl8cac-nodejs-20.17.0/bin/node]
 5: 0x10b6479 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/nix/store/dcdc33kdjdhjnzg6rkmd0cx4kpwl8cac-nodejs-20.17.0/bin/node]
 6: 0x108f0e7 v8::internal::HeapAllocator::AllocateRawWithLightRetrySlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/nix/store/dcdc33kdjdhjnzg6rkmd0cx4kpwl8cac-nodejs-20.17.0/bin/node]
 7: 0x108fd24 v8::internal::HeapAllocator::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/nix/store/dcdc33kdjdhjnzg6rkmd0cx4kpwl8cac-nodejs-20.17.0/bin/node]
 8: 0x106f04e v8::internal::Factory::NewFillerObject(int, v8::internal::AllocationAlignment, v8::internal::AllocationType, v8::internal::AllocationOrigin) [/nix/store/dcdc33kdjdhjnzg6rkmd0cx4kpwl8cac-nodejs-20.17.0/bin/node]
 9: 0x14d8d70 v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [/nix/store/dcdc33kdjdhjnzg6rkmd0cx4kpwl8cac-nodejs-20.17.0/bin/node]
10: 0x719fe2699ef6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;N.B.&lt;/strong&gt; &lt;em&gt;I used &lt;a href="https://github.com/dwayne/L-System-Studio/tree/19128ad9f05d18fd5e4ad9f7a554b75f40629b18" rel="noopener noreferrer"&gt;my fork of the repository&lt;/a&gt; which I shared as an example in &lt;a href="https://dev.to/dwayne/github-actions-devbox-and-elm-g50#example-lsystem-studio"&gt;GitHub Actions, Devbox, and Elm&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I can't even check the length of the array after 7 iterations because the algorithm used for generation is using up too much memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does &lt;code&gt;LSys.generateSequence&lt;/code&gt; work?
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;generateSequence&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Float&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Array&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt;
&lt;span class="n"&gt;generateSequence&lt;/span&gt; &lt;span class="n"&gt;iterations&lt;/span&gt; &lt;span class="n"&gt;axiom&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt;
        &lt;span class="n"&gt;expandSymbol&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="kt"&gt;Extra&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
                &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;replacement&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromList&lt;/span&gt; &lt;span class="n"&gt;replacement&lt;/span&gt;

                &lt;span class="kt"&gt;Nothing&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromList&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foldl&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="n"&gt;seq&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;expandSymbol&lt;/span&gt; &lt;span class="n"&gt;seq&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toList&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toList&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;concat&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromList&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toList&lt;/span&gt; &lt;span class="n"&gt;axiom&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;range&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;round&lt;/span&gt; &lt;span class="n"&gt;iterations&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;iterations&lt;/code&gt; - The number of iterations&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;axiom&lt;/code&gt; - The initial string&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rules&lt;/code&gt; - The rewrite rules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recall &lt;code&gt;List.foldl : (a -&amp;gt; b -&amp;gt; b) -&amp;gt; b -&amp;gt; List a -&amp;gt; b&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For each character in the array we apply &lt;code&gt;expandSymbol&lt;/code&gt; to it. &lt;code&gt;expandSymbol&lt;/code&gt; attempts to find a rewrite string (represented as a list of characters) for a given character. A new array of lists is built which is eventually transformed back into an array of characters. A lot of intermediate lists and arrays are built up along the way. This is done &lt;code&gt;round iterations&lt;/code&gt; number of times over arrays of increasing length.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;expandSymbol&lt;/code&gt; searches an association list multiple times for the same symbol and each time it creates a new array from the replacement list of characters it finds.&lt;/p&gt;

&lt;p&gt;It seems then that a poor choice of data structures and algorithms has led to performance and memory issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thoughts after the investigation
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Why is &lt;code&gt;iterations&lt;/code&gt; even a &lt;code&gt;Float&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rules&lt;/code&gt; would be easier to enter via &lt;code&gt;elm repl&lt;/code&gt; if it had the type &lt;code&gt;List ( Char, String )&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rules&lt;/code&gt; should be processed into a data structure that has good lookup performance.&lt;/li&gt;
&lt;li&gt;The replacements returned by a rule lookup should be created exactly once.&lt;/li&gt;
&lt;li&gt;An array or a list is not the right data structure for storing the characters generated by a given L-System since when stored in memory it will take up too much space. We need to be able to represent the whole value that could be generated but only generate what's needed at any given point in time. Could lazy evaluation work here?&lt;/li&gt;
&lt;li&gt;Maybe I could repurpose &lt;a href="https://package.elm-lang.org/packages/dwayne/elm-trs2/1.0.0/Logic-Stream" rel="noopener noreferrer"&gt;&lt;code&gt;Logic.Stream&lt;/code&gt;&lt;/a&gt; from my &lt;a href="https://github.com/dwayne/elm-trs2/tree/1.0.0" rel="noopener noreferrer"&gt;The Reasoned Schemer (2nd Edition)&lt;/a&gt; project to use as the data structure for the in-memory representation of the entire L-System.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The new API
&lt;/h2&gt;

&lt;p&gt;This is the new API I came up with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="kt"&gt;LSystem&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;generate&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="kt"&gt;Rules&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Rules&lt;/span&gt;

    &lt;span class="c1"&gt;-- Create&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fromList&lt;/span&gt;

    &lt;span class="c1"&gt;-- Query&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lookup&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Rules&lt;/span&gt;

&lt;span class="n"&gt;fromList&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Rules&lt;/span&gt;

&lt;span class="n"&gt;lookup&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Rules&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt;

    &lt;span class="c1"&gt;-- Create&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;singleton&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fromString&lt;/span&gt;

    &lt;span class="c1"&gt;-- Combine&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;concat&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;concatMap&lt;/span&gt;

    &lt;span class="c1"&gt;-- Query&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;

    &lt;span class="c1"&gt;-- Convert&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uncons&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toList&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;

&lt;span class="n"&gt;singleton&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;fromString&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt;

&lt;span class="n"&gt;concat&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;concatMap&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;

&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;

&lt;span class="n"&gt;uncons&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;toList&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;code&gt;LSystem&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="kt"&gt;LSystem&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Rules&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Rules&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;generate&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt;
&lt;span class="n"&gt;generate&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt; &lt;span class="n"&gt;axiom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;generateHelper&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Rules&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromList&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Sequence&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromString&lt;/span&gt; &lt;span class="n"&gt;axiom&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;generateHelper&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Rules&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt;
&lt;span class="n"&gt;generateHelper&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="n"&gt;current&lt;/span&gt;

    &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt;
            &lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                &lt;span class="kt"&gt;Sequence&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;concatMap&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flip&lt;/span&gt; &lt;span class="kt"&gt;Rules&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lookup&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;
        &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="n"&gt;generateHelper&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;


&lt;span class="n"&gt;flip&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
&lt;span class="n"&gt;flip&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The number of iterations, &lt;code&gt;n&lt;/code&gt;, is represented as an &lt;code&gt;Int&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rules&lt;/code&gt; input has changed to &lt;code&gt;List ( Char, String )&lt;/code&gt; which should make it easier to enter on the &lt;code&gt;elm repl&lt;/code&gt; or for tests but that's not the way rules are used internally. They are processed into a data structure, &lt;code&gt;Rules.fromList rules&lt;/code&gt;, that has better lookup performance than an association list.&lt;/p&gt;

&lt;p&gt;The looping is done using &lt;code&gt;generateHelper&lt;/code&gt; which is implemented &lt;a href="https://functional-programming-in-elm.netlify.app/recursion/tail-call-elimination.html" rel="noopener noreferrer"&gt;tail-recursively&lt;/a&gt; to ensure we don't blow the stack. Though that may not have been necessary since &lt;code&gt;n&lt;/code&gt; doesn't need to be too large. For e.g. 50 iterations is probably over doing it. Either way, the implementation can handle millions of iterations now.&lt;/p&gt;

&lt;p&gt;The workhorse of the algorithm is &lt;code&gt;Sequence.concatMap&lt;/code&gt;. It is used to transform the sequence from one iteration to the next.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;Rules&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="kt"&gt;Rules&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Rules&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fromList&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Dict&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Rules&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Rules&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Dict&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="n"&gt;fromList&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Rules&lt;/span&gt;
&lt;span class="n"&gt;fromList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Rules&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="kt"&gt;Dict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromList&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Tuple&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapSecond&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;lookup&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Rules&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt;
&lt;span class="n"&gt;lookup&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Rules&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Dict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;withDefault&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Sequence&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;singleton&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Rules&lt;/code&gt; is an &lt;a href="https://elm-radio.com/episode/intro-to-opaque-types/" rel="noopener noreferrer"&gt;opaque type&lt;/a&gt; that is implemented as a dictionary. The keys are characters and the values are preprocessed, &lt;code&gt;List.map (Tuple.mapSecond Sequence.fromString)&lt;/code&gt;, into sequences that get reused each time a key is looked up.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;lookup&lt;/code&gt; finds the expansion for a given character.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;N.B.&lt;/strong&gt; &lt;em&gt;The only issue I have with &lt;code&gt;lookup&lt;/code&gt; is that for missing keys it creates a new singleton sequence each time. Any ideas to avoid doing that would be appreciated.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;Sequence&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;singleton&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fromString&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;concat&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;concatMap&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uncons&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toList&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Empty&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Cons&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Thunk&lt;/span&gt; &lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A sequence is a combination of a list and a lazy list. An idea I stole from &lt;a href="https://github.com/dwayne/elm-trs2/tree/1.0.0" rel="noopener noreferrer"&gt;The Reasoned Schemer (2nd Edition)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you remove the &lt;code&gt;Thunk&lt;/code&gt; constructor then you have a list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Empty&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Cons&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you combine the &lt;code&gt;Cons&lt;/code&gt; and &lt;code&gt;Thunk&lt;/code&gt; constructors then you have a lazy list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;LazyList&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Empty&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Cons&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;LazyList&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;singleton&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;singleton&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Cons&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="kt"&gt;Empty&lt;/span&gt;


&lt;span class="n"&gt;fromString&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt;
&lt;span class="n"&gt;fromString&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Thunk&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foldr&lt;/span&gt; &lt;span class="kt"&gt;Cons&lt;/span&gt; &lt;span class="kt"&gt;Empty&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Combine
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;concat&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;concat&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Empty&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;b&lt;/span&gt;

        &lt;span class="kt"&gt;Cons&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="kt"&gt;Cons&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;concat&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;Thunk&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="kt"&gt;Thunk&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;concat&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;concatMap&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="n"&gt;concatMap&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Empty&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="kt"&gt;Empty&lt;/span&gt;

        &lt;span class="kt"&gt;Cons&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;concat&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;concatMap&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;Thunk&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="kt"&gt;Thunk&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;concatMap&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;()))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The major difference between this implementation and &lt;a href="https://package.elm-lang.org/packages/dwayne/elm-trs2/1.0.0/Logic-Stream" rel="noopener noreferrer"&gt;&lt;code&gt;Logic.Stream&lt;/code&gt;&lt;/a&gt; is that &lt;code&gt;Sequence.concat&lt;/code&gt; which corresponds to &lt;a href="https://github.com/dwayne/elm-trs2/blob/1.0.0/src/Logic/Stream.elm#L84-L127" rel="noopener noreferrer"&gt;&lt;code&gt;Logic.Stream.append&lt;/code&gt;&lt;/a&gt; doesn't interleave between sequences.&lt;/p&gt;

&lt;h3&gt;
  
  
  Query
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;lengthHelper&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;


&lt;span class="n"&gt;lengthHelper&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
&lt;span class="n"&gt;lengthHelper&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Empty&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;n&lt;/span&gt;

        &lt;span class="kt"&gt;Cons&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;lengthHelper&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt;

        &lt;span class="kt"&gt;Thunk&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;lengthHelper&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sequences can get very long after relatively few iterations, much longer than the value an &lt;code&gt;Int&lt;/code&gt; can store. So this &lt;code&gt;length&lt;/code&gt; is really only useful for debugging purposes and it has to be implemented tail-recursively to ensure it gets optimized into a loop. Otherwise, it won't even be helpful for debugging.&lt;/p&gt;

&lt;h3&gt;
  
  
  Convert
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;uncons&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;uncons&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Empty&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="kt"&gt;Nothing&lt;/span&gt;

        &lt;span class="kt"&gt;Cons&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;Thunk&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;uncons&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;())&lt;/span&gt;


&lt;span class="n"&gt;toList&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;toList&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Empty&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;[]&lt;/span&gt;

        &lt;span class="kt"&gt;Cons&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;toList&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt;

        &lt;span class="kt"&gt;Thunk&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;toList&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;uncons&lt;/code&gt; allows you to process the sequence one character at time. It's not used for generation but my plan is to use it for rendering.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;toList&lt;/code&gt; is only useful for debugging purposes on small sequences. I didn't even bother to make it tail-recursive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; import LSystem
&amp;gt; import Sequence

&amp;gt; Sequence.length &amp;lt;| LSystem.generate 6 [('F', "F+F-F-FF+F+F-F")] "F+F+F+F"
1947355 : Int

&amp;gt; Sequence.length &amp;lt;| LSystem.generate 7 [('F', "F+F-F-FF+F+F-F")] "F+F+F+F"
15578843 : Int

&amp;gt; Sequence.length &amp;lt;| LSystem.generate 8 [('F', "F+F-F-FF+F+F-F")] "F+F+F+F"
124630747 : Int

&amp;gt; Sequence.length &amp;lt;| LSystem.generate 9 [('F', "F+F-F-FF+F+F-F")] "F+F+F+F"
997045979 : Int

&amp;gt; Sequence.length &amp;lt;| LSystem.generate 10 [('F', "F+F-F-FF+F+F-F")] "F+F+F+F"
7976367835 : Int

&amp;gt; Sequence.uncons &amp;lt;| LSystem.generate 100 [('F', "F+F-F-FF+F+F-F")] "F+F+F+F"
Just ('F',Cons '+' (Cons 'F' (Cons '-' (Cons 'F' (Cons '-' (Cons 'F' (Cons 'F' (Cons '+' (Cons 'F' (Cons '+' (Cons 'F' (Cons '-' (Cons 'F' (Cons '+' (Thunk &amp;lt;function&amp;gt;)))))))))))))))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Iteration 10 of the L-System we started off with has over 7 billion characters. 🤯&lt;/p&gt;

&lt;p&gt;We can now generate and store the result of 100 iterations of the L-System and process it one character at time. 🎉&lt;/p&gt;

&lt;p&gt;How are we going to render the L-System if we can't store it in a list or put billions of SVG nodes in the browser? These are questions best left for another day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interesting Related Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=XrNdvWqxBvA" rel="noopener noreferrer"&gt;Why Functional Programming Matters by John Hughes at Functional Conf 2016&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Word-at-a-time programming vs whole value programming

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=XrNdvWqxBvA&amp;amp;t=1111s" rel="noopener noreferrer"&gt;One Word at a Time&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Lazy evaluation implied whole value programming was possible

&lt;ul&gt;
&lt;li&gt;John Hughes was interested in separating the decision of how many things to compute from the code you have to compute&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=XrNdvWqxBvA&amp;amp;t=1999s" rel="noopener noreferrer"&gt;Lazy Evaluation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://archive.org/details/recursiveprogram0000burg" rel="noopener noreferrer"&gt;Recursive Programming Techniques&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Section 3.10 - Sequences, Coroutines, and Streams&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Subscribe to my newsletter
&lt;/h2&gt;

&lt;p&gt;If you're interested in improving your skills with Elm then I invite you to subscribe to my newsletter, &lt;a href="https://buttondown.com/elmwithdwayne" rel="noopener noreferrer"&gt;Elm with Dwayne&lt;/a&gt;. To learn more about it, please read this &lt;a href="https://dev.to/dwayne/announcing-my-newsletter-elm-with-dwayne-hoi"&gt;announcement&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>elm</category>
      <category>performance</category>
    </item>
    <item>
      <title>Diary of an Elm Developer</title>
      <dc:creator>Dwayne Crooks</dc:creator>
      <pubDate>Thu, 15 May 2025 14:00:45 +0000</pubDate>
      <link>https://dev.to/dwayne/diary-of-an-elm-developer-2p8h</link>
      <guid>https://dev.to/dwayne/diary-of-an-elm-developer-2p8h</guid>
      <description>&lt;p&gt;Every morning I gift myself 90 mins to work on anything I desire. Sometimes I read interesting books (&lt;a href="https://www.hup.harvard.edu/books/9780674237513" rel="noopener noreferrer"&gt;1&lt;/a&gt;, &lt;a href="https://www.bloomsbury.com/us/logicomix-9781596914520/" rel="noopener noreferrer"&gt;2&lt;/a&gt;, &lt;a href="https://calnewport.com/slow/" rel="noopener noreferrer"&gt;3&lt;/a&gt;, &lt;a href="https://www.toddrose.com/endofaverage" rel="noopener noreferrer"&gt;4&lt;/a&gt;), other times I learn new skills (&lt;a href="https://mitpress.mit.edu/9780262062794/essentials-of-programming-languages/" rel="noopener noreferrer"&gt;5&lt;/a&gt;, &lt;a href="https://artofproblemsolving.com/" rel="noopener noreferrer"&gt;6&lt;/a&gt;) but most times I work on programming projects.&lt;/p&gt;

&lt;p&gt;For a long time now those programming projects have been Elm related. What can I say? I simply enjoy using Elm. I've tried to share my knowledge along the way by taking the time to reflect on what I've done and by writing about it from my perspective. On many occasions I just shared the work and put off sharing my thoughts about the work for so long that it became a chore to write about it.&lt;/p&gt;

&lt;p&gt;Reflecting and writing about my programming selfishly helps me improve my software development skills. Writing in public forces me to think more deeply about what I'm doing and why I'm doing it such that it gives me a richer understanding of my craft. A positive side effect is that what I share could also potentially help others. Though, I try to temper my hubris and not fool myself into thinking that random people on the internet actually care about what I have to say.&lt;/p&gt;

&lt;p&gt;Given that I sometimes don't share and write about my work as often as I'd like and that I think sharing and writing about my work helps myself and others, I've decided to start a new series on this blog called "Diary of an Elm Developer". It's a way for me to give myself permission to share more frequently about my Elm related excursions in an open and freer format.&lt;/p&gt;

&lt;p&gt;I'm feeling excited about this new series. Stay tuned!&lt;/p&gt;

&lt;h2&gt;
  
  
  Subscribe to my newsletter
&lt;/h2&gt;

&lt;p&gt;If you're interested in improving your skills with Elm then I invite you to subscribe to my newsletter, &lt;a href="https://buttondown.com/elmwithdwayne" rel="noopener noreferrer"&gt;Elm with Dwayne&lt;/a&gt;. To learn more about it, please read this &lt;a href="https://dev.to/dwayne/announcing-my-newsletter-elm-with-dwayne-hoi"&gt;announcement&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>elm</category>
    </item>
  </channel>
</rss>
