<?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>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>
    <item>
      <title>Validation approaches in Clojure</title>
      <dc:creator>Kasey Speakman</dc:creator>
      <pubDate>Fri, 16 Oct 2020 01:51:59 +0000</pubDate>
      <link>https://forem.com/kspeakman/validation-approaches-in-clojure-3e0e</link>
      <guid>https://forem.com/kspeakman/validation-approaches-in-clojure-3e0e</guid>
      <description>&lt;p&gt;&lt;em&gt;Classifying the different approaches I've seen and some of their trade-offs.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Lately I have been struggling to come up with a good validation library in Clojure. And it led me to discover and evaluate the different approaches.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why not use Spec?
&lt;/h1&gt;

&lt;p&gt;Spec is great at type specification, but is not so great for validation. Because spec intentionally &lt;a href="https://stackoverflow.com/questions/45188850/is-use-of-clojure-spec-for-coercion-idiomatic"&gt;avoids data transformations&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Especially on the web, most input values are strings. And frequently they need to be sanitized and transformed before they are ready to have logic performed on them. Since Spec does not allow you to transform data during validation, it is not sufficient for this use case.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Well, sortof&lt;/strong&gt;&lt;br&gt;
Using spec for transformation is discouraged by the authors as mentioned in the link and &lt;a href="https://clojure.org/about/spec#_informational_vs_implementational"&gt;alluded to&lt;/a&gt; in the documentation. However, Sean Corfield rightly pointed out in Clojure Slack that Spec is extensible to allow data transformations. And his company has &lt;a href="https://github.com/worldsingles/web-specs"&gt;a library&lt;/a&gt; which does so.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Approaches
&lt;/h1&gt;

&lt;p&gt;Here are some approaches I have seen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reverse-engineer Specs
&lt;/h2&gt;

&lt;p&gt;This is a common theme for Clojure validation libraries. You first make a spec, and then the library will attempt to make the data you provide fit into that spec. So if the field is &lt;code&gt;int?&lt;/code&gt; and it finds a string, it will automatically convert using a known string-to-int conversion.&lt;/p&gt;

&lt;p&gt;The big problem with this approach is the lack of control over the validation process. It is very common for me to have transformations and validations both before and after the user input is coerced.&lt;/p&gt;

&lt;p&gt;For example when the user enters a monetary value, I trim and verify the field is not empty first (as a separate error) before bothering to convert to a number. After converting, I transform the floating point number entered by the user into a whole number of cents. Neither the blank-ness nor the cent transformation is representable by Spec and cannot be reverse engineered from an &lt;code&gt;int?&lt;/code&gt; spec.&lt;/p&gt;

&lt;p&gt;Some of the libraries provide extra APIs to plug in those things. But then it becomes learning the custom API of that library, undercutting the point of using Spec. And spreading out the validation steps in different places.&lt;/p&gt;

&lt;h2&gt;
  
  
  Declarative validations
&lt;/h2&gt;

&lt;p&gt;These kinds of libraries create a DSL for validations so that you can specify &lt;code&gt;:required&lt;/code&gt; for example. And it has code that it invokes when it sees that keyword.&lt;/p&gt;

&lt;p&gt;The main trade-offs with this approach is learning the library's DSL (non-portable knowledge). And then figuring out how to work around it when it lacks enough expressiveness for your exact problem. These are often developed based on the problems the author encountered, and are not general enough to a wide range of uses. Once the DSL becomes general enough, then it has become a new language!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is also explained in the Spec overview section &lt;a href="https://clojure.org/about/spec#_code_is_data_not_vice_versa"&gt;Code is data (not vice versa)&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Composition and Partial application
&lt;/h2&gt;

&lt;p&gt;This is what &lt;a href="https://github.com/XeSoft/ValiData"&gt;I used&lt;/a&gt; in F# to great effect. Validations are expressed in code and value transformations chain forward into the next one. Since it is code you can do most anything. It mainly works as a concise option because F# is curried by default and partial application is automatic.&lt;/p&gt;

&lt;p&gt;You see, in F# functions always expect a precise number of arguments. So if you specify less arguments, F# will automatically partially apply those arguments and return a new function which only takes the remaining arguments. That allows you to tersely compose functions to do everything you need for the validation. Then apply data values later.&lt;/p&gt;

&lt;p&gt;Clojure functions can take a variable number of arguments, so automatic currying and partial application are not available. Instead, the &lt;code&gt;partial&lt;/code&gt; function is used to do partial application and/or &lt;code&gt;comp&lt;/code&gt; for function composition.&lt;/p&gt;

