<?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>Forem: Kasey Speakman</title>
    <description>The latest articles on Forem by Kasey Speakman (@kspeakman).</description>
    <link>https://forem.com/kspeakman</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%2F15366%2Fc6550569-8654-4c9e-828b-c1dcb9c8a0df.png</url>
      <title>Forem: Kasey Speakman</title>
      <link>https://forem.com/kspeakman</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/kspeakman"/>
    <language>en</language>
    <item>
      <title>Explaining the LMAX Disruptor</title>
      <dc:creator>Kasey Speakman</dc:creator>
      <pubDate>Wed, 10 Sep 2025 06:05:29 +0000</pubDate>
      <link>https://forem.com/kspeakman/explaining-the-lmax-disruptor-jkd</link>
      <guid>https://forem.com/kspeakman/explaining-the-lmax-disruptor-jkd</guid>
      <description>&lt;p&gt;More than a decade ago, the &lt;a href="https://lmax-exchange.github.io/disruptor/disruptor.html" rel="noopener noreferrer"&gt;LMAX Disruptor&lt;/a&gt; pattern entered the scene. I found it interesting. But frankly had trouble wrapping my head around it. It wasn't until years later when I started working on a JVM project that I finally understood. There is a lot of good performance engineering baked into it. But it basically only exists because of JVM limitations. It wouldn't rise to the level of a pattern in system level languages like Go or Rust, or even a high level language with more performance knobs like C#.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;Let's back up a bit. LMAX was building a high frequency trading platform. Such a system optimizes latency and throughput. For these constraints, there's a big problem with their choice of platform. The JVM was designed for Java, which ostensibly is meant for high-level -- especially object-oriented -- programming. It just doesn't give the user many performance knobs to turn.&lt;/p&gt;

&lt;p&gt;One of the reasons I had such a hard time understanding the purpose for many of the details in the Disruptor was because I came from .NET. Even though Microsoft basically copied Sun's Java homework to make C# on .NET, in the process they also managed to bake in some improvements including performance tune-ability. Many of the pattern details were workarounds that I didn't recognize. I'll give you some examples:&lt;/p&gt;

&lt;h3&gt;
  
  
  Java Limitations
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Structs
&lt;/h4&gt;

&lt;p&gt;Java does not allow the user to create primitives aka structs aka stack-allocated types. It only allows you to make garbage-collected (heap-allocated) types. C# allows you to create structs. System level languages tend to take the inverse approach. Types are assumed stack-allocated until you explicitly allocate them on the heap.&lt;/p&gt;

&lt;p&gt;Here's the oversimplified explanation of how that works. Each code scope (e.g. function execution) runs in its own stack frame. (Note: this is where a stack trace comes from.) When you return from that function, the stack frame goes away along with whatever local variables were allocated in it. So stack-allocated variables do not need any special memory management. When you pass struct data into a function or return struct data from it, this data is copied between stack frames. This is efficient until the data gets too large. That's when you might reach for a separate memory allocation on the heap. Then the stack frame only copies the pointer (8 bytes on a 64-bit system) for the heap data to the next stack frame instead of the full data.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What constitutes "too large depends" on the system, but on .NET I recall the threshold being 24-32 bytes (3-4 longs or reference objects). Above that, object references could be better performance, depending on garbage collector behavior. As with all performance optimizations, you don't know for sure which is better until you test it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;High level languages default to heap allocated data so that it's copying 8 byte pointers between stack frames instead of the full data. This is probably better performance in common cases, but optimized code might do better with structs. Java doesn't give you the option to tune performance in this way.&lt;/p&gt;

&lt;h4&gt;
  
  
  Memory Layout
&lt;/h4&gt;

&lt;p&gt;Alignment to CPU cache line is another common performance optimization. In short, CPUs have multiple levels of caches. Especially for collections, there are potential performance advantages if you design your data types to be exactly 8, 16, 32, or 64 bytes. This is because data is read from memory into CPU cache lines, which are generally 64 bytes. If your data fits exactly in a cache line, the CPU can use optimizations / pipelining which improves performance when processing collections.&lt;/p&gt;

&lt;p&gt;Java has no way to specify memory layout, so Disruptor makes use of padding fields in the classes used by ring buffers. Named like &lt;code&gt;p1&lt;/code&gt;, &lt;code&gt;p2&lt;/code&gt;, etc. These fields are not used by logic, but are only there to align the data structure. Whereas .NET has StructLayout and related attributes to allow you to add padding explicitly.&lt;/p&gt;

&lt;h4&gt;
  
  
  Boxing
&lt;/h4&gt;

&lt;p&gt;Even though Java does not allow you to create primitive data types, it has some built in, like &lt;code&gt;int&lt;/code&gt;. However, when you use primitives in generics Java "boxes" them up as heap-allocated objects. Especially in generic collections, this uses more memory and costs CPU instructions to box and unbox.&lt;/p&gt;

&lt;p&gt;Disruptor doesn't explicitly mention this, but it is a well-known drag on JVM generic performance. One which I wasn't familiar with from .NET as it doesn't box primitives for generics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance optimizations
&lt;/h3&gt;

&lt;p&gt;Overall, the Disruptor is a marriage of qualities from actor and SEDA patterns with some distinct performance optimizations like a ring buffer that is also an object pool. From Actor / Agent pattern, Disruptor took the message inbox and independent processing. However, the disruptor is also step-wise coordinated with previous steps like in SEDA.&lt;/p&gt;

&lt;p&gt;The ring buffer plays a prominent role in Disruptor for a few reasons. First, it is a common way to implement a fixed-size queue. This is necessary in high-throughput systems because unbounded queues will eventually reach memory usage limits. The bounded queue forces you to think about your memory usage and full queue behavior. (E.g. load shedding vs blocking)&lt;/p&gt;

&lt;p&gt;The ring buffer simultaneously serves as an object pool. Object pools are pretty common in game development. Business devs will be more familiar with them from database or HTTP connection pooling. The general idea being that creating a new object is expensive in some way, so instead keep a pool of them and soft reset and reuse an old instance. For db or http pools, the expensive part is actually making the network connection. But for very high-throughput systems, the churn of allocating new objects and then garbage collecting them in a tight loop is the problem. By pre-allocating blank objects in the ring buffer, these objects will never be garbage collected nor need new instantiations later. So it essentially circumvents garbage collection.&lt;/p&gt;

&lt;p&gt;There are other optimizations around the ring buffer such as only using sizes which are powers of 2. The intrinsic way a ring buffer works is that it has an ever-increasing queue position which can go past the actual size of the buffer. So if the ring buffer size is 4 (indexes 0 to 3), but we're processing our 15th item out of the queue, we can calculate the ring buffer position with modulus by the buffer size. 15 modulus 4 is 3. So the item we want is at index 3. The problem is the modulus operation, which can be expensive to calculate in a tight loop. But not for powers of 2. Since the computer operates in binary, powers of 2 are especially simple to divide by. So using a power of 2 as the ring buffer size increases performance.&lt;/p&gt;

&lt;p&gt;I didn't cover all the optimizations, but you get the idea.&lt;/p&gt;

</description>
      <category>jvm</category>
      <category>architecture</category>
      <category>programming</category>
    </item>
    <item>
      <title>Fixing the Elm Architecture</title>
      <dc:creator>Kasey Speakman</dc:creator>
      <pubDate>Mon, 28 Jul 2025 21:23:09 +0000</pubDate>
      <link>https://forem.com/kspeakman/fixing-the-elm-architecture-9g</link>
      <guid>https://forem.com/kspeakman/fixing-the-elm-architecture-9g</guid>
      <description>&lt;p&gt;Elm architecture aka MVU was a breath of fresh air when I encountered it. It remains the best UI dev experience I know of. As I've done larger and larger projects with it, used it on the backend, and seen its usage in other contexts, it has some friction points. Here are my current thoughts on how to resolve those sore spots.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Code below is in F# unless otherwise noted.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Centralizing logic into &lt;code&gt;update&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//                          sus&lt;/span&gt;
&lt;span class="c1"&gt;//                           |&lt;/span&gt;
&lt;span class="c1"&gt;//                           v&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="n"&gt;initArg&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Model&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="nc"&gt;Cmd&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;One thing that has always bothered me about &lt;code&gt;init&lt;/code&gt; is that it comes out swinging by returning a &lt;code&gt;Cmd&lt;/code&gt;. This can be a problem in some UI technology (e.g. WPF) that is sensitive to the timing of initializing elements vs side effects. I recall at least one issue about this in the Elmish repo.&lt;/p&gt;

&lt;p&gt;It's also annoying that all decisions are made in &lt;code&gt;update&lt;/code&gt;... except for the 1 made in &lt;code&gt;init&lt;/code&gt;, as indicated by returning Cmd. This means that to test an MVU program both functions have to be exercised. I played around with my own backend-focused lib and determined that this doesn't have to be the case.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I had to write my own library for backend workflows because they have different run characteristics from UIs, but my lib used identical MVU functions, minus &lt;code&gt;view&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;All logical decisions could be done in &lt;code&gt;update&lt;/code&gt; without changing it in any way. Just move &lt;code&gt;init&lt;/code&gt;'s Cmd-producing decision into its own Msg case. Then get rid of &lt;code&gt;init&lt;/code&gt;. That works, but the caller has to know how to construct an initial &lt;code&gt;Model&lt;/code&gt; and &lt;code&gt;Msg&lt;/code&gt; to send to &lt;code&gt;update&lt;/code&gt;. So then I thought, why not use &lt;code&gt;init&lt;/code&gt; for this purpose instead... basically an adapter function to translate the data you have (&lt;code&gt;initArg&lt;/code&gt;) into the form needed to kick off the &lt;code&gt;update&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="n"&gt;initArg&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Model&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="nc"&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;...&lt;/span&gt;
&lt;span class="c1"&gt;//                  \---------/&lt;/span&gt;
&lt;span class="c1"&gt;// tells how to          |&lt;/span&gt;
&lt;span class="c1"&gt;// start update          |&lt;/span&gt;
&lt;span class="c1"&gt;//             -----------&lt;/span&gt;
&lt;span class="c1"&gt;//             |&lt;/span&gt;
&lt;span class="c1"&gt;//             v&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;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="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Model&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="nc"&gt;Cmd&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;For UI platforms that need it, this would also provide an opportunity for UI elements to be initialized from the Model, and the Msg queued for later processing, without kicking off any side effects.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;At the time I made this change, I already implemented a similar tactic for resumable workflows. My code had the model from the previous completed workflow. It just needed to know the correct Msg to resume the workflow. I created a function called &lt;code&gt;resume&lt;/code&gt; for that purpose. So it seemed natural for &lt;code&gt;init&lt;/code&gt; to do the nearly same thing to start the workflow.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Parameter order
&lt;/h2&gt;

&lt;p&gt;Another thing that always bothered me about Elm is the parameter order of &lt;code&gt;update&lt;/code&gt;. You can look at Cmd as a future Msg, so the &lt;code&gt;update&lt;/code&gt; function returns nearly the same types as its inputs, but with their order swapped.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//     should be swapped&lt;/span&gt;
&lt;span class="c1"&gt;//          ------&lt;/span&gt;
&lt;span class="c1"&gt;//          |    |&lt;/span&gt;
&lt;span class="c1"&gt;//          v    v&lt;/span&gt;
&lt;span class="k"&gt;let&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="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Cmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;This is definitely a minor point, practically speaking. I just have to bake in an argument swap when I'm testing &lt;code&gt;update&lt;/code&gt;. But it's logically incorrect for how &lt;code&gt;update&lt;/code&gt; is used. And I can't unsee that.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is a winding road to explain how Elm arrived at that. But it has to do with its Haskell heritage; Haskell's focus on laziness; and laziness being represented by the right fold operation. Which in this case puts &lt;code&gt;model&lt;/code&gt; as the 2nd argument. Whereas left fold -- which would have &lt;code&gt;model&lt;/code&gt; as the 1st argument -- is considered eagerly computed. It's an inherited philosophical choice, not a practical one, since Elm doesn't focus on laziness like Haskell. You can even observe this cognitive dissonance in Elm's &lt;code&gt;List.foldl&lt;/code&gt; which processes the list left to right, but combines elements right to left as a right fold would, so it behaves unexpectedly when order matters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// F#, Haskell, and every other language's left fold result&lt;/span&gt;
&lt;span class="s2"&gt;"abc"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;List&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fold&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt; &lt;span class="n"&gt;s2&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="s2"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="s2"&gt;"c"&lt;/span&gt;&lt;span class="p"&gt;]&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="c1"&gt;-- Elm's left fold result&lt;/span&gt;
&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cba"&lt;/span&gt; &lt;span class="o"&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="n"&gt;s1&lt;/span&gt; &lt;span class="n"&gt;s2&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="p"&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="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;b"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;c"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;-- ^&lt;/span&gt;
&lt;span class="c1"&gt;--  \&lt;/span&gt;
&lt;span class="c1"&gt;--   huh?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It seems like &lt;code&gt;update&lt;/code&gt; arguments are meant to match Elm's incorrect left fold behavior. This is easily fixable by swapping input argument order.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// before&lt;/span&gt;
&lt;span class="k"&gt;let&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="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Model&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="nc"&gt;Cmd&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="c1"&gt;//           \  /&lt;/span&gt;
&lt;span class="c1"&gt;//            \/  swap&lt;/span&gt;
&lt;span class="c1"&gt;//            /\&lt;/span&gt;
&lt;span class="c1"&gt;// after     /  \&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="n"&gt;model&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="nc"&gt;Model&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="nc"&gt;Cmd&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;blockquote&gt;
&lt;p&gt;Later in this article, you'll see an example of testing &lt;code&gt;update&lt;/code&gt; logic by providing a list of Msgs, then checking that the correct side effects were produced. Testing made the most sense with left fold. Which made the most sense with the corrected version of update.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Untestable &lt;code&gt;Cmd&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Elm's representation of side effects is with Cmd. The Elm philosophy is that users are not allowed to do side effects in Elm, so Cmd is locked down. You can use a Cmd-generating platform function provided by Elm. Or when you inevitably need a side effect that Elm doesn't cover, you must send data out of an asynchronous port to let JS run your side effect and send the result back in thru another async port.&lt;/p&gt;

&lt;p&gt;In Elmish, the F# library, Cmd is an alias for a list of user-declared functions. Each of which is provided with a dispatcher to send Msgs back. (Unlike Elm, you actually get to write your side effects in F#! And JS &amp;lt;-&amp;gt; F# interop is easy.) So your &lt;code&gt;update&lt;/code&gt; code returns functions to run when it's time to call side effects.&lt;/p&gt;

&lt;p&gt;In both cases, Cmd is untestable. In Elm it's intentionally opaque, even the fact that it's a collection under the covers. There is no way to test it at all. In Elmish you could potentially test generated Cmds if your &lt;code&gt;update&lt;/code&gt; fn also accepts side effect dependencies as parameters and you pass in mocks instead of the real ones. But I consider this to be the hard way to test things, on top of polluting &lt;code&gt;update&lt;/code&gt; with side effect related concerns.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I'll be honest, I never did a lot of unit testing on frontend code. It was just not needed. But backend is a different story. It is all about making business decisions and executing the chosen side effects. It needs to be tested thoroughly for correctness.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Declarative side effects
&lt;/h3&gt;