&lt;p&gt;I haven't found Clojure validation libraries which take this approach. Although I believe it to be possible.&lt;/p&gt;

&lt;p&gt;It is also fair to say that composed and partial functions are not particularly easy to read at first. It takes practice to get accustomed to them. But the knowledge is generally portable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Macros
&lt;/h2&gt;

&lt;p&gt;This path adds new semantics to the language so that you can use code but also have a desired expressiveness. This is the path that Spec chose.&lt;/p&gt;

&lt;p&gt;The main trade-off with macros is the same one that always exists with macros. Unlike regular functions, the interaction of different macros can become unpredictable. Since macros literally transform the code. (See &lt;a href="https://clojure.org/guides/core_async_go#_unsupported_constructs_and_other_limitations_in_go_blocks"&gt;go-block limitations&lt;/a&gt;, for example.)&lt;/p&gt;

&lt;p&gt;To put it another way, macros specialize the language toward one purpose. And that specialization may conflict with another. So this approach has to be undertaken very thoughtfully.&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;So that is a sampling of the different types of validation approaches that I found in and around Clojure.&lt;/p&gt;

&lt;p&gt;Personally I prefer the composition approach without macros. I am still trying to find a way to represent this well in Clojure. If I can find that, the implementation should be small and user extensible. Let me know if I missed a library that is already taking this approach.&lt;/p&gt;

</description>
      <category>clojure</category>
      <category>validation</category>
    </item>
    <item>
      <title>Life after Elm: What we did</title>
      <dc:creator>Kasey Speakman</dc:creator>
      <pubDate>Mon, 12 Oct 2020 00:32:21 +0000</pubDate>
      <link>https://forem.com/kspeakman/life-after-elm-what-we-did-4hkp</link>
      <guid>https://forem.com/kspeakman/life-after-elm-what-we-did-4hkp</guid>
      <description>&lt;p&gt;It's been just over 2 years since I wrote &lt;a href="https://dev.to/kspeakman/elm-019-broke-us--khn"&gt;Elm 0.19 Broke Us 💔&lt;/a&gt;. Where did we go from there and how did it compare?&lt;/p&gt;

&lt;h1&gt;
  
  
  Elmish and F &lt;em&gt;#&lt;/em&gt;
&lt;/h1&gt;

&lt;p&gt;For our latest project, we chose Elmish and F#. Elmish provides Elm-like abstractions for F#. Fable translates F# to JS. And for view rendering it uses React. Elmish is general enough that it also works in other UI contexts. There are libraries that adapt it to desktop and mobile applications.&lt;/p&gt;

&lt;p&gt;F# was mostly a smooth transition for us. The language is in the same ML family as Elm so the syntax differences are quite minor. The biggest hangup was probably the capitalization differences. We were already using F# on the back-end, so there was at least a little familiarity with it from front-end devs. (Who are now back-end devs too.)&lt;/p&gt;

&lt;h1&gt;
  
  
  Unnecessary Worries
&lt;/h1&gt;

&lt;p&gt;Some of the big worries that we had about transitioning turned out more theoretical than actual. Here are the ones we maybe focused too much on before switching.&lt;/p&gt;

&lt;h2&gt;
  
  
  Auto-formatting
&lt;/h2&gt;

&lt;p&gt;Our normal coding workflow depended pretty heavily on the official Elm auto-format. We would often just vomit (especially UI) code into the editor wherever and hit save to watch it snap into its right place. So we thought this would be a big downside since there was no official or obvious path to do this in F#.&lt;/p&gt;

&lt;p&gt;Developing a product in F# Elmish for almost 2 years now, I can say that it didn't turn out to be a big deal. One of the major factors is that newlines count as whitespace in F#. Getting all those separators rightly placed is one of the main benefits of Elm auto-format and with F# that requirement is just gone. Secondly, we had the freedom to express code in ways which are more convenient for our usage. We experimented and eventually chose this form.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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="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="n"&gt;str&lt;/span&gt; &lt;span class="s2"&gt;"blah"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Without leading symbols (list bracket, comma/semi-colon) in child content, this makes code at any level more friendly to being moved around. And instead of hitting Ctrl-S to auto-format, we just hit the indent or outdent keys a few times to line it up.&lt;/p&gt;

&lt;p&gt;And lastly, Elm auto-format created tons of extra vertical whitespace in some common cases. Especially &lt;code&gt;let&lt;/code&gt; and &lt;code&gt;case&lt;/code&gt; blocks, which are excruciatingly over-spaced sometimes. We would catch ourselves avoiding &lt;code&gt;let&lt;/code&gt; to avoid a space explosion. (&lt;code&gt;case&lt;/code&gt; was unavoidable so you just accept it.) So this isn't a thing anymore. And in fact F# has an abbreviated &lt;code&gt;let&lt;/code&gt; syntax, so we use it a lot more.&lt;/p&gt;