&lt;p&gt;For backend, the Model is less important to test. Rather, I needed to be sure that the correct side effects were decided. Cmd being hard to test was just not acceptable. So I introduced another step in the MVU process. This involves adding another type similar in style to &lt;code&gt;Msg&lt;/code&gt; but for the purpose of declaring a side effect to run. I called it &lt;code&gt;Effect&lt;/code&gt;. Then I introduced a function to &lt;code&gt;perform&lt;/code&gt; those side effects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Msg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;// define possible side effects, a DU like Msg&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Effect&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="nc"&gt;Model&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;let&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="n"&gt;initArg&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Model&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="nc"&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;...&lt;/span&gt;

&lt;span class="c1"&gt;//                            test with equality&lt;/span&gt;
&lt;span class="c1"&gt;//                                     v&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="n"&gt;model&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="nc"&gt;Model&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Effect&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;//    could be Task and/or synchronous too&lt;/span&gt;
&lt;span class="c1"&gt;//                     v&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;perform&lt;/span&gt; &lt;span class="n"&gt;effect&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Async&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Msg&lt;/span&gt;&lt;span class="p"&gt;&amp;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;&lt;em&gt;Like Msg, Effect is a user-declared type.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Above, &lt;code&gt;update&lt;/code&gt; is living its best functional life. Its inputs and outputs are &lt;strong&gt;value-equatable&lt;/strong&gt; immutable data. That means side effects can be tested for correctness with simple equality checks. Like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//  expected    model      effects&lt;/span&gt;
&lt;span class="c1"&gt;//                |           |&lt;/span&gt;
&lt;span class="c1"&gt;//                v           v&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;expected&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="o"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;LoadWidget&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;//   basically left fold            model   simulated scenario&lt;/span&gt;
&lt;span class="c1"&gt;//            |                       |             |&lt;/span&gt;
&lt;span class="c1"&gt;//            v                       v     /----------------\&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sim&lt;/span&gt; &lt;span class="nn"&gt;MyWorkflow&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="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;Go&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nc"&gt;LoadFailed&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;// test workflow with simple equality&lt;/span&gt;
&lt;span class="n"&gt;assertEqual&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Notice that I don't need to involve &lt;code&gt;init&lt;/code&gt; in this test, since it no longer returns a side-effect... only &lt;code&gt;update&lt;/code&gt; does.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Above, LoadFailed is an actual Msg that would return from &lt;code&gt;perform&lt;/code&gt; when there was a database failure. The only Effect produced is the attempt to load a widget. After that, we're expecting that upon receiving LoadFailed the &lt;code&gt;update&lt;/code&gt; fn will stop and not try to produce more side effects. If it does produce more Effects, &lt;code&gt;assertEqual&lt;/code&gt; will fail the test. So this not only tests that it does what it should but also that it doesn't do more than it should.&lt;/p&gt;

&lt;p&gt;This is what the relevant section of &lt;code&gt;perform&lt;/code&gt; might look like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;perform&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dbConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;effect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;effect&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;LoadWidget&lt;/span&gt; &lt;span class="n"&gt;widgetId&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="n"&gt;widget&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;findWidget&lt;/span&gt; &lt;span class="n"&gt;dbConfig&lt;/span&gt; &lt;span class="n"&gt;widgetId&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;WidgetLoaded&lt;/span&gt; &lt;span class="n"&gt;widget&lt;/span&gt;
            &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Widget load failed {widgetId}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;widgetId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;LoadFailed&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;We take advantage of partial application to inject dependencies as the first argument.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With this arrangement, side effects no longer need to be mocked -- with all the overhead involved in that -- to test logical decisions. In fact, side effects play no part at all in unit testing. They only have to be integration tested.&lt;/p&gt;

&lt;p&gt;We used this same adaptation on frontend. There, having a place for side effects prevented them from being introduced accidentally in &lt;code&gt;update&lt;/code&gt;. We also found it made &lt;code&gt;update&lt;/code&gt; code easy to reason about. When we did have a UI bug, it was usually quick to spot and fix. At least it was once we got away from &lt;a href="https://dev.to/kspeakman/organizing-mvu-projects-2218#for-ui-state"&gt;sharing state across pages&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nested page fatigue
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Everything I've mentioned so far is easily usable inside Elmish, maybe even Elm, with tiny adapter functions. That's what I did for our Elmish frontend. This one could need more than that, not sure yet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What is nested page fatigue? It's the vaunted fractal nature of The Elm Architecture. It is great to a degree, but it requires wiring together too dang many things between a parent and child. Every time you create a child page, you have to wire it into the parent page in every possible way.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Msg case for child msgs&lt;/li&gt;
&lt;li&gt;Model property or case for child model&lt;/li&gt;
&lt;li&gt;init or update case to initialize child

&lt;ul&gt;
&lt;li&gt;depends on whether it uses navigation&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;update case to call child update&lt;/li&gt;

&lt;li&gt;view code to call child view&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;After hundred thousands of lines of UI code and many dozens of pages, the friction is real. And this is the most common complaint about MVU. The backend library taught me that it was possible to avoid this fatigue while still using MVU all the way down.&lt;/p&gt;

&lt;p&gt;You see, when I tried to compose F# backend MVU workflows in the traditional way, similar to frontend, it made things very awkward and mixed a lot of concerns between workflows.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If this were OO, that way of wiring things would be a dark-but-humorously-named anti-pattern called Inappropriate Intimacy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then I noticed when non-MVU code called MVU workflows, it first used an adapter function to convert it to a normal Async-returning function. Then I realized that this is the best way to compose MVU workflows... treat each one as a single overall side effect. This makes composing them easy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="n"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// workflow1/2 are records containing init, update, etc&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="n"&gt;result1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;runWorkflow&lt;/span&gt; &lt;span class="n"&gt;workflow1&lt;/span&gt; &lt;span class="n"&gt;initArg&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="n"&gt;result2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;runWorkflow&lt;/span&gt; &lt;span class="n"&gt;workflow2&lt;/span&gt; &lt;span class="n"&gt;result1&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// or thru perform&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;perform&lt;/span&gt; &lt;span class="n"&gt;deps&lt;/span&gt; &lt;span class="n"&gt;effect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;effect&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
        &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;MyEffect&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;let&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="n"&gt;runWorkflow&lt;/span&gt; &lt;span class="n"&gt;workflow1&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;EffectHappens&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Right okay, but how does that help frontend?&lt;/p&gt;

&lt;p&gt;The purpose of these backend workflows is to make correct decisions about side effects and run them. So composing them as side effects made sense. The purpose of a frontend program is decide about and display UI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Composition through &lt;code&gt;view&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This gave me the idea that a whole Elmish-React child program could be packaged up as a React component, then displayed along side the other HTML elements (also React components) in the parent's &lt;code&gt;view&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="n"&gt;model&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;div&lt;/span&gt; &lt;span class="bp"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Route&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
        &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;CourseList&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;initArg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt;
            &lt;span class="nn"&gt;Elmish&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="n"&gt;initArg&lt;/span&gt; &lt;span class="nn"&gt;CourseList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="nn"&gt;CourseList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="nn"&gt;CourseList&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="nc"&gt;CourseDetail&lt;/span&gt; &lt;span class="n"&gt;courseId&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="nn"&gt;Elmish&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="n"&gt;courseId&lt;/span&gt; &lt;span class="nn"&gt;CourseDetail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="nn"&gt;CourseDetail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="nn"&gt;CourseDetail&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since Elmish has termination support, the self-contained page would be coded to run clean up when it is switched out from view. There's no longer a need to wire it thru all the parent's functions and types as before.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What if I want communication back to the parent? For example, the parent needs to know when a cancel button is clicked on the child page to no longer show it. Easy. Make it an extra argument to the child's view function. Partially apply it.&lt;br&gt;
&lt;code&gt;let onClose () = dispatch Closed&lt;/code&gt;&lt;br&gt;
&lt;code&gt;Elmish.page ... (CourseDetail.view onClose)&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When I looked around, I realized this was not a particularly original idea. This is the common way &lt;code&gt;Fable.React.useElmish&lt;/code&gt; is employed, but being hosted directly from React JS components instead of parent MVU views. (Which also avoids all that parent-child wiring.) I also found common React libraries that tied the navigation route directly to the displayed React components, which only makes sense.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Composition through &lt;code&gt;view&lt;/code&gt; would also naturally fit building as dynamically loadable ESM modules instead of a fully bundled app.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This one, I haven't tried yet. The &lt;code&gt;Elmish.page&lt;/code&gt; function doesn't exist. I need to write that and figure out how to adapt it to &lt;code&gt;Elmish.mkProgram&lt;/code&gt;. It looks to be technically possible, and I am excited to try it the next time I build another MVU app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;MVU is an amazing pattern that helps me write highly maintainable and correct UIs. I love it. That's probably why the core pattern remains essentially the same even after my fixes.&lt;/p&gt;

&lt;p&gt;Yet, these changes have materially improved the experience for me. Maybe they could for you too.&lt;/p&gt;

</description>
      <category>elm</category>
      <category>fsharp</category>
      <category>ui</category>
      <category>mvu</category>
    </item>
    <item>
      <title>Event Sourcing - Stream Metadata</title>
      <dc:creator>Kasey Speakman</dc:creator>
      <pubDate>Sat, 29 Jan 2022 22:16:17 +0000</pubDate>
      <link>https://forem.com/kspeakman/event-sourcing-stream-metadata-429k</link>
      <guid>https://forem.com/kspeakman/event-sourcing-stream-metadata-429k</guid>
      <description>&lt;p&gt;&lt;em&gt;Discovering the need for stream metadata.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One of the best uses of Event Sourcing is to manage behaviors between entities, aka relationships. This is where all interesting things happen. For example, a product catalog is just a collection of data. It doesn't do anyone any good until those products are sold, shipped, restocked, etc. All of these processes occur between people or companies. They occur in relationships.&lt;/p&gt;

&lt;h2&gt;
  
  
  Relationships with Event Sourcing
&lt;/h2&gt;

&lt;p&gt;These relationships need to be reflected somewhere in the event stream. In our domain of safety training, the main activity we track is training. Which involves a student and a course. We use a Training ID (a UUID) to identify each training process. If we were creating relational tables, we would include foreign keys to Student ID and Course ID to track the entities involved. How do we express this in an event store?&lt;/p&gt;

&lt;h3&gt;
  
  
  In starter event
&lt;/h3&gt;

&lt;p&gt;In our first effort, we simply included reference IDs (Student ID and Course ID) in the starter event of our training process stream. For example, the starter event for a training process might be &lt;code&gt;RegistrationCreated&lt;/code&gt;. Storing the reference IDs in the starter event supports executing workflows and running queries. (The command and query APIs in CQRS parlance.) Both of these use a "replay" process that can keep the reference IDs from the first event for later use. This worked out great for our first event sourced application. But then we built an app for a more complex domain.&lt;/p&gt;

&lt;h3&gt;
  
  
  In key events
&lt;/h3&gt;

&lt;p&gt;We rearchitected one of our internal apps for the cloud. This time the domain is auditing. More specifically, verifying a vendor (contractor, supplier, etc) meets requirements before they are allowed to do work for a company.&lt;/p&gt;

&lt;p&gt;We started out placing the reference IDs only in the starter event. But this strategy was problematic for some kinds of event listeners -- like process managers -- and for building more complicated data models. In these cases, the "starting" event for the listener / data model is different from the starting event for the stream. We ended up solving this problem by just repeating the same reference IDs on these key events. The minor downside is storing duplicate data.&lt;/p&gt;

&lt;p&gt;As an alternative, we considered loading the reference IDs from an existing data model. But we decided this was a net loss. It would have created multiple degrees of coupling (server, code) between subsystems. And add complication to the infrastructure that would benefit relatively few cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stream metadata
&lt;/h3&gt;

&lt;p&gt;We ran into this again recently. Only this time I realized that these reference IDs (so far) are always static for a given stream. So they should be attached to the stream itself rather than specific events. They should be stream metadata. When the stream is "started", reference IDs are saved in stream metadata instead of on the initial event. When an event is loaded from the event store, the stream metadata can optionally be loaded too. So the reference IDs are available to listeners, regardless of which events they care about. No more repeating the same IDs in multiple events.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prior art
&lt;/h2&gt;

&lt;p&gt;I had seen this concept of stream metadata in my research on Event Store (Greg Young's open source product). But at the time I wasn't sure why I needed it. I have been burned before because I used tools incorrectly when I didn't understand them. So I did not include it in my Postgres event store implementation. In many ways I feel like I am just rediscovering things that Greg Young figured out years ago. Then documenting them in a way that my past self might understand. 😄&lt;/p&gt;

</description>
      <category>eventsourcing</category>
    </item>
    <item>
      <title>Shareholder Value: the Other Pandemic</title>
      <dc:creator>Kasey Speakman</dc:creator>
      <pubDate>Sun, 09 Jan 2022 12:50:50 +0000</pubDate>
      <link>https://forem.com/kspeakman/shareholder-value-the-other-pandemic-5816</link>
      <guid>https://forem.com/kspeakman/shareholder-value-the-other-pandemic-5816</guid>
      <description>&lt;p&gt;This post was inspired by Nvidia's recent announcement of the RTX 3090 TI. With its expected MSRP of $2000+, this graphics card comes at a time when many PC builders cannot find graphics cards at all. Most of them go into the hands of crypto miners or are flipped by scalpers at twice the MSRP. This is the latest example of Nvidia getting in on the scalping action too. By repackaging a lower-priced product line as a new SKU to inflate the price. Justifying it with a negligible performance increase. Then reducing or stopping production of the cheaper version. During these global hard times, Nvidia is shipping &lt;a href="https://www.tomshardware.com/news/jpr-q3-2021-desktop-discrete-gpu-shipments"&gt;12% more GPUs&lt;/a&gt; while making &lt;a href="https://nvidianews.nvidia.com/news/nvidia-announces-financial-results-for-third-quarter-fiscal-2022"&gt;84% more net income&lt;/a&gt; year-over-year. By squeezing it out of customers.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Boring Dystopia
&lt;/h2&gt;

&lt;p&gt;But none of this is new or surprising. (Or even among the &lt;a href="https://en.wikipedia.org/wiki/Bausch_Health#2015_drug_price_inflation_controversy"&gt;worst examples&lt;/a&gt;.) It's one of the most cliche things of our time: the corporate zeitgeist of maximizing shareholder value. This philosophy seems to be accepted at every level as the way business is supposed to work.&lt;/p&gt;

&lt;p&gt;"Jack CEO, if you can increase profitability 2% next year, the board will give you a 2 million dollar bonus." The CEO slashes employee benefits, gouges customers, and eliminates "waste" such as R&amp;amp;D spending. Profits and stock prices go up, making the investors hundreds of millions. The financial sector calls Jack CEO a hero. He is in higher demand because of this "success". So he'll probably be long gone when the company's market position enters the nose dive he setup. Employee hiring/retention problems and customer dissatisfaction mount as toxicity increases. The company fails to stay relevant without innovation from R&amp;amp;D. If the board keeps trying to find a repeat of Jack CEO's "success" the company might even get its own documentary. Like Enron or USA banks.&lt;/p&gt;

&lt;p&gt;The story may play out in different ways, but this one illustrates the logical conclusion of focusing on shareholder value. Investors and executives get richer by sacrificing the company's future. But the giving end of the sacrifice is done by employees and customers. It's no wonder the wealth gap keeps getting wider.&lt;/p&gt;

&lt;h2&gt;
  
  
  Allow me to Translate
&lt;/h2&gt;

&lt;p&gt;"Shareholder value" is just a dressed up word for greed. And it is enshrined in our global economic systems. Profit (or the potential thereof) is &lt;em&gt;the&lt;/em&gt; measure of company health in financial markets. CEOs are required to prioritize it above all. Failing to do so opens the door to lawsuits. For example...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Having chosen a for-profit corporate form, the craigslist directors are bound by the fiduciary duties and standards that accompany that form. Those standards include acting to promote the value of the corporation for the benefit of its stockholders.&lt;br&gt;
...&lt;br&gt;
Thus, I cannot accept as valid ... a corporate policy that specifically, clearly, and admittedly seeks not to maximize the economic value of a for-profit Delaware corporation for the benefit of its stockholders...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://h2o.law.harvard.edu/cases/3472"&gt;eBay vs Craigslist&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;This lawsuit was about a few different things. The excerpt is about Craigslist creating policies designed to prevent eBay from destroying the company's culture to chase profit. Here the judge's interpretation of the law considers this a breach of duty. As a non-lizard-person I find this horrifying.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Now?
&lt;/h2&gt;

&lt;p&gt;I don't have any easy answers. My first reaction to hearing the Nvidia announcement was: I wish more product companies were non-profits. Only this would not be a direct solution. Investor money allows companies to expand and innovate at a more rapid pace. (Initially. Before it's ultimately enslaved to profit-seeking.) Whereas non-profits have to grow very carefully. There's no extra profit to reinvest. And no investor help for expansion shortfalls. A non-profit Amazon would have failed immediately instead of "almost" being a long-term Ponzi scheme.&lt;/p&gt;

&lt;p&gt;Many jurisdictions have legal frameworks for &lt;a href="https://en.wikipedia.org/wiki/Benefit_corporation"&gt;Benefit&lt;/a&gt; or Social Purpose corporations. These enable other goals (in addition to profit) to be recognized by civil law. So the company could take investors but (maybe) still win a lawsuit for refusing to treat employees like test subjects.&lt;/p&gt;

&lt;p&gt;The main challenge with these alternatives is that they are likely to struggle to be successful in the market. It is way more effort to achieve responsibility in multiple dimensions at once. Effort that increases the bigger the company gets. Most consumers will appreciate your company's approach but actually buy the cheaper or sooner-to-market option. Or in today's world, the in-stock option.&lt;/p&gt;




&lt;p&gt;I waited in line 5 hours for my local Best Buy to open. Unfortunately most people waited at least 9 in their tents and RVs. By the time it was my turn to pick a graphics card, the only choices left were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an EVGA-price-gouged 3060 TI

&lt;ul&gt;
&lt;li&gt;only about 60% faster than my 7-year old 980 TI&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;a lobotomy-required EVGA 3080 TI for almost $1500&lt;/li&gt;
&lt;li&gt;a white hot fury-inducing Nvidia 3080 TI at $1200&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I decided to leave without buying anything. I began a lifetime boycott of Nvidia products. I waited 3 years for shortages to subside. Then bought a 9800 XT direct from AMD, because I had to boycott all their partner brands for price-gouging too.&lt;/p&gt;

&lt;p&gt;Except that's not what happened. I spent money I didn't have to get the Nvidia 3080 TI.&lt;/p&gt;

&lt;p&gt;I am part of the problem. But trying to find the path to change.&lt;/p&gt;

</description>
      <category>startup</category>
      <category>management</category>
      <category>culture</category>
    </item>
    <item>
      <title>Desktop apps in HTML and F#</title>
      <dc:creator>Kasey Speakman</dc:creator>
      <pubDate>Mon, 30 Aug 2021 22:21:20 +0000</pubDate>
      <link>https://forem.com/kspeakman/desktop-apps-in-html-and-f-3oj4</link>
      <guid>https://forem.com/kspeakman/desktop-apps-in-html-and-f-3oj4</guid>
      <description>&lt;p&gt;&lt;em&gt;I created a starter desktop app using HTML/CSS/F# for the front end and F# .NET for the back end. All hosted in WPF.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This post mostly covers the "why". Hit the &lt;a href="https://github.com/kspeakman/FSharpHtmlDesktopApp" rel="noopener noreferrer"&gt;repo&lt;/a&gt; if you just care about "how".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Developers at my company are mostly coding for the web. But once in a while we need to create a Windows desktop app. Because some situations require direct access to hardware and running software. But a big challenge with desktop apps is their UI tech.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why web UI?
&lt;/h2&gt;

&lt;p&gt;Our devs have a lot of skill investment in web tech. And I don't mean React and Webpack -- although we know a little about those. I'm talking about the open standards of HTML, CSS, and Javascript. Because of standardization I can use web tech to make a consistent-looking UI, no matter what device is consuming it.&lt;/p&gt;

&lt;p&gt;Meanwhile desktop UI techs tend to be opinionated and proprietary. We develop for desktop infrequently, so non-transferrable learning investments into desktop UI tech is a hard sell.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which web UI?
&lt;/h2&gt;

&lt;p&gt;It has been possible to use web tech in Windows desktop apps for a while. Problem was, the solutions were full of compromises.&lt;/p&gt;

&lt;p&gt;UWP, for example, allows you to build apps using web tech. But it runs sandboxed, making it hard to do the one job it needs to do: direct access. WebBrowser and WebView have also existed for a while. They are embeddable browsers for WinForms/WPF apps. But they use IE or EdgeHTML for rendering. Need I say more?&lt;/p&gt;

&lt;h3&gt;
  
  
  Why not Electron?
&lt;/h3&gt;

&lt;p&gt;The elephant in the room is Electron. The main downside for us is the Node.js back-end. Some of our vendors do not provide libraries for it. And even if it can be awkwardly made to work using unsupported libraries, Node requires us to work in Javascript.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Electron's cross-platform support is probably its biggest benefit... but we don't need that.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yes, earlier I did list Javascript as a positive thing for web tech. I do like the standardized browser APIs available thru JS -- Media, Storage, Intl, etc. But let's be honest, JS also has footguns baked into the standard. And I strongly prefer to use something else: F# on .NET.&lt;/p&gt;

&lt;h3&gt;
  
  
  WebView2
&lt;/h3&gt;

&lt;p&gt;WebView2 is an embeddable browser for WinForms/WPF like WebView before it. WebView2 uses Chromium-based Edge for rendering. So unlike its predecessor, WebView2 keeps evolving with the web standards. And I get to use F# on .NET.&lt;/p&gt;

&lt;p&gt;We have actually used WebView2 since shortly after its release. We used it to show HTML5 video content in a computer lab. It was a rush job and WebView2 was only a month old at the time. I wasn't ready to trust it, much less consider it for other uses. And I forgot about it.&lt;/p&gt;

&lt;p&gt;Then earlier this year, Microsoft announced &lt;a href="https://devblogs.microsoft.com/dotnet/announcing-net-6-preview-1/#blazor-desktop-apps" rel="noopener noreferrer"&gt;Blazor Desktop&lt;/a&gt; based on WebView2. And then I found out Microsoft Teams is &lt;a href="https://twitter.com/rishmsft/status/1408085784016539653" rel="noopener noreferrer"&gt;ditching Electron for WebView2&lt;/a&gt;. This got me to take another look at it. One thing led to another and over the weekend I created a working demo: a WPF app with an F# web UI and F# .NET backend API.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does it work?
&lt;/h2&gt;

&lt;p&gt;The UI is a webpack-built JS app just like you would expect. It uses F# and Elmish for a functional (as in functional programming) UI. The F# code gets transpiled to JS and uses React for DOM rendering.&lt;/p&gt;

&lt;p&gt;The "backend" API is an F# library. In this starter app, the backend is very simple. So there is nothing interesting to demonstrate yet.&lt;/p&gt;

&lt;p&gt;A messaging library is shared between the front and back. It defines all the requests and responses that can pass between them. &lt;/p&gt;

&lt;p&gt;The host application is WPF. WinForms would work too. Either way there is not much to it. The only control is the WebView2 control. And about 40 lines of wiring code.&lt;/p&gt;

&lt;p&gt;For communication between the JS front and .NET back, I used WebView2's string-based messaging. Each side gets some functions to send/receive messages. Types from the messaging library are converted to JSON strings for transport.&lt;/p&gt;

&lt;h2&gt;
  
  
  The repo
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/kspeakman" rel="noopener noreferrer"&gt;
        kspeakman
      &lt;/a&gt; / &lt;a href="https://github.com/kspeakman/FSharpHtmlDesktopApp" rel="noopener noreferrer"&gt;
        FSharpHtmlDesktopApp
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A starter desktop app which uses HTML and F# for the UI and F# .NET for the API.
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;This was a fun weekend project. It has a lot of promise to make desktop apps more manageable for our dev team. I'll try it out this coming month and let you know how it goes. For now, let me know what you think about making desktop apps with web tech and F#.&lt;/p&gt;

</description>
      <category>fsharp</category>
      <category>wpf</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Readable vs Workable Code</title>
      <dc:creator>Kasey Speakman</dc:creator>
      <pubDate>Thu, 05 Aug 2021 22:32:54 +0000</pubDate>
      <link>https://forem.com/kspeakman/readable-vs-workable-code-2mla</link>
      <guid>https://forem.com/kspeakman/readable-vs-workable-code-2mla</guid>
      <description>&lt;p&gt;&lt;em&gt;Balancing some code to be readable yet workable.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Today I was coding and decided to refactor some older code. In particular, this code is handling navigation updates in a Fable Elmish app in F#.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;routeToPage&lt;/span&gt; &lt;span class="n"&gt;routeOpt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;routeOpt&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;None&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;effects&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;OpsUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="nc"&gt;None&lt;/span&gt;
        &lt;span class="nc"&gt;OpsUserSearch&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;effects&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Some&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
        &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;ToOpsUserSearch&lt;/span&gt; &lt;span class="n"&gt;searchOpt&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;effects&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;OpsUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Search&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;searchOpt&lt;/span&gt;
            &lt;span class="nc"&gt;OpsUserSearch&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;effects&lt;/span&gt;
        &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;ToOpsUserAdd&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;effects&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;OpsUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt;
            &lt;span class="nc"&gt;OpsUserAdd&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;effects&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;This function has many route cases but only two are shown here.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Now to refactor
&lt;/h2&gt;

&lt;p&gt;First I started to remove duplication. Starting at the innermost duplication, notice how &lt;code&gt;page, effect&lt;/code&gt; is repeated twice. We can remove this duplication with a helper function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;Tuple&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;mapFirst&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;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="p"&gt;)&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;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;routeToPage&lt;/span&gt; &lt;span class="n"&gt;routeOpt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;routeOpt&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;None&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="nn"&gt;OpsUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="nc"&gt;None&lt;/span&gt;
        &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Tuple&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapFirst&lt;/span&gt; &lt;span class="nc"&gt;OpsUserSearch&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Some&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
        &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;ToOpsUserSearch&lt;/span&gt; &lt;span class="n"&gt;searchOpt&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="nn"&gt;OpsUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Search&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;searchOpt&lt;/span&gt;
            &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Tuple&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapFirst&lt;/span&gt; &lt;span class="nc"&gt;OpsUserSearch&lt;/span&gt;
        &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;ToOpsUserAdd&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="nn"&gt;OpsUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Tuple&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapFirst&lt;/span&gt; &lt;span class="nc"&gt;OpsUserAdd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;mapFirst&lt;/code&gt; helper function is not specific to this code. It could be used on any tuple. So I made a small Tuple module that I can extract later if needed.&lt;/p&gt;

&lt;p&gt;Next, all of these cases do the same thing, but using a different page tag, init function, and init argument. So we can make a helper to parameterize those.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;Tuple&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;mapFirst&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;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="p"&gt;)&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;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;routeToPage&lt;/span&gt; &lt;span class="n"&gt;routeOpt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;inline&lt;/span&gt; &lt;span class="n"&gt;initPage&lt;/span&gt; &lt;span class="n"&gt;pageTag&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="n"&gt;initArg&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;initArg&lt;/span&gt;
        &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Tuple&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapFirst&lt;/span&gt; &lt;span class="n"&gt;pageTag&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;routeOpt&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;None&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;initPage&lt;/span&gt; &lt;span class="nc"&gt;OpsUserSearch&lt;/span&gt; &lt;span class="nn"&gt;OpsUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="nc"&gt;None&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Some&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
        &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;ToOpsUserSearch&lt;/span&gt; &lt;span class="n"&gt;searchOpt&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;initPage&lt;/span&gt; &lt;span class="nc"&gt;OpsUserSearch&lt;/span&gt; &lt;span class="nn"&gt;OpsUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Search&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;searchOpt&lt;/span&gt;
        &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;ToOpsUserAdd&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;initPage&lt;/span&gt; &lt;span class="nc"&gt;OpsUserAdd&lt;/span&gt; &lt;span class="nn"&gt;OpsUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;I used &lt;code&gt;inline&lt;/code&gt; here as micro optimization. You can omit/ignore that as it won't make any logical difference to the code. Generally it is best to avoid using it in other places unless you know how it will affect execution.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So the cases are down to one-liners. We can further split the main cases from the default one, into a separate function. And reuse that function to also initialize the default case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;Tuple&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;mapFirst&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;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="p"&gt;)&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;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;initRoute&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;inline&lt;/span&gt; &lt;span class="n"&gt;initPage&lt;/span&gt; &lt;span class="n"&gt;pageTag&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="n"&gt;initArg&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;initArg&lt;/span&gt;
        &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Tuple&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapFirst&lt;/span&gt; &lt;span class="n"&gt;pageTag&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;ToOpsUserSearch&lt;/span&gt; &lt;span class="n"&gt;searchOpt&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;initPage&lt;/span&gt; &lt;span class="nc"&gt;OpsUserSearch&lt;/span&gt; &lt;span class="nn"&gt;OpsUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Search&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;searchOpt&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;ToOpsUserAdd&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;initPage&lt;/span&gt; &lt;span class="nc"&gt;OpsUserAdd&lt;/span&gt; &lt;span class="nn"&gt;OpsUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;routeToPage&lt;/span&gt; &lt;span class="n"&gt;routeOpt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;routeOpt&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;None&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;initRoute&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ToOpsUserSearch&lt;/span&gt; &lt;span class="nc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Some&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;initRoute&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here &lt;code&gt;initRoute&lt;/code&gt; will be the primary area that devs will modify going forward. We can also look at Option functions to further reduce duplication in &lt;code&gt;routeToPage&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;Tuple&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;mapFirst&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;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="p"&gt;)&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;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;initRoute&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;inline&lt;/span&gt; &lt;span class="n"&gt;initPage&lt;/span&gt; &lt;span class="n"&gt;pageTag&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="n"&gt;initArg&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;initArg&lt;/span&gt;
        &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Tuple&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapFirst&lt;/span&gt; &lt;span class="n"&gt;pageTag&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;ToOpsUserSearch&lt;/span&gt; &lt;span class="n"&gt;searchOpt&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;initPage&lt;/span&gt; &lt;span class="nc"&gt;OpsUserSearch&lt;/span&gt; &lt;span class="nn"&gt;OpsUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Search&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;searchOpt&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;ToOpsUserAdd&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;initPage&lt;/span&gt; &lt;span class="nc"&gt;OpsUserAdd&lt;/span&gt; &lt;span class="nn"&gt;OpsUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;routeToPage&lt;/span&gt; &lt;span class="n"&gt;routeOpt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;routeOpt&lt;/span&gt;
    &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;defaultValue&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ToOpsUserSearch&lt;/span&gt; &lt;span class="nc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;initRoute&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To remove every ounce of fat we can use point-free style. This necessitates extracting &lt;code&gt;initPage&lt;/code&gt; outside of &lt;code&gt;initRoute&lt;/code&gt;. Consequently, I'll remove &lt;code&gt;inline&lt;/code&gt; so if anyone later tries to reuse it they don't get unexpected performance issues. I will also separate out the default route as its own value. This extracts out the only thing in &lt;code&gt;routeToPage&lt;/code&gt; that is likely to change over time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;Tuple&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;mapFirst&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;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="p"&gt;)&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;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;initPage&lt;/span&gt; &lt;span class="n"&gt;pageTag&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="n"&gt;initArg&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;initArg&lt;/span&gt;
    &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Tuple&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapFirst&lt;/span&gt; &lt;span class="n"&gt;pageTag&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;initRoute&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;ToOpsUserSearch&lt;/span&gt; &lt;span class="n"&gt;searchOpt&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;initPage&lt;/span&gt; &lt;span class="nc"&gt;OpsUserSearch&lt;/span&gt; &lt;span class="nn"&gt;OpsUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Search&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;searchOpt&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;ToOpsUserAdd&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;initPage&lt;/span&gt; &lt;span class="nc"&gt;OpsUserAdd&lt;/span&gt; &lt;span class="nn"&gt;OpsUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;defaultRoute&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="nc"&gt;ToOpsUserSearch&lt;/span&gt; &lt;span class="nc"&gt;None&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;routeToPage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="nn"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;defaultValue&lt;/span&gt; &lt;span class="n"&gt;defaultRoute&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;initRoute&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Is this better?