&lt;p&gt;This doesn't quite equate with Elm's auto-format, but overall I would say it is equivalent or better quality of life.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dispatch function
&lt;/h2&gt;

&lt;p&gt;In Elm, when you want to trigger a Msg from a DOM event, you just tell it what message you want to send. Example: &lt;code&gt;onClick Clicked&lt;/code&gt;. But Elmish gives you a dispatch function, and in the view's DOM event you have to invoke dispatch with your messages in order to send it, which is a side effect. In F# this looks more like this: &lt;code&gt;OnClick (fun _ -&amp;gt; dispatch Clicked)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I suppose the main concern with this approach was devs doing weird side-effecty things and making views less maintainable. But that concern has not materialized so far. Another minor issue is that DOM event handlers are longer to type since you pretty much always use a lambda function and call dispatch in it.&lt;/p&gt;

&lt;p&gt;I asked the maintainer of Elmish about this, who said they intended to write a layer on top of Elmish.React to match what Elm does. But it just never seemed necessary. And by keeping the dispatch function approach, it made Elmish portable to more UI targets without modification.&lt;/p&gt;

&lt;p&gt;Likewise, we could have written our own library to translate Elmish's way into the Elm way, but it just did not seem worth it. So ultimately we just accept that DOM event handlers are slightly longer to write. We write helper functions for any especially tedious ones. And although it is less elegant to read, there is a minor upside of less separation from our source material (the DOM). In Elm, you have to write DOM decoders for custom event behavior. But not in F#.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I never found much benefit in decoding DOM values for DOM events anyway. If there was an error, it was silently ignored (I assume in order to have no runtime exceptions) and I usually wanted to see it to know I wired up something wrong.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Side effects everywhere
&lt;/h2&gt;

&lt;p&gt;F# allows side effects anywhere as a pragmatic language design choice. That means you can run side effects in &lt;code&gt;update&lt;/code&gt; and F# will not complain. Doing so undercuts the value of the MVU pattern, and obviously things are going to slip through, right? This didn't turn out to be a problem for us. But only because I invested the time to design and designate a specific area where side effects happen.&lt;/p&gt;

&lt;p&gt;To do this I added a function and data type to the MVU pattern. The function is &lt;code&gt;perform&lt;/code&gt; and the data type is &lt;code&gt;Effect&lt;/code&gt;. Looking at it from an Elm perspective, &lt;code&gt;Effect&lt;/code&gt; is roughly equivalent to &lt;code&gt;Cmd&lt;/code&gt;. And &lt;code&gt;perform&lt;/code&gt; is roughly equivalent to JS code you invoked through ports. But with some major quality of life differences.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Adding in &lt;code&gt;perform&lt;/code&gt; and &lt;code&gt;Effect&lt;/code&gt; was done purely in user space. It only took a couple of tiny adapter functions to make it work with Elmish.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;First, Effect is defined by you and can be value equatable. That means not only can you test that the model was updated correctly. But you can also test that the correct side effects were invoked with the correct options. By just using an equality check -- no mocking. 🤯 You can't even do that in Elm. Since Effect is yours to control, unlike Cmd, it is extensible to do whatever side effects you need.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Slight downside, that also means you need to create effects for common scenarios that would already be built into Elm like HTTP requests. On the plus side, there are plenty of libraries that provide F# idioms on top of common JS APIs. For example, we use &lt;a href="https://github.com/Zaid-Ajaj/Fable.SimpleHttp"&gt;Fable.SimpleHttp&lt;/a&gt; to make HTTP requests.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Secondly, the &lt;code&gt;perform&lt;/code&gt; function is written in F#. The reason that is significant is because Elm forces you to write side effects in JS (if you need something other than what is built-in). And the data has to go through an encoding/decoding process to pass between JS and Elm. Not anymore with &lt;code&gt;perform&lt;/code&gt;. And if you need to call a JS function as part of your side effect, JS interop in F# is trivial in most cases. Open a namespace and directly call a JS function as though it were in F#. Synchronous, asynchronous, try/catch -- it is all available to you.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can also export F# code so that it can be called as a JS module. Or import JS modules into F#.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;All tolled, side effects everywhere was the largest fear with switching from Elm to F#. But the flexibility provided by being able to call side effects from F# brought the single biggest quality of life improvement.&lt;/p&gt;