&lt;/h2&gt;

&lt;p&gt;So at this point I have removed all the duplication I can find.  Now it's time to look at the bigger picture. How clear is this code? The experienced among you might say it is quite clear. But what if you were new to coding? How many different types of syntax must be mastered? How many layers must a dev trace through to understand &lt;code&gt;routeToPage&lt;/code&gt;? Are there any syntaxes that are especially difficult for new devs to understand?&lt;/p&gt;

&lt;p&gt;I like to consider readability from a new dev's perspective. This makes the code base more understandable to all readers including future-me. And pragmatically it allows more flexibility. I don't just have to hire experienced devs or devs with a specific background. Devs of all experience levels and backgrounds can transition between projects in less time.&lt;/p&gt;

&lt;p&gt;From this perspective the code has a number of aspects that I think harms general readability. Here is what I will likely commit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;initRoute&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;inline&lt;/span&gt; &lt;span class="n"&gt;initPage&lt;/span&gt; &lt;span class="n"&gt;pageTag&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="n"&gt;initArg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;effects&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;initArg&lt;/span&gt;
        &lt;span class="n"&gt;pageTag&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;effects&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;ToOpsUserSearch&lt;/span&gt; &lt;span class="n"&gt;searchOpt&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;initPage&lt;/span&gt; &lt;span class="nc"&gt;OpsUserSearch&lt;/span&gt; &lt;span class="nn"&gt;OpsUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Search&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;searchOpt&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;ToOpsUserAdd&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;initPage&lt;/span&gt; &lt;span class="nc"&gt;OpsUserAdd&lt;/span&gt; &lt;span class="nn"&gt;OpsUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;defaultRoute&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="nc"&gt;ToOpsUserSearch&lt;/span&gt; &lt;span class="nc"&gt;None&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;routeToPage&lt;/span&gt; &lt;span class="n"&gt;routeOpt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;routeOpt&lt;/span&gt; &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;defaultValue&lt;/span&gt; &lt;span class="n"&gt;defaultRoute&lt;/span&gt;
    &lt;span class="n"&gt;initRoute&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One challenge to readability is point-free notation. A lot of functional programmers like it. But it is not obvious to read at first glance. The point-free functions look like value assignments. You have to look inside the code block for specific signs that is point-free, like &lt;code&gt;function&lt;/code&gt; or &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;. Then the implicit parameter can leave a bit of doubt in the mind of a new programmer as to what is actually happening. In all I consider point-free to be more of an optimization than an abstraction. And it does not add sufficient value here for the duplication it optimizes away.&lt;/p&gt;

&lt;p&gt;Next I considered &lt;code&gt;Tuple.mapFirst&lt;/code&gt;. It was only used in one place. It results in more code than not having it. Worst of all, it makes &lt;code&gt;initPage&lt;/code&gt; less readable. Because when you dig into &lt;code&gt;mapFirst&lt;/code&gt;, you have to context-switch into the mechanics of a tuple, away from the problem you were originally trying to understand. For this reason, even if the function was included in the base F# libs I would avoid using it here for clarity.&lt;/p&gt;

&lt;p&gt;I'm not entirely convinced of the value of &lt;code&gt;initPage&lt;/code&gt;, but I will keep it for now. It seems readable. It saves a little code in the place where devs will most commonly be working. And understanding it is only a one-level deep journey.&lt;/p&gt;

&lt;p&gt;I could have written &lt;code&gt;routeToPage&lt;/code&gt; with all pipes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;routeToPage&lt;/span&gt; &lt;span class="n"&gt;routeOpt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;routeOpt&lt;/span&gt;
    &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;defaultValue&lt;/span&gt; &lt;span class="n"&gt;defaultRoute&lt;/span&gt;
    &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;initRoute&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But I think the way I coded it is more clear. Specifically, the first line is more obviously converting an optional route to a non-optional one. So even if the dev is new enough that they haven't encountered &lt;code&gt;Option.defaultValue&lt;/code&gt;, the function is still understandable at a surface level.&lt;/p&gt;

&lt;p&gt;Quite frankly, the end result of this process does not seem any more readable than the original. This speaks volumes about the value of explicitness even if it means duplication. But the result filters down the likely-to-change code into a specific function. And it provides a tool for reuse there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;Good code is like good literature: it forms a tangible picture in your mind. But it also is like a good workbench: it is easy to work on. It would be nice if there was a single tactic -- such as the Don't Repeat Yourself principle -- that could accomplish these goals. But I haven't found it yet. Instead good code requires trying different things and evaluating what works.&lt;/p&gt;

&lt;p&gt;I shared my thoughts on balancing this code. How do you balance your code?&lt;/p&gt;

</description>
      <category>fsharp</category>
      <category>mvu</category>
    </item>
    <item>
      <title>Organizing MVU Projects</title>
      <dc:creator>Kasey Speakman</dc:creator>
      <pubDate>Tue, 29 Dec 2020 17:28:00 +0000</pubDate>
      <link>https://forem.com/kspeakman/organizing-mvu-projects-2218</link>
      <guid>https://forem.com/kspeakman/organizing-mvu-projects-2218</guid>
      <description>&lt;p&gt;&lt;em&gt;Strategies for organizing large Model-View-Update projects that have worked for us.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We have been working on functional UI projects using the MVU pattern for 4 years now. We first started using this pattern with Elm. Then we switched to full stack F#, using the Elmish library for MVU. We evolved our approach to MVU several times along the way. This post details some practices we learned.&lt;/p&gt;

&lt;p&gt;This post assumes some experience with MVU. I do not mention the &lt;code&gt;view&lt;/code&gt; function very much since we do not usually find it a challenge to organize. A &lt;code&gt;view&lt;/code&gt; function for a particular page usually resides near that page's &lt;code&gt;update&lt;/code&gt; function.&lt;/p&gt;

&lt;h1&gt;
  
  
  Modified TEA for UI state
&lt;/h1&gt;

&lt;p&gt;The Elm Architecture (TEA) is one of the early ideas behind Elm. It defined a nested hierarchy of Msg, Model, Update, and View. But it was disavowed by Elm because it has a fatal flaw. TEA required a lot of laborious boilerplate to convert Msg between parents and children. It also made simultaneous active pages very difficult. We went away from it. But as our programs got larger, we found ourselves with gigantic Msg and update files, requiring us to use Ctrl-F to find things inside them. Basically everything was in a couple of huge piles of code instead of being organized for quick human access.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Quick stats&lt;/strong&gt;&lt;br&gt;
Latest project's UI code (F#)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Largest app - 35k lines of code&lt;/li&gt;
&lt;li&gt;All apps - 65k lines of code&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;It became clear that we needed some organization. TEA is really just grouping all the things for a single page together in a module. This idea makes a lot of sense, so we went back to that for everything except Msg. All Msgs are declared first in their entirety, centrally and separately from the page parts. &lt;strong&gt;This means that every page module can directly send any messages they want, even to other pages.&lt;/strong&gt; This got rid of all that Msg mapping and OutMsg patterns plaguing TEA.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update 6 Jan 2021: I just checked out Elm's current &lt;a href="https://guide.elm-lang.org/webapps/structure.html"&gt;documentation&lt;/a&gt; and it now mentions only Model, init, view, and update in page modules. So they must have come to a similar conclusion. When we were in the Elm community TEA included Msg but became disavowed because of its shortcomings, and the documentation about it was removed. Then there was a propaganda blitz for flat Msg. We switched away from Elm before this latest iteration on Elm app structure.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In our modified TEA, Msg can still have a hierarchical organization pattern as needed. So we can place a particular set of messages in their own file.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Remember that organization works for you, not the other way around. Every time you add a level of organization to your project, you also add more overhead (structure). It is better to keep things flat as much as possible. And only increase the degree of organization when you discover it is needed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;for UI state&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;An important subtlety in the section title is "Modified TEA &lt;em&gt;&lt;strong&gt;for UI state&lt;/strong&gt;&lt;/em&gt;". Often an app will have data that it is tracking independent of whatever the UI elements are doing. This kind of data should be placed outside the UI state. Everything in the UI state should be viewed as just what is currently being displayed, available to throw away at any time.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It is necessary to point this out because our industry is so accustomed to object-oriented UIs. There the ownership of state is distributed among many different objects. This can lead MVU users to try to make UI pages "own" specific kinds of data on behalf of other pages. But with MVU, pages only represent what is displayed on the screen. This is the way.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This has a couple of major advantages. It eliminates a common source of bugs: two different pages using shared state inconsistently. It also eliminates the need to manage the UI state's lifecycle, since it is part of the page lifecycle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// changing pages&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;pageData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmds&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;Page&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Home&lt;/span&gt; &lt;span class="n"&gt;pageData&lt;/span&gt; &lt;span class="o"&gt;},&lt;/span&gt; &lt;span class="n"&gt;cmds&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above example, the important thing here is what is missing. There is no code to check the previous page. Or clean up shared state. It is not needed. Any data that the page needs it will request or create on &lt;code&gt;init&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Independent data
&lt;/h3&gt;

&lt;p&gt;What if pages need to display or update that shared, independent data? The best answer really depends on the situation, but here are some approaches. If the independent data is read-only reference data, you can pass it as an argument to the page's &lt;code&gt;init&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For a more robust approach, treat it as an external integration. This works for cases where the state isn't static or pages can request changes to it. For this, provide Cmds to make these requests. Also Msg cases to process changes in an MVU style. This is a headless MVU service pattern, where the independent data is the service state.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does "independent data" look like?
&lt;/h3&gt;

&lt;p&gt;I keep using this term &lt;em&gt;independent data&lt;/em&gt; like it is something special. It is not. To demonstrate how "not a big thing" it is, here is an example Model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Model&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;ApiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="nc"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Page&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;ApiKey&lt;/code&gt; is what I am calling independent data. And &lt;code&gt;Page&lt;/code&gt; is a piece of UI state. The app model could contain multiples of either kind. The &lt;code&gt;Msg&lt;/code&gt; for this app might contain a set of messages to manipulate either or both parts of Model. It depends entirely on what the app needs.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;code&gt;update&lt;/code&gt; refinements
&lt;/h1&gt;

&lt;p&gt;UI states are just data that represents what is on the screen. So in them we make extensive use of both records and (discriminated) unions. Records are what most people are most accustomed to, similar to structs or DTOs. Unions like you will see below are used when the state can only be one of multiple possibilities. Here is a typical example of a UI state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Page&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Home&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nn"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Model&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;CourseSearch&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Model&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;CourseDetail&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Model&lt;/span&gt;
  &lt;span class="c1"&gt;// and so forth&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is basically spelling out that &lt;code&gt;Page&lt;/code&gt; can be in one of these states. And then each page also has its own inner state (for example &lt;code&gt;Home.Model&lt;/code&gt;), which is also some combination of records and unions.&lt;/p&gt;

&lt;p&gt;That is well and good, but there are a couple of practical issues when we go to use this in &lt;code&gt;update&lt;/code&gt;. With this structure we have to dig the page data out of the union type. That is Problem #1. Separately, since &lt;code&gt;Msg&lt;/code&gt; is an app-wide type -- where any page can send any message -- now it is possible to send a Msg to a page which is not active. That is Problem #2.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Technically #2 was always possible. A delayed API operation and an impatient user can lead to receiving a response for page which is no longer active.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To resolve both of these problems, we went to an update style that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;let&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="p"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Home&lt;/span&gt; &lt;span class="n"&gt;pageData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;HomeMsg&lt;/span&gt; &lt;span class="n"&gt;pageMsg&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;pageData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmds&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Pages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Home&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;pageMsg&lt;/span&gt; &lt;span class="n"&gt;pageData&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;Page&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Home&lt;/span&gt; &lt;span class="n"&gt;pageData&lt;/span&gt; &lt;span class="o"&gt;},&lt;/span&gt; &lt;span class="n"&gt;cmds&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This serves to unwrap the page and message data from their respective union types. While also verifying we are handling a message appropriate for the UI state.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You might also notice this has a strong similarity to something else -- a state machine. At its core, a state machine is just a set of states and the valid transitions between them. This match is spelling out the valid transitions (messages) for each UI state.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We also do something that many of you FPers will not like. We use a catch-all match for any unmatched state transitions. This allows us to specify only the scenarios that we want to handle and ignore the rest.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;let&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="p"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="k"&gt;with&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="p"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;// no changes&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a classic &lt;a href="https://en.wikipedia.org/wiki/Expression_problem"&gt;expression problem&lt;/a&gt; trade-off of extensibility vs completeness. With the underscore match we are choosing extensibility. By choosing this we lose the advantage of the compiler telling us of intended matches that we forgot. But we also do not get the multitude more unintended matches that we want to ignore. We believe our choice makes sense under the circumstances. &lt;em&gt;Logging the catch-all case provides enough of a hint when we forget to add an intended case.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Routing and Navigation
&lt;/h1&gt;

&lt;p&gt;A "route" is simply enough data to load a specific page. In MVU, pages are constructed by their &lt;code&gt;init&lt;/code&gt; function. Therefore a route is just the arguments to the page's init function, along with a tag to indicate the page. It is so much simpler to think about it this way, that I eventually started naming this InitArgs in new projects instead of Route. And this can be used practically to package up all the arguments to the &lt;code&gt;init&lt;/code&gt; function. Here is an example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// PageTypes\Course\Detail.fs&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Detail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;InitArgs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;CourseId&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="c1"&gt;// AppTypes.fs&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;InitArgs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;HomeInit&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;CourseDetailInit&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;InitArgs&lt;/span&gt;

&lt;span class="c1"&gt;// PageFns\Course\Detail.fs&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Detail&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="n"&gt;initArgs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;CourseId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;initArgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CourseId&lt;/span&gt;
      &lt;span class="nc"&gt;Data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Loading&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="nc"&gt;LoadCourseDetail&lt;/span&gt; &lt;span class="n"&gt;initArgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CourseId&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;I'll cover the folder organization of the project later.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Switching pages then is a matter of adding a Msg case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// AppTypes.fs&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Msg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;SwitchPage&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nc"&gt;InitArgs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And handling page changes looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;switchPage&lt;/span&gt; &lt;span class="n"&gt;initArgs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;initArgs&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;HomeInit&lt;/span&gt; &lt;span class="n"&gt;pageArgs&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;pageData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmds&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Home&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;pageArgs&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;Page&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Home&lt;/span&gt; &lt;span class="n"&gt;pageData&lt;/span&gt; &lt;span class="o"&gt;},&lt;/span&gt; &lt;span class="n"&gt;cmds&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;CourseDetailInit&lt;/span&gt; &lt;span class="n"&gt;pageArgs&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;pageData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmds&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Detail&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;pageArgs&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;Page&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CourseDetail&lt;/span&gt; &lt;span class="n"&gt;pageData&lt;/span&gt; &lt;span class="o"&gt;},&lt;/span&gt; &lt;span class="n"&gt;cmds&lt;/span&gt;

&lt;span class="k"&gt;let&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="p"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="o"&gt;_,&lt;/span&gt; &lt;span class="nc"&gt;SwitchPage&lt;/span&gt; &lt;span class="n"&gt;initArgs&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;switchPage&lt;/span&gt; &lt;span class="n"&gt;initArgs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Like Msg, InitArgs must be centralized and declared ahead of time so that any page has the ability switch to any other page. Or to calculate a URL so the user can switch pages with a link. Speaking of...&lt;/p&gt;

&lt;h3&gt;
  
  
  adding URL navigation
&lt;/h3&gt;

&lt;p&gt;The main code you have to fill in for this involves converting InitArgs &lt;code&gt;toUrl&lt;/code&gt; and &lt;code&gt;fromUrl&lt;/code&gt;. Here is an example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;InitArgs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;

  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;toUrl&lt;/span&gt; &lt;span class="n"&gt;initArgs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;initArgs&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;HomeInit&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="s2"&gt;"#/home"&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;CourseDetailInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;CourseId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;courseId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="n"&gt;sprintf&lt;/span&gt; &lt;span class="s2"&gt;"#/course/%i"&lt;/span&gt; &lt;span class="n"&gt;courseId&lt;/span&gt;

  &lt;span class="c1"&gt;// using Elmish.UrlParser&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;parseInitArgs&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;oneOf&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="nc"&gt;HomeInit&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="s2"&gt;"home"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;/&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;unitArg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="n"&gt;courseId&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
               &lt;span class="nc"&gt;CourseDetailInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;CourseId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;courseId&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="s2"&gt;"course"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;/&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;

  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;fromUrl&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;parseHash&lt;/span&gt; &lt;span class="n"&gt;parseInitArgs&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;toUrl&lt;/code&gt; is mainly for your pages to generate navigable links to other pages. It is optimal to change pages with links when possible, so that the browser's back/forward buttons work as the user expects. Sometimes it is also desirable to manually update the URL from your app when the page options change significantly so that this URL gets added to history.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fromUrl&lt;/code&gt; is used in navigation events to pick the page to display. The function will also get used on app init, in case the user came to your app from a bookmark at a specific page. All that remains is to wire up those cases. MVU libraries have built-in integration for this. It can be done manually as well. Pass the location into app init to handle it there. And add an event listener for location changes, tag it in a Msg case like LocationChanged and dispatch it. Then handle it as another case of &lt;code&gt;update&lt;/code&gt; where you call &lt;code&gt;fromUrl&lt;/code&gt; and &lt;code&gt;switchPage&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Side effects
&lt;/h1&gt;

&lt;p&gt;A main emphasis of functional programming is pure functions. The complement of this emphasis is that side effects become an explicit concept. In MVU, explicit side effects are represented by &lt;code&gt;Cmd&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However Cmd implementations have some less-than-ideal characteristics. In Elm they were very painful to extend with your own functionality (ports). In F# Elmish, Cmd simply defines a function signature which enables you to do whatever you want and dispatch Msgs to report the results back. This is about the most extensible you can get.&lt;/p&gt;

&lt;p&gt;However I saw some opportunities for improvement. First it seemed like a mix of concerns to have &lt;code&gt;update&lt;/code&gt; potentially creating a side effect function. Secondly it left &lt;code&gt;update&lt;/code&gt; less testable than it could be. You can test the model easily, but it is quite invasive to test whether the correct side effects were requested. The ideal would be for Cmd to be just data representing the side effect and its necessary arguments.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Effect&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;I called the type &lt;code&gt;Effect&lt;/code&gt;. It is entirely user-defined. &lt;em&gt;I did not want to call it &lt;code&gt;Cmd&lt;/code&gt; since Elmish already uses this name.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Effect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;GetCurrentDateTime&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;SendApiRequest&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nc"&gt;ApiRequest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;update&lt;/code&gt; function returns an &lt;code&gt;Effect list&lt;/code&gt; instead of Cmd.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;let&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="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Model&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="nc"&gt;Effect&lt;/span&gt; &lt;span class="kt"&gt;list&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Home&lt;/span&gt; &lt;span class="n"&gt;pageData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;HomeMsg&lt;/span&gt; &lt;span class="n"&gt;pageMsg&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;pageData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;effects&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Home&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;pageMsg&lt;/span&gt; &lt;span class="n"&gt;pageData&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;Page&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Home&lt;/span&gt; &lt;span class="n"&gt;pageData&lt;/span&gt; &lt;span class="o"&gt;},&lt;/span&gt; &lt;span class="n"&gt;effects&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This completely decouples side effects from the update function. Effect is just data and is not tied to any implementation. It also makes it possible for Effect to be value equatable. Which means you can simply setup some expected data and use &lt;code&gt;=&lt;/code&gt; to test that actual output matches, just like with Model. No mocks, stubs, or anything invasive at all. So now when you test &lt;code&gt;update&lt;/code&gt; you can not only test model changes, but also whether the correct Effects are triggered.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It is sometimes necessary to work with unpredictable reference data like JS Files inside Effect. A match statement can be used to specially check those cases and use value equality for the rest.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The last piece is to code up the side effect implementation. I call the function for this &lt;code&gt;perform&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;perform&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="n"&gt;effect&lt;/span&gt; &lt;span class="n"&gt;dispatch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;effect&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;GetCurrentDateTime&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;dispatch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CurrentDateTimeIs&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;SendApiRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// http request details elided&lt;/span&gt;
      &lt;span class="n"&gt;dispatch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ApiResponseIs&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Async&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StartImmediate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Elmish, this new Effect/perform pattern is pretty easy to integrate. It just requires a couple of helper functions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="p"&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;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;effects&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;App&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;arg&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;effects&lt;/span&gt; &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;List&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;perform&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;EffectConfig&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;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="p"&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;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;effects&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;App&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="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;effects&lt;/span&gt; &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;List&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;perform&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;EffectConfig&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;

&lt;span class="nn"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mkProgram&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="nn"&gt;App&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;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;withReactBatched&lt;/span&gt; &lt;span class="s2"&gt;"elmish-app"&lt;/span&gt;
&lt;span class="p"&gt;#&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nc"&gt;DEBUG&lt;/span&gt;
&lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;withConsoleTrace&lt;/span&gt;
&lt;span class="p"&gt;#&lt;/span&gt;&lt;span class="n"&gt;endif&lt;/span&gt;
&lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;EffectConfig&lt;/code&gt; is where I keep data needed by side effects. For example: API URLs, local storage keys, auth endpoints, etc. These are usually settings found in a JS file deployed with the app. They are grabbed just before the program starts and passed in as the &lt;code&gt;init&lt;/code&gt; argument.&lt;/p&gt;

&lt;h3&gt;
  
  
  Practical issues
&lt;/h3&gt;

&lt;p&gt;My web apps so far have a small number of highly reusable effects, so I tend to make Effect just a flat universal type that any page can use. In larger apps, it can be annoying to get the return Msg back to the specific page that requested it. What I presented above has &lt;code&gt;perform&lt;/code&gt; sending responses back with non-page-specific Msgs. So you'd have to add a match for this message to each page that might receive one of these responses.&lt;/p&gt;

&lt;p&gt;An alternative approach that I later found my devs using is to provide a return tag with the Effect. This tag is a function which takes the return value and wraps it in a Msg. The Msg would target the specific page that requested it. This hinders the testability of Effects, since you now have to specially treat most of the Effect cases to ignore the tag function and only test the equatable data. But it works since we only have a dozen or less effects, and a handful that use return tags.&lt;/p&gt;

&lt;p&gt;You could also avoid this by customizing the available Effects per page. But we haven't found this worth the extra code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To be perfectly honest, we do not test &lt;code&gt;update&lt;/code&gt; currently. In our years of working with MVU there has not been a strong need. Yes we have broken things, but with the other mentioned principles they have been easy to spot and fix quickly. Yet we still wanted the ability to easily test &lt;code&gt;update&lt;/code&gt; in case we need to introduce dynamically generated UIs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The way we handle side effects is my favorite adaptation to the original MVU pattern. Because over the years I have observed that Separation of Concerns is the most important principle to maintainable software. And effect-as-data provides this via loose coupling between logic and side effect implementations.&lt;/p&gt;

&lt;h1&gt;
  
  
  Project organization tricks
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Note on Model
&lt;/h3&gt;

&lt;p&gt;Originally TEA had Msg, Model, init, and update inside the page module. We had to move Msg and the newly invented InitArgs out of the page module and fully define these types for all the pages before the page function definitions.&lt;/p&gt;

&lt;p&gt;We can technically keep a page's Model with its functions. But then Model would be the only type that we defined there. So we ended up just moving it up as well. In essence, we split the page's related parts into PageTypes (defined first) and PageFns.&lt;/p&gt;

&lt;p&gt;A typical project file structure would look like this.&lt;/p&gt;

&lt;p&gt;App project&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PageTypes

&lt;ul&gt;
&lt;li&gt;Home.fs - Home's Msg, InitArgs, Model&lt;/li&gt;
&lt;li&gt;Course

&lt;ul&gt;
&lt;li&gt;Search.fs&lt;/li&gt;
&lt;li&gt;Detail.fs&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;AppTypes.fs - App's Msg, InitArgs, Model&lt;/li&gt;
&lt;li&gt;InitArgs.fs - has toUrl, fromUrl&lt;/li&gt;
&lt;li&gt;PageFns

&lt;ul&gt;
&lt;li&gt;Home.fs - Home's init, update, view&lt;/li&gt;
&lt;li&gt;Course

&lt;ul&gt;
&lt;li&gt;Search.fs&lt;/li&gt;
&lt;li&gt;Detail.fs&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;App.fs - App's init, update, view, perform&lt;/li&gt;
&lt;li&gt;Main.fs - Elmish Program wireup&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;F# has a single-pass compiler. And file order is compile order. So this organization structure is almost like we are creating our own two-pass compiler where types are compiled first, then the functions that use them. If F# had a 2-pass compiler -- types then functions -- it might be possible to keep all the page parts organized in a single module.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  VS 2019 bug
&lt;/h3&gt;

&lt;p&gt;The project structure &lt;em&gt;would&lt;/em&gt; look like above. Except Visual Studio has a long-standing F#-specific bug. When you have two file paths that have the same subfolder as part of the path, intellisense glitches out. In the example above PageTypes and PageFns both contain a Course subfolder. Any file in the PageFns\Course subfolder will have completely broken intellisense as well as any other file defined after it. It also stops displaying the correct file order in the Solution Explorer window.&lt;/p&gt;

&lt;p&gt;We simply name the PageFns subfolders with an underscore on the end (PageFns\Course_\Search.fs) to make the path different. The module names are kept the same (no underscore).&lt;/p&gt;

&lt;p&gt;Another way around this is to not use folders but instead use multi-dotted files. Example: Course.Search.fs. Of course, this sacrifices the ability to roll-up/hide all the Course files when I am not using them.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I hope this gets priority to fix soon, because it is embarrassing to have to mention it in a post like this one.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Trick: Merging modules
&lt;/h3&gt;

&lt;p&gt;One really nice property of F# is that opening two different namespaces will effectively merge all the types and functions across same-named modules within them. For example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="nc"&gt;PageTypes&lt;/span&gt;
&lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="nc"&gt;PageFns&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;MyMsg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Msg&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;myInit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;

&lt;span class="c1"&gt;// "Home" is a combination of all the stuff from:&lt;/span&gt;
&lt;span class="c1"&gt;// PageTypes\Home.fs&lt;/span&gt;
&lt;span class="c1"&gt;// PageFns\Home.fs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This still allows you to use all the page parts as though they were under one module. This is also how I extend existing types with new functions.&lt;/p&gt;

&lt;h1&gt;
  
  
  Pain point
&lt;/h1&gt;

&lt;p&gt;The one major pain point with all the tactics I describe above is structural duplication. What I mean by that is Msg, InitArgs, and Model all have the same basic tree structure, but with different types as leaf nodes. To avoid conflicts, I must name them slightly differently.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// AppTypes.fs&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Msg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;HomeMsg&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nn"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Msg&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;CourseSearchMsg&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Msg&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;CourseDetailMsg&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Msg&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;InitArgs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;HomeInit&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nn"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;InitArgs&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;CourseSearchInit&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;InitArgs&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;CourseDetailInit&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;InitArgs&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Page&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Home&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nn"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Model&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;CourseSearch&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Model&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;CourseDetail&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Model&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anyone familiar with Haskell is probably jumping up and down, screaming "Higher-Kinded Types". F# doesn't have those.&lt;/p&gt;

&lt;p&gt;However I can think of 3 ways to reduce these 3 tree structures to 1 in F#. I will list them (quite subjectively) from least desirable to most.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;These are thought experiments. I have not tried them in a real app yet.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3. Generics as fake HKTs
&lt;/h3&gt;

&lt;p&gt;The setup for this is worse than the original solution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Area&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;courseSearch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;courseDetail&lt;/span&gt;&lt;span class="p"&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="nc"&gt;Home&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;CourseSearch&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;courseSearch&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;CourseDetail&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;courseDetail&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Msg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;      &lt;span class="nc"&gt;Area&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Msg&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;InitArgs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Area&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;InitArgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;InitArgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;InitArgs&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Page&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;     &lt;span class="nc"&gt;Area&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding a new page is pretty egregious. You have to touch Area in 2 places, then add a line to each of Msg, InitArgs, and Page.&lt;/p&gt;