&lt;h1&gt;
  
  
  Downsides
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Beginner Experience
&lt;/h2&gt;

&lt;p&gt;Elm is not just a language. It is also a platform and a set of tools. Its documentation therefore can take an end-to-end approach and is quite good for beginners. And our company hires beginners and teaches them to program.&lt;/p&gt;

&lt;p&gt;On the other hand our F# solution is a mix of different techs (Fable, Elmish, React, F#), each with their own varying levels of documentation. And mostly they are not aimed at complete beginners, nor are they always cohesive with each other. Even F# introductions usually assume you are coming from OO languages.&lt;/p&gt;

&lt;p&gt;I setup some things for them, but invariably they have to be exposed to a lot more of the JS ecosystem horrors earlier than with Elm. (Like most front-end tech.) Which can be overwhelming and imposter-syndrome-triggering as a new dev. It happens in Elm too, but to a lesser degree.&lt;/p&gt;

&lt;h1&gt;
  
  
  Q &amp;amp; A
&lt;/h1&gt;

&lt;p&gt;I pretend you asked a question and then answer it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Are you rewriting Elm apps into F#?
&lt;/h2&gt;

&lt;p&gt;Not as a goal unto itself. We still have Angular 1.x apps here and there, so if we didn't rewrite those yet... As long as the 0.18 toolchain keeps working, we can keep maintaining it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do you regret switching to F#?
&lt;/h2&gt;

&lt;p&gt;No. On balance it is an improvement from my perspective.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do you regret posting the complaint about Elm?
&lt;/h2&gt;

&lt;p&gt;On the whole, no. There was the expected vitriol which was no fun, but it also seemed to put a voice to the way others were feeling. And they commented to say so.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who was at fault according to you?
&lt;/h2&gt;

&lt;p&gt;Ok, I see where this is going... But I will answer your question. Nobody was at fault. I see it as just an unfortunate combination of circumstances.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't you think you will just find fault in any community you are a member of?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;I refuse to join any club that would have me as a member.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Groucho Marx&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Jokes aside, the whole reason you take part in a community is to help advance whatever that community is doing. As part of "helping", there are always some members who disagree with current direction or want "bad" features. But most communities I've been in handled those situations... differently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Has Elm "improved" according to you since then?
&lt;/h2&gt;

&lt;p&gt;I hope so! I have not kept up with it at all. Are we done with this line of questioning yet?&lt;/p&gt;

&lt;h2&gt;
  
  
  Is there a template you recommend for Elmish?
&lt;/h2&gt;

&lt;p&gt;I pieced ours together so there isn't one that I have direct experience with. However the SAFE Stack 2.0 templates seem like good starting points. I was glad to see the addition of the minimal template.&lt;/p&gt;

</description>
      <category>fsharp</category>
      <category>functional</category>
      <category>mvu</category>
    </item>
    <item>
      <title>You are being manipulated</title>
      <dc:creator>Kasey Speakman</dc:creator>
      <pubDate>Tue, 22 Sep 2020 03:42:42 +0000</pubDate>
      <link>https://forem.com/kspeakman/you-are-being-manipulated-29d7</link>
      <guid>https://forem.com/kspeakman/you-are-being-manipulated-29d7</guid>
      <description>&lt;p&gt;I wish this were a click-bait title. Sadly not. It is also not trying to push you into a political party or a religion or a business interest. It is an invitation to look at what is being done with our modern media.&lt;/p&gt;

&lt;p&gt;Anything I probably want to say in this post is well covered by a documentary called &lt;a href="https://www.thesocialdilemma.com/"&gt;The Social Dilemma&lt;/a&gt;. Most of the interviews are with people who made the tech we all use. From management to the developers themselves.&lt;/p&gt;

&lt;p&gt;I guess I won't rehearse all the things they presented there. But I will say this about the perceived dividedness that we face today. Media makes the most money by selling you the weapons and motivation to fight your neighbor. Social media does it at scale.&lt;/p&gt;

&lt;p&gt;Please watch the documentary so you understand how it is happening. &lt;em&gt;Insert borrowed Netflix password meme here.&lt;/em&gt; And as software developers consider how your company is using your creations. And I do not mean ICE using github... I mean something much bigger. Is the business model -- the engine that drives the company -- humane? Or does it data-mine and sell its users to advertisers? That is the fastest way to make money, but it also brings out the worst in humanity. Because what sells best are the things that appeal to our basest instincts.&lt;/p&gt;

&lt;p&gt;I heard a song once... went something like this: "Is this the world you want? You're making it." Especially true for developers these days.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