&lt;p&gt;The only improvement with this approach is that the update function is marginally nicer than the original solution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;let&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="p"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Home&lt;/span&gt; &lt;span class="n"&gt;pageData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Home&lt;/span&gt; &lt;span class="n"&gt;msgData&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;CourseSearch&lt;/span&gt; &lt;span class="n"&gt;pageData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;CourseSearch&lt;/span&gt; &lt;span class="n"&gt;msgData&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some people like this approach, but overall I think this has more losses than gains.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Union leaf data
&lt;/h3&gt;

&lt;p&gt;This one is also more work to setup initially.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Leaf&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&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;'&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&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="nc"&gt;Msg&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Init&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Page&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Area&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Home&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;         &lt;span class="nc"&gt;Leaf&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                         &lt;span class="nn"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;InitArgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                         &lt;span class="nn"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;CourseSearch&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nc"&gt;Leaf&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                         &lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;InitArgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                         &lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;CourseDetail&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nc"&gt;Leaf&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                         &lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;InitArgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                         &lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding a new page is a slight improvement over the original solution. It is typing a few lines in one place instead of a new line in a few places.&lt;/p&gt;

&lt;p&gt;The update function is slightly more verbose than the original solution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;let&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="p"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Home&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Page&lt;/span&gt; &lt;span class="n"&gt;pageData&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;Home&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Msg&lt;/span&gt; &lt;span class="n"&gt;pageMsg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;CourseDetail&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Page&lt;/span&gt; &lt;span class="n"&gt;pageData&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;CourseDetail&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Msg&lt;/span&gt; &lt;span class="n"&gt;pageMsg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The weird part of this approach is that &lt;code&gt;model.Page&lt;/code&gt; and &lt;code&gt;msg&lt;/code&gt; have exactly the same type. That means you can accidentally swap them and not get a compiler error. Then the UI won't work and there is no obvious reason why. Although, it should be possible to add some match cases to detect this and log a warning at runtime.&lt;/p&gt;

&lt;p&gt;Overall I'd prefer this over #3, but it is still not ideal.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Only Msg
&lt;/h3&gt;

&lt;p&gt;The last way I can think of to get one tree structure involves just eliminating InitArgs and Page, leaving us with only Msg.&lt;/p&gt;

&lt;h4&gt;
  
  
  Bye InitArgs
&lt;/h4&gt;

&lt;p&gt;Getting rid of InitArgs is pretty far-reaching, but also has other benefits. Essentially the &lt;code&gt;init&lt;/code&gt; function is removed and its behavior is put in a new case of &lt;code&gt;update&lt;/code&gt; function. Then the InitArgs for that page becomes a Msg case. We will have to add one thing, a "zero" or empty model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// PageTypes\Course\Detail.fs&lt;/span&gt;
&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;PageTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;Detail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;

  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Msg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Init&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="n"&gt;courseId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Guid&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;CourseLoaded&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nc"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;QueryError&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Model&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;CourseId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Guid&lt;/span&gt;
      &lt;span class="nc"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Remote&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;CourseId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt;
      &lt;span class="nc"&gt;Data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Loading&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// PageFns\Course\Detail.fs&lt;/span&gt;
&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;PageFns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;Detail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;

  &lt;span class="k"&gt;let&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="p"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Init&lt;/span&gt; &lt;span class="n"&gt;courseId&lt;/span&gt; &lt;span class="p"&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="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;CourseId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;courseId&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="nc"&gt;ApiRequest&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GetCourse&lt;/span&gt; &lt;span class="n"&gt;courseId&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="nc"&gt;CourseLoaded&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Ok&lt;/span&gt; &lt;span class="n"&gt;course&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&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="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;Data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Loaded&lt;/span&gt; &lt;span class="n"&gt;course&lt;/span&gt; &lt;span class="o"&gt;},&lt;/span&gt; &lt;span class="bp"&gt;[]&lt;/span&gt;
    &lt;span class="c1"&gt;// and so on&lt;/span&gt;

  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="n"&gt;dispatch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="c1"&gt;// stuff&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have to do special handling of the Init message in the main &lt;code&gt;update&lt;/code&gt;. We are using this kind of code instead of the &lt;code&gt;switchPage&lt;/code&gt; function from before.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// App.fs&lt;/span&gt;
&lt;span class="k"&gt;let&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="p"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="o"&gt;_,&lt;/span&gt; &lt;span class="nc"&gt;CourseDetail&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Init&lt;/span&gt; &lt;span class="o"&gt;_)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pageMsg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;pageData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;effects&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Detail&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;pageMsg&lt;/span&gt; &lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Detail&lt;/span&gt;&lt;span class="p"&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;model&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;Page&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CourseDetail&lt;/span&gt; &lt;span class="n"&gt;pageData&lt;/span&gt; &lt;span class="o"&gt;},&lt;/span&gt; &lt;span class="n"&gt;effects&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;I used a similar approach when I built a &lt;a href="https://dev.to/kspeakman/clojurescript-simple-mvu-loop-refined-4i6e"&gt;Clojure MVU library&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Moving Page
&lt;/h4&gt;

&lt;p&gt;UI state like Page is a bit different from Msg or InitArgs. The details of it are only used by the page functions. Outside parties do not need to know the page Model's contents. Previously we set it up as a public tree type for consistency with how we are handling other types. But there is another way. We can use a marker interface.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A marker interface is just an empty interface. Any type can just say it implements a marker interface, since there is nothing to implement. Marker interfaces can be looked at as a slightly different union type. You do not have to define all cases up front... a type locally chooses to opt in to the marker interface. But you also do not get compiler guarantees of complete matches. It is another expression problem trade-off toward extensibility rather than completeness.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Types.fs&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;IPage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;// PageTypes\Course\Detail.fs&lt;/span&gt;
&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;PageTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;Detail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;

  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Model&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;CourseId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Guid&lt;/span&gt;
      &lt;span class="nc"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Remote&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IPage&lt;/span&gt;

&lt;span class="c1"&gt;// AppTypes.fs&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Model&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;EffectConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;EffectConfig&lt;/span&gt;
    &lt;span class="nc"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;IPage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// App.fs&lt;/span&gt;
&lt;span class="k"&gt;let&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="p"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="o"&gt;:?&lt;/span&gt; &lt;span class="nn"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Model&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pageData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Home&lt;/span&gt; &lt;span class="n"&gt;pageMsg&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can essentially "tag" each page model as IPage. And any IPage can be stored in the Model.Page property. Unwrapping it to a specific page's model is a few more keystrokes. But there is no central Page union type to maintain anymore.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I have not tried this beyond checking that it would compile in a Fable Elmish project.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  End result
&lt;/h4&gt;

&lt;p&gt;At this point, we have eliminated all the duplicate tree structured types and are left with only Msg. We have also eliminated the &lt;code&gt;init&lt;/code&gt; function. So the page logic is centralized in &lt;code&gt;update&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Over the last 4 years I have made MVU my playground and learned quite a bit. It is a surprisingly resilient and flexible pattern for organizing UI applications. Because of its state-machine-like qualities, we even use it &lt;a href="https://dev.to/kspeakman/testable-back-end-programming-in-f-41n5"&gt;server-side&lt;/a&gt;. Hopefully some of the adaptations I discovered have been interesting to you.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This post has a lot of code snippets, but it could use a companion repo with more complete examples. With my ADHD, an effort like that will never go anywhere if left up to me. So if something like that is of interest to you, let me know. I would be more than happy to contribute.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;cover image from &lt;a href="https://undraw.co"&gt;unDraw&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Addendum
&lt;/h1&gt;

&lt;p&gt;I tried the &lt;strong&gt;#1 Only Msg&lt;/strong&gt; approach and faced some challenges.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving InitArgs into Msg and removing &lt;code&gt;init&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Moving InitArgs into Msg
&lt;/h3&gt;

&lt;p&gt;One thing I didn't cover was how this affects navigation. Instead of converting InitArgs to/from URLs, this now means converting Msg to/from URLs. The data to create a URL needs to be dug out of the specific page messages. And there will need to be a catch-all match because only a small amount of messages correspond to URLs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;CourseDetail&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Init&lt;/span&gt; &lt;span class="n"&gt;initArgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="s2"&gt;"#/course/"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;initArgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CourseId&lt;/span&gt;
&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;_&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="n"&gt;defaultUrl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Alternative InitArgs tactic
&lt;/h4&gt;

&lt;p&gt;It has become our standard practice to create an InitArgs record for each page. This record is a container for all the necessary parameters to the &lt;code&gt;init&lt;/code&gt; function. And these page InitArgs need to be app-wide like page Msgs. So any page may use them to construct links to other pages.&lt;/p&gt;

&lt;p&gt;We could instead use tupled values or anonymous records for a page's init args.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;InitArgs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
  &lt;span class="c1"&gt;//| CourseSearchInit of Course.Search.InitArgs&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;CourseSearchInit&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="n"&gt;search&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="n"&gt;page&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;pageSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then avoid having to define and organize those page InitArgs records. But some duplication of the parameter definitions may be necessary. For example in &lt;code&gt;init&lt;/code&gt; if the type would be ambiguous in its usage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
  &lt;span class="c1"&gt;// ambiguous type error, requires annotating search as string&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;normalizedSearch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Trim&lt;/span&gt;&lt;span class="bp"&gt;()&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And of course there are the same tradeoffs of using tuples vs records. When creating a new value, records are more verbose but easier to understand because the values are labeled.&lt;/p&gt;

&lt;p&gt;Using tuples versus organizing all the page's InitArgs types to be app-wide, I do not think there is a clear winner. So you just have to weigh the tradeoffs on a case by case basis. It is probably easier to start with tuples if unsure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Removing &lt;code&gt;init&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;When removing init, a disadvantage is sometimes empty values can be a pain to construct. Like arbitrarily nested records. (I mainly encountered this when trying to remove the overall app init.) So using an explicit &lt;code&gt;init&lt;/code&gt; function can feel more natural in those cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Overall
&lt;/h3&gt;

&lt;p&gt;This does not feel like a worthwhile change. InitArgs is an additional app-wide type, but it has its own specific usefulness -- converting to/from URLs. And although &lt;code&gt;init&lt;/code&gt; usually feels like a special case of update, it can sometimes be useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving Page
&lt;/h2&gt;

&lt;p&gt;I mentioned using the &lt;code&gt;IPage&lt;/code&gt; marker interface to represent pages instead of a central DU. But because F# does not auto-upcast, the main update has to explicitly upcast every page model to &lt;code&gt;IPage&lt;/code&gt;. That's arguably worse than just tagging the page-specific model to go in a central DU.&lt;/p&gt;

&lt;p&gt;One of the big benefits of using a marker interface is keeping the page Model with its init and update functions. And this is still possible when using a DU. Simply define the central Page DU in the main app file with the app's &lt;code&gt;init&lt;/code&gt; and &lt;code&gt;update&lt;/code&gt; (versus declaring it app-wide like Msgs). We used to organize it this way, but when I moved the other types (Msg, InitArgs) out of the pages I dragged Model with them. So I over-organized that.&lt;/p&gt;

&lt;h1&gt;
  
  
  Closing thoughts
&lt;/h1&gt;

&lt;p&gt;Turns out that some of the annoying duplication I mentioned in the main article adds more value than it costs. And there are still minor tweaks available to tone down the annoyance. Without fundamentally deviating from the standard MVU structures.&lt;/p&gt;

</description>
      <category>mvu</category>
      <category>fsharp</category>
      <category>functional</category>
      <category>ui</category>
    </item>
    <item>
      <title>Entity Component System - an old new thing</title>
      <dc:creator>Kasey Speakman</dc:creator>
      <pubDate>Sun, 13 Dec 2020 00:58:31 +0000</pubDate>
      <link>https://forem.com/kspeakman/entity-component-system-an-old-new-thing-3224</link>
      <guid>https://forem.com/kspeakman/entity-component-system-an-old-new-thing-3224</guid>
      <description>&lt;p&gt;&lt;em&gt;Structured Programming rediscovered and honed for game development.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I've been looking into game dev lately. Always been a gamer. Always wanted to make games. But getting into it seems completely overwhelming. I found a podcast on DEV with Jason Weimann talking about game dev, and then his youtube channel with videos like this one.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/Lu76c85LhGY"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;His videos are incredibly helpful. But I noticed that many of them are centered around dealing with Object-Oriented ceremony and footguns. This is because Unity uses the OO model pervasively, and requires your code to use it as well. Being an old dev interested in system design, I went looking for other game dev organization schemes. I discovered the &lt;a href="https://en.wikipedia.org/wiki/Entity_component_system" rel="noopener noreferrer"&gt;Entity-Component-System&lt;/a&gt; or ECS architecture.&lt;/p&gt;

&lt;h1&gt;
  
  
  What is ECS?
&lt;/h1&gt;

&lt;p&gt;The three parts of ECS are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Entity&lt;/strong&gt; - a game object with a unique ID, contains components&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Component&lt;/strong&gt; - data about a single aspect of a game object&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System&lt;/strong&gt; - logic for a single aspect of game objects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The obvious interaction here is to pass the Entity to a System behavior. Such as "add to inventory" in the Inventory system or "take damage" in the Health system. The behavior can check if the Entity has a component that it knows how to work with. And if so perform the requested behavior on that component.&lt;/p&gt;

&lt;p&gt;ECS cleanly separates the systems of the game into maintainable slices. The Unity folks seemed to think so as well since in 2018 they made an announcement that they were adding ECS to Unity. Not only for maintainability reasons but for performance.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/TeyTdbqkHmE"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;At the end of 2020 this still remains to be seen. They have now hidden the beta ECS packages. (You can still install if you know the URL.) Presumably because there is so much churn that it is guaranteed to break game code in future updates. &lt;/p&gt;

&lt;h1&gt;
  
  
  This looks familiar...
&lt;/h1&gt;

&lt;p&gt;On the whole the system ends up breaking down into data structures with no behavior, and functions or procedures that operate on those data structures (and the implicit side effects those procedures cause) to produce a behavior. This is the same arrangement used by &lt;a href="https://en.wikipedia.org/wiki/Structured_programming" rel="noopener noreferrer"&gt;Structured Programming&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Structured Programming came about in the late 1950s, at first primarily being about block structured syntax. And later officially branded in a 1966 paper by Corrado Böhm and Giuseppe Jacopini. But probably more well known as championed by Edsger W. Dijkstra, where we got our "... Considered Harmful" memes.&lt;/p&gt;

&lt;p&gt;Like most things that are created and released into the world, the creators and proponents of this paradigm probably assumed it would be used in a certain beneficial way. To tame the spaghetti of Assembly code. But instead of using the tools to organize our code we just moved the spaghetti into procedures with &lt;a href="https://en.wikipedia.org/wiki/Procedural_programming" rel="noopener noreferrer"&gt;Procedural Programming&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Most languages have procedural programming -- a descendant of structured programming -- as the substrate underneath their paradigm. Meaning once you get past the declarations the code is written as a statement-oriented and block-structured procedure.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  What is different?
&lt;/h1&gt;

&lt;h2&gt;
  
  
  ECS is an architecture, not a program structure
&lt;/h2&gt;

&lt;p&gt;The primary difference is purpose. Structured Programming is meant to put your program on good footing for maintenance. A video game is just a program, right?&lt;/p&gt;

&lt;p&gt;Look at it this way. If a business has a universe of applications and services working together to deliver business value, a video game is a microverse (or sometimes an even larger universe) of all the same kinds of things working together to deliver user value (game experience). The breakdown of a triple-A game into its many systems, when translated into the business world, would be the domain of a software architect. So in essence ECS is not simply a program structure, but a base architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Entities have runtime composition
&lt;/h2&gt;

&lt;p&gt;This significant difference in packaging compared to business systems leads to another important adaptation. Namely, you can add or remove arbitrary Components (data structures) from an entity at runtime. This is quite different from structured programming where data structures were completely pre-defined. Additions and removals of this kind would need a recompile and redeploy.&lt;/p&gt;

&lt;p&gt;Since ECS is an architecture, this property of entities makes perfect sense. Put on your Business Dev hat and consider a Customer. Accounting does not need to know about every sales lead... only the ones that buy something. And then they care about their own specific aspects of that customer which sales may not, e.g. billing address. In this analogy the billing address is part of the Accounting ECS component, but not the Sales component. And Accounting ECS system requires that component it be present to perform its function.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Remember, an ECS Component is only data. State would have been a better name for it, but nobody asked me back in 2007 when they named it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Other connections
&lt;/h1&gt;

&lt;p&gt;I have previously &lt;a href="https://dev.to/kspeakman/mere-functional-programming-in-f-do8"&gt;made a connection&lt;/a&gt; between Structured Programming and Functional Programming. Basic FP (without all that category theory) takes Structured Programming and draws out one more key concept -- the side effect -- and makes it first class and explicit instead of implicit. This greatly increases the testability and maintainability of code. And so I think ECS could create a strong synergy between game dev and FP. &lt;em&gt;Wait: FP and game dev -- am I crazy? I don't think so... check out &lt;a href="https://gamasutra.com/view/news/169296/Indepth_Functional_programming_in_C.php" rel="noopener noreferrer"&gt;John Carmack's thoughts&lt;/a&gt; on the subject.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Like most new ideas, ECS stands upon the shoulders of giants that came before. It is not new at its core, but how it is applied is novel. Unity seems to be a popular and "easy" entry into the world of game dev. I look forward to trying their ECS system. Especially since F# is also usable with Unity. ECS and F# would make the ultimate combo for me.&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>ecs</category>
    </item>
    <item>
      <title>Static vs Dynamic Typing for  ADHD</title>
      <dc:creator>Kasey Speakman</dc:creator>
      <pubDate>Mon, 07 Dec 2020 22:15:40 +0000</pubDate>
      <link>https://forem.com/kspeakman/static-vs-dynamic-typing-for-adhd-4d28</link>
      <guid>https://forem.com/kspeakman/static-vs-dynamic-typing-for-adhd-4d28</guid>
      <description>&lt;p&gt;&lt;em&gt;Static typing has boring overhead but it augments my brain's limited working memory.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  The PHP years
&lt;/h1&gt;

&lt;p&gt;My first professional software product was a sign-out board for consultants and company cars. At the time I was employed quite cheaply as IT support not a programmer. I wrote it in PHP, which is dynamic in case you don't know, circa 2000. Later I added an expense reporting feature to replace their existing process of emailing spreadsheets to accounting.&lt;/p&gt;

&lt;p&gt;I went on to write many small things in PHP on the side. A service to receive automated responses from an SSN validation provider. (Similar to what web hooks do today.) A limited blog-type website for a charity. (WordPress didn't exist back then.) And a few other things.&lt;/p&gt;

&lt;p&gt;After a couple of years the company I worked for closed, and I was without a job. My dad hired me to write an internal ordering system for his small business, which produces lighted outdoor signs. There is a tiny chance that some of those golden arches you see from the interstate in the USA were made to order at his shop.&lt;/p&gt;

&lt;h2&gt;
  
  
  My equipment betrays me
&lt;/h2&gt;

&lt;p&gt;Pricing a sign and speccing it out for manufacturing is complicated. The wizard-style app I ended up writing had a dozen or 2 possible pages of data collection. It had branching paths depending on selected options and multiple pricing rules. This is where PHP broke me.&lt;/p&gt;

&lt;p&gt;Later after I tried other languages, I came to have a strong distaste for PHP. But I realize now that the negative experience I had with PHP was not its fault. It was my brain's tiny working memory -- a trait of ADHD. To keep the small details of a wizard process in mind across a dozen pages was too much for it. Especially when ADHD prevents me from choosing what to focus on. So every day became a frustrating process of continually losing focus and forgetting the information I needed to carry from one page to the next. And having to repeatedly remind myself of the same information. Eventually breaking through and having enough information to make a little hard-fought progress. Some days I was too burned out and simply let the hours pass in defeat. I realized none of this at the time and I just assumed programming for a living was not for me.&lt;/p&gt;

&lt;h1&gt;
  
  
  The (mostly) typed years
&lt;/h1&gt;

&lt;p&gt;I did eventually finish that app. And as much as I wanted to do hardware work, people kept wanting to pay me for software. I took on some work writing in VB, then C#, Javascript, some others, and finally F#. After C# I realized that I could maybe see my way to coding for a living and did so from then on. Fast forward a decade to the last few months, I starting learning Clojure. &lt;/p&gt;

&lt;h1&gt;
  
  
  Rediscovering dynamism
&lt;/h1&gt;

&lt;p&gt;I created MVU and validation libraries in Clojure. I absolutely loved how tiny yet understandable they were. I started rewriting my front-end-only budget simulator in it. While doing that, a feeling struck me that I had not felt for a long time. It was frustration with myself for not being able to remember basic information from one page to the next and the fear that I had remembered it wrong. It was notable because it happened repeatedly.&lt;/p&gt;

&lt;p&gt;Then I discovered I had ADHD, and these experiences started to make sense.&lt;/p&gt;

&lt;h1&gt;
  
  
  Dynamism in context
&lt;/h1&gt;

&lt;p&gt;Part of the reason I was interested in Clojure was precisely because it was dynamic. After many years of writing in typed languages and creating larger and larger apps, maintaining types can become tedious. And it definitely adds coding overhead that I was interested in reclaiming. What I did not realize until recently is that my neurological traits make types highly beneficial for me. Types essentially provide a library for me to look up the shape of the data. And editors instrument this with the ability to hover and see the type definition in an instant. So types augment my limited working memory very effectively.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Programming already requires an expanded working memory as you progress toward an overall solution. But with ADHD a lot of the important minutia (like what did I name that field, or what kind of data was in it?) is impossible to keep in working memory because it is uninteresting and therefore evades focus.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For other people, this may be less of an issue and dynamic languages are a net savings. But for me the overhead of maintaining types is well worth the benefit. It is especially detrimental to the self-esteem to get so repeatedly frustrated with one's own limitations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Same for FP&lt;/strong&gt;&lt;br&gt;
This is also the reason that the functional programming paradigm works so well for me. FP focuses on using deterministic (aka pure) functions. By definition, this mean I do not have to remember any state from outside the function. I only have to deal with what is right in front of my face. This plays to my strengths.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I have not given up on Clojure. Spec allows me to have types, in effect. The main issue is that Spec is still in alpha and extensive use of specs seems to be considered abnormal in Clojure code. For now I am keeping my eye on it. I have enjoyed writing code in Clojure so I am interested in figuring out a system that works for me there at some point.&lt;/p&gt;

&lt;h1&gt;
  
  
  Path ahead
&lt;/h1&gt;

&lt;p&gt;Overall, this realization about my neurological traits means I will be preferring types in some form or another for the foreseeable future. And F# is my go-to language.&lt;/p&gt;

</description>
      <category>mentalhealth</category>
      <category>typesystem</category>
    </item>
    <item>
      <title>ADHD: The Grief of Discovery</title>
      <dc:creator>Kasey Speakman</dc:creator>
      <pubDate>Fri, 20 Nov 2020 05:27:53 +0000</pubDate>
      <link>https://forem.com/kspeakman/adhd-the-grief-of-discovery-1njp</link>
      <guid>https://forem.com/kspeakman/adhd-the-grief-of-discovery-1njp</guid>
      <description>&lt;p&gt;&lt;strong&gt;&lt;em&gt;Mourning the loss of the person I might have been.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is a followup to &lt;a href="https://dev.to/kspeakman/i-have-adhd-h9e"&gt;I have ADHD 🌧️&lt;/a&gt;. There is a strange phenomenon where the discovery of having ADHD late in life is followed by a grieving period.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When I was first told I had ADHD it was surprising to me. I had heard of ADHD and made some assumptions about it. I thought it would be an add-on diagnosis to the problem I really had. But it turns out that ADHD is quite serious by itself. If it is unmanaged, it can ruin lives. And it certainly has caused a lot of problems in mine.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ADHD carries with it significantly higher risk of substance abuse, incarceration, divorce, suicide, bankruptcy, traffic accidents, and many other bad outcomes. More on this in a later post... if I can concentrate long enough.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I was overjoyed to finally know what was wrong and that there are ways to manage it. But the joy was short-lived.&lt;/p&gt;

&lt;p&gt;Fundamentally, ADHD leaves you feeling not good enough for the rest of society. Especially for someone like me who was diagnosed as an adult. I spent years trying my hardest and failing at the most basic tenants of modern life: structure, order, consistency... routine. I could not help but feel that I was missing some vital human quality that everyone else has. It not only affected my behavior in the present, but also my outlook on what is possible.&lt;/p&gt;

&lt;p&gt;For example, in early childhood I was overly talkative and frequently got in trouble for it at school. Not only that but it tended to annoy other kids, and they unsurprisingly reacted with negative reinforcement. I became very socially reserved and anxious. I knew that I could not get social situations right. So I had to change how I approached it -- now with a lot of caution and fear. I became more isolated and did not have many friends. The ones I did have, I typically did not spend much time with outside of school obligations. Because hanging with me could be exhausting. The high school years were very hard.&lt;/p&gt;

&lt;p&gt;I was told I had intelligence. But I did not feel smart. How is it smart to not do your homework or study for tests? I had some classes that interested me so I was easily able to do assignments and excel in those. This was evidence that I could do well "if I tried". But all my other subjects were a cold war, waged internally and externally with my parents and teachers. As much as I wanted to prove I was the intelligent person others believed I could be, eventually I was choosing easier classes to avoid the conflict.&lt;/p&gt;

&lt;p&gt;I had dreams of going to MIT, which to me was the pinnacle of technology schools. But I knew there was no way I could get accepted there, much less complete a degree. For all my gifts I could not even manage high school well, graduating with a middling rank in my class. My 4-year college degree took 9 years, off and on. I finished at a local college and really only with the help of my classmates who had formed a study group.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I am thankful for that local college and my classmates or I might never have finished my degree at all! And in the 9 years it took, I ended up with 3 majors.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To top it off I had an addiction to video games. I could not get enough of them since I got my first Nintendo around age 10. Now I see it for what it is, self-medicating that missing dopamine. But at the time I and my parents thought it was the crippling illness that hindered my life goals.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Goals is the wrong word. I never had goals. The act of making goals is just laying a minefield of disappointment for future me. I think the right word here is potential.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  The Me that never was
&lt;/h1&gt;

&lt;p&gt;So at this point I go from being joyful at the discovery I have ADHD to morbid grief. The person that I was growing up never had a chance. Their own brain was fighting them tooth and nail. And that traditional future where they realized their potential is gone forever. It wounded me deeply, like the loss of a twin brother. I spent a day crying over it.&lt;/p&gt;

&lt;p&gt;It would be easy to reach for blame here. I mean it is always the parent's fault right? Shouldn't they have seen it? Or the teachers? Well not really, no. Considering the knowledge of ADHD was not widespread back then and my brain lives to adapt (and trying to appear "normal"), it would have been hard for them to see it. When I look at how bad outcomes can be for unmanaged ADHD, I think my parents did a great job by giving me boundaries and a compass to keep me away from most of them.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/3t2p48P609M"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  The Me that is
&lt;/h1&gt;

&lt;p&gt;The good (?) thing about ADHD is that I lose interest in things, including grief, quickly. (Plus I didn't &lt;em&gt;actually&lt;/em&gt; lose someone.) I'm sure it will come back up now and again (like it did while writing this post). But there is no use playing the What If game. Also taking stock of today, I am doing well. Not sure if y'all heard but experienced programmers make a decent living. And I have managed to form meaningful relationships and keep them, by the grace of God.&lt;/p&gt;

&lt;p&gt;So the right question is what to do now.&lt;/p&gt;

&lt;p&gt;Well you are reading it. I am sharing my story and raising awareness. A lot of people still think ADHD (and mental health in general) is like having brown hair, not a big deal. Or like the Easter Bunny, not real. Or like the opioid crisis, a pharma conspiracy. Or like spoiled kids, a parental problem. It is none of those things. If unmanaged it can devastate lives, not just of those who have it. &lt;em&gt;It is torturous to watch a loved one struggle and self-destruct!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So my call to action is to educate yourself. Not every problem is going to turn out to be ADHD. But if you've done everything you know to do and still just "don't feel right" or "can't do right" talk to someone who has the training and tools to help you figure it out. And if you have kids who are struggling in that way, get them to the right people. Why waste another moment?&lt;/p&gt;

&lt;h1&gt;
  
  
  Managed ADHD is a gift
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;I forgot to add this part initially.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;From what I have researched many people with managed ADHD (through medication or developing skills or both) are glad to have it. We tend to have certain characteristics that make us really good at specific and rare things that society and businesses need. Even though mine is not managed to the degree I want, I know that I bring a unique x-factor to our team that helps it succeed.&lt;/p&gt;

&lt;p&gt;I'm left to wonder whether thinking differently is intrinsically a disorder or whether modern society just hasn't got around to making space for us yet.&lt;/p&gt;

</description>
      <category>mentalhealth</category>
    </item>
    <item>
      <title>I have ADHD 🌧️</title>
      <dc:creator>Kasey Speakman</dc:creator>
      <pubDate>Tue, 17 Nov 2020 23:48:15 +0000</pubDate>
      <link>https://forem.com/kspeakman/i-have-adhd-h9e</link>
      <guid>https://forem.com/kspeakman/i-have-adhd-h9e</guid>
      <description>&lt;p&gt;&lt;em&gt;I am pathologically incapable of performing uninteresting tasks. And I hate myself for it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;First I should probably explain what ADHD &lt;strong&gt;really&lt;/strong&gt; is. It is a deficiency of specific neurotransmitters in the brain. Or "malfunctioning" neurons which flush the neurotransmitters too early. Or both. This results in neurons not firing when they should. &lt;em&gt;Disclaimer: this is my understanding, I am not a neuroscientist.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The primary consequence is that people with ADHD have little control over their ability to focus attention on tasks. Two common tricks that we use to focus are pressure (deadlines, something broken in production, etc) and being genuinely interested. Especially strong versions of these stimuli can push the ADHD person into a state of hyperfocus. In this state, the body can completely forget about sleep, food, and bathroom breaks while the person works or plays furiously. The counter-balance to that is when not stimulated, the ADHD brain cannot engage in planning or organizing tasks.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;It is not about willpower&lt;/strong&gt;&lt;br&gt;
Everyone experiences lack of motivation sometimes. A non-ADHD person has to muster the willpower to do uninteresting tasks. For an ADHD person in this state, their brains are chemically preventing them from concentrating. So it is like piloting an airplane through a hurricane... willpower is not enough to get you through.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/ouZrZa5pLXk"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;There are actually 2 other behavior areas affected by ADHD: impulsivity and hyperactivity (edit: and emotional dysregulation, but this is harder to quantify scientifically). And there is a spectrum of severity.&lt;/p&gt;

&lt;p&gt;Certain (now rare) occupations or lifestyles are well-suited to the differences in thinking of an ADHD person. So in those cases it may be a benefit rather than a problem. In the more sedentary modern life that most of us live, ADHD makes things quite difficult but is highly treatable with medicine and/or therapy.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/fWCocjh5aK0"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  My experiences
&lt;/h1&gt;

&lt;p&gt;For me personally, hyperactivity and impulsivity are milder. Those are usually the things that cause frustrated parents to have their child examined. And so I went undiagnosed for the first 41 years of my life. Instead I came to understand myself as a lazy and undependable jerk, whom I loathed.&lt;/p&gt;

&lt;p&gt;However, I did not make it through 20 years as a software developer by getting nothing done. With a lot of struggle, I organically adapted to my limitations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing code
&lt;/h2&gt;

&lt;p&gt;Some people can trick themselves into getting things done by setting fake deadlines. This unfortunately has no effect for me. Even when other people give me deadlines, when I can tell they are arbitrary (which they usually are), they provide no stimulation for me. What does work along these lines is when immediate help is required (production issue, mentoring, etc).&lt;/p&gt;

&lt;p&gt;Aside from emergent situations, the only thing that keeps me on task is being interested in the subject matter. And I only stay interested while there is a mystery left to solve. Earlier in my career my code looked like it was written by many different people. Because in a larger app, I would switch between many different approaches before I finished it. Trying interesting new approaches was the only way I could focus on writing code.&lt;/p&gt;

&lt;p&gt;As time went on, my interest went from tactics in the small to application organization and then whole system design (architecture). It is perhaps fitting that the area that came to interest me the most about software was the area where I struggle the most personally: how to organize things.&lt;/p&gt;

&lt;p&gt;Eventually I became valued for my expertise. Because I had tried or researched a lot of things, and I remembered what did and did not work about them and why. And I could synthesize solutions that matched the problem constraints. Although introverted, I learned also to talk with customers to tease out those problem constraints.&lt;/p&gt;

&lt;p&gt;I never cease to be interested in designing solutions. But the main problem is that most companies need someone to fill in code primarily and design solutions a relatively small percentage of the time. Even as a lead developer. And after 20 years, it has gotten very hard for me to find interesting things in coding.&lt;/p&gt;

&lt;p&gt;Management would be another option, except that I have little tolerance for authoritarian structures (top-down management). The way I approach my teams is by providing information and guidance, but letting them decide. And I prefer to be approached in the same way. I find the prospect of enforcing the arbitrary and unexplained whims of those "above" me onto those "below" me to be an intolerable situation. So that rules out management in most companies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Changing jobs
&lt;/h2&gt;

&lt;p&gt;I must periodically change jobs or I will probably get fired. Keeping up with the routine of an office job eventually becomes extremely challenging. When I first start a job, it is an exciting time and it is easy to keep up the routine. There is a lot to learn about the position, the company, the team dynamics, the subject matter, etc.&lt;/p&gt;

&lt;p&gt;But eventually things start to get comfortable. And with all my heart I wish that meant I settled into a routine. But instead it means that there is no longer enough stimulation to engage me in the routine. I start coming in a few minutes late. Then it gets later and later. I don't miss scheduled meetings. But eventually I might come in afternoons or call in for a mental health day.&lt;/p&gt;

&lt;p&gt;Some of this issue is sleep. I do not feel tired at night. If I try to go to bed my mind is still occupied or I am still locked on to an interest (research, video game, personal coding, etc.). I cannot transition to sleeping. Many times I go to bed anyway. Sometimes there is sleep, but often I will just lie in bed awake most of the night with my mind racing. I have typically found it best to just get up and do whatever is stuck in my mind to release it. Then I can sleep. But that means I stay up late.&lt;/p&gt;

&lt;p&gt;Getting up in the morning isn't hard when I already have something on my mind or am looking forward to something. But on an average day, I lay in bed letting my mind wander until eventually it hits an interesting thought. And only then can I get up. And usually being late to something (pressure-induced focus) is required to finally get up. Days which I know have particularly uninteresting tasks, I keep thinking about them, but it provides pressure against getting up. On days when I call in, I have usually laid there in bed for a long time, unable to move. But as soon as I call in and no longer have to worry about it, I think of something that gets me out of bed. It could be as simple as a delightful cup of coffee.&lt;/p&gt;

&lt;p&gt;When things start to get comfortable at work, the danger begins for me. I reckon this takes about 2 years. I will probably have started coming in late well before that as I got used to the office situation. But by 2 years I will probably have gotten comfortable with the code base. And I cannot focus to work on it anymore. Then I need to find another job to restart the process before I get fired.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I have actually stayed 5 or 6 years at a couple of jobs and never gotten fired. Miraculously in some cases. I think it is a combination of trying to change projects or positions within a company to reignite interest. And also when approached about my inability to follow routine I own it and try to figure out ways to adapt. They don't last but I try.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If I keep soldiering on and manage not to get fired but also not change the situation, I eventually become deeply unhappy. Depressed even.&lt;/p&gt;

&lt;h2&gt;
  
  
  Personal life
&lt;/h2&gt;

&lt;p&gt;None of this is even to mention the effects of ADHD on my personal life. I can probably count on one hand the things I do as consistent routines. Actually the only things I can think of are making the bed and making coffee when I get up (not necessarily in the morning). Pretty much everything else might stabilize for a week or two, but will eventually be in flux as to whether and when I do it.&lt;/p&gt;

&lt;p&gt;I learned long ago that joining any groups that had routine activities was a recipe for disappointment. It is especially bad because I tend to go all-in at first. But then when it becomes routine, I cannot even show up anymore. This hurts everyone's feelings as they think I do not like them anymore or I got offended or whatever. And I didn't even know why myself.&lt;/p&gt;

&lt;p&gt;As you might imagine, this has some detrimental effect on relationships. I am amazingly blessed with an angel of a wife who has put up with me for 13 years, not even knowing I had an illness. We have certainly had arguments about me not helping with chores or sleeping during the day or playing video games for 12 straight hours to the exclusion of all else. But we always kept trying with each other. As I write this I realize that I in no way deserve the grace that she or my children have given me.&lt;/p&gt;

&lt;p&gt;I also feel an immense guilt for not being able to write code some days (or weeks). I feel that I am hurting my team and my company and my salary would be better spent on others. I feel that I deserve to be fired as a matter of fairness.&lt;/p&gt;

&lt;h1&gt;
  
  
  How did I not know?
&lt;/h1&gt;

&lt;p&gt;An outside observer might think it should have been obvious that something was wrong. And to a degree I have always known something was wrong, that some "normal" things seemed really hard for me. But I was led to believe from early age I was being lazy and selfish. And that is what I thought was wrong and told others was wrong with me. I didn't exactly behave like that, but sometimes it fit.&lt;/p&gt;

&lt;p&gt;And it gets attributed to personality. For example, my wife showed me the Type 5 Enneagram, which is said to have limited energy so they are very careful about how they spend it. It seems to fit some of my behavior quite well, so people assume it is part of my personality.&lt;/p&gt;

&lt;p&gt;What actually made me think that my problem might be an illness was a video called Devs and Depression that &lt;a class="comment-mentioned-user" href="https://dev.to/ben"&gt;@ben&lt;/a&gt;
 posted in a comment several weeks ago. So glad Ben posted it and I saw it.&lt;/p&gt;

&lt;h1&gt;
  
  
  What now?
&lt;/h1&gt;

&lt;p&gt;Right now I am in a waiting game. There seems to be more demand for mental health professionals than supply. Especially during this time where everyone is forced into new social patterns. I had to wait a few weeks for a therapist appointment. And am currently waiting another month to see a psychiatrist to get further testing and probably a prescription. Meanwhile I am languishing in the guilt and shame of what most employers would deem inexcusable negligence. An unfortunate but familiar companion to me.&lt;/p&gt;

&lt;p&gt;Normally I would have already sought another job, for all our sakes. Except I love my company and my team, and we are on the verge of some growth. Growth that could provide me an opportunity to change roles to work that fits me better. And I could get medicine soon. Which might make things better. I don't even know what the world might be like if I am able to directly control what I can focus on. Or if I am far too practiced in my current approaches to be able to. I tear up when I think it possible that I could have a daily routine last for more than a couple of weeks. It seems too much to hope.&lt;/p&gt;

&lt;p&gt;So here I am with seemingly everything on the line, waiting to see what situation resolves first. It is not the most comfortable state.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There is a strange phenomenon when someone discovers they have ADHD as an adult. After some initial joy there is a period of mourning. I wrote about mine &lt;a href="https://dev.to/kspeakman/adhd-the-grief-of-discovery-1njp"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Take away
&lt;/h1&gt;

&lt;p&gt;The reason I wrote this post was to share my experiences and raise awareness. ADHD is not a made-up thing or because parents are being too soft (opinions which I have held in the past). It is not in the person's control. It is a significant problem. It is easy to miss, and people with it are conditioned to hide their difficulties. People who have it end up with self-esteem problems because they are constantly expected to do things which are very hard or impossible for them but 90% of people seem to do with ease.&lt;/p&gt;

&lt;p&gt;Consider that you never really know what is going on with a person's behavior. And they may not know either! If you feel safe doing so, lead with compassion and offer your support. Help them find out what's wrong. You could be a life-saver.&lt;/p&gt;

</description>
      <category>mentalhealth</category>
    </item>
    <item>
      <title>Clojure validation in 30 lines</title>
      <dc:creator>Kasey Speakman</dc:creator>
      <pubDate>Tue, 20 Oct 2020 09:46:55 +0000</pubDate>
      <link>https://forem.com/kspeakman/clojure-validation-in-30-lines-358c</link>
      <guid>https://forem.com/kspeakman/clojure-validation-in-30-lines-358c</guid>
      <description>&lt;p&gt;&lt;em&gt;A function composition approach to validation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In the previous post, I offered some critique of the validation approaches I found. In his post I bash together an alternative solution.&lt;/p&gt;

&lt;h1&gt;
  
  
  Goals
&lt;/h1&gt;

&lt;p&gt;Here are the things I was interested in.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;validation definitions are functions&lt;/li&gt;
&lt;li&gt;composable from smaller pieces&lt;/li&gt;
&lt;li&gt;no macros&lt;/li&gt;
&lt;li&gt;simple errors&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  5 functions
&lt;/h1&gt;

&lt;p&gt;There are 5 core functions to compose validations.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;fn&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This creates a basic validation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="w"&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;validation.core&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&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="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;v/fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:required&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;not-empty&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="nf"&gt;validate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"asdf"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"asdf"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:v/error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:required&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="nf"&gt;v/error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:required&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any function can be used for validation provided it returns nil if anything goes wrong. When that happens, &lt;code&gt;:v/error&lt;/code&gt; is returned with the provided trait (&lt;code&gt;:required&lt;/code&gt; here).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You provide a trait so you know exactly the errors to expect.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;and&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This combinator can be used to send a value through a series of transformations, each of which might fail. The end value will be returned if all steps are successful. Otherwise the failing trait will be returned as an error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;v/and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;v/fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:required&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;not-empty&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="nf"&gt;v/fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:keyword&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;keyword&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="nf"&gt;validate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"asdf"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:asdf&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:v/error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:required&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;blockquote&gt;
&lt;p&gt;Note: This is not a strong validation because it does not deal well with non-string inputs or whitespace.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;seq&lt;/code&gt;, &lt;code&gt;hmap&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Validate a sequence.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;v/seq&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;v/and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;v/fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:required&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;not-empty&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="nf"&gt;v/fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:keyword&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;keyword&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="nf"&gt;validate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;:a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:b&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="nf"&gt;validate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;:a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:v/error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:required&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;Validate a hash-map.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;v/hmap&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;v/fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:required&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;not-empty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="no"&gt;:b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;v/and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;v/fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:required&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;not-empty&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="nf"&gt;v/fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:keyword&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;keyword&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="nf"&gt;validate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="n"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:b&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="nf"&gt;validate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="n"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:v/error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:required&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="nb"&gt;map&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="no"&gt;:a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&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="nf"&gt;validate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;})])&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:v/error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:required&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;h2&gt;
  
  
  &lt;code&gt;or&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This combinator returns the first passing validation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;v/or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:date-or-int&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;v/fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;str-&amp;gt;date&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="nf"&gt;v/fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;str-&amp;gt;int&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 takes an additional trait since returning every failing trait would not would not be entirely useful.&lt;/p&gt;

&lt;h1&gt;
  
  
  Validation functions
&lt;/h1&gt;

&lt;p&gt;The 30 lines of code are just the definition of the 5 core functions and 2 error checks. Validation functions are still needed to plug into them. Clojure has some built-in functions that can be used but you are probably going to want more. Fortunately they are easy to create. Here are some examples.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;non-blank&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&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="nb"&gt;when&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&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="nf"&gt;not-empty&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&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="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;in-range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;min-val&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;max-val&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="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;v&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="nb"&gt;when&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;min-val&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="nb"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;max-val&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;v&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;When a validation takes multiple parameters as &lt;code&gt;in-range&lt;/code&gt; does above, it should return a function to take the last parameter. This bakes in the necessary partial application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;v/fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;in-range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&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="nf"&gt;validate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:v/error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:range&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;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;I really like the function composition approach to validation. It is composable and extensible. Its implementation is very small (see below). Which means I can easily change or add more functions. I find it works quite well for user input validation.&lt;/p&gt;




&lt;h3&gt;
  
  
  The 30 lines
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;ns&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;validation.core&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;:refer-clojure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:exclude&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;seq&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="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;trait&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;if-some&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;%&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;::error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;trait&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="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;loop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;%&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fs&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="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;contains?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;::error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;first&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fs&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="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;nil?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;recur&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&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="nb"&gt;next&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fs&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="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;trait&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;loop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;possible&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;%&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fs&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="nf"&gt;if-not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;contains?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;possible&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;::error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="n"&gt;possible&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;first&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fs&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="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;nil?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;f&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="no"&gt;::error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;trait&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="nf"&gt;recur&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&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="nb"&gt;next&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fs&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="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hmap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;reduce-kv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&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="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;seq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;map&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&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="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;v&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="nb"&gt;contains?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;::error&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="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;v&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="no"&gt;::error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&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;



</description>
      <category>clojure</category>
      <category>validation</category>
    </item>
  </channel>
</rss>
