<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Symbolica</title>
    <description>The latest articles on DEV Community by Symbolica (@symbolica).</description>
    <link>https://dev.to/symbolica</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%2Forganization%2Fprofile_image%2F4145%2F30867480-4931-44a7-abb3-1aadb2607df5.png</url>
      <title>DEV Community: Symbolica</title>
      <link>https://dev.to/symbolica</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/symbolica"/>
    <language>en</language>
    <item>
      <title>Typesafe F# configuration binding</title>
      <dc:creator>Matt Thornton</dc:creator>
      <pubDate>Sun, 28 Nov 2021 09:34:51 +0000</pubDate>
      <link>https://dev.to/symbolica/typesafe-f-configuration-binding-16gp</link>
      <guid>https://dev.to/symbolica/typesafe-f-configuration-binding-16gp</guid>
      <description>&lt;p&gt;At &lt;a href="https://www.symbolica.dev" rel="noopener noreferrer"&gt;Symbolica&lt;/a&gt; we're building a symbolic execution service that explores every reachable state of a user's program and verifies assertions at each of these states to check that the program is correct. By default it will check for common undefined behaviours, such as out-of-bounds memory reads or divide by zero, but it can also be used with custom, application specific, assertions too just like the kind you'd write in a unit test. Seen from this perspective it's kind of like &lt;a href="https://fscheck.github.io/FsCheck/" rel="noopener noreferrer"&gt;FsCheck&lt;/a&gt; (or Haskell's &lt;a href="https://hackage.haskell.org/package/QuickCheck" rel="noopener noreferrer"&gt;QuickCheck&lt;/a&gt; or Python's &lt;a href="https://hypothesis.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;Hypothesis&lt;/a&gt;), but much more exhaustive and without the randomness.&lt;/p&gt;

&lt;p&gt;As much as we like finding bugs with Symbolica, we prefer to not write any in the first place. Our first line of defence is a strong type system, so that we can try to design types that make invalid states impossible and let the compiler tell us off when we make a mistake. For that reason we've opted to build our service using F# as it also interops nicely with the core part of our &lt;a href="https://github.com/Symbolica/Symbolica" rel="noopener noreferrer"&gt;symbolic executor&lt;/a&gt; which is written in C#.&lt;/p&gt;

&lt;p&gt;One of the many things we love about F# is that, by default, it doesn't permit &lt;code&gt;null&lt;/code&gt; as a regular value. This feature eliminates a whole class of errors caused by &lt;code&gt;null&lt;/code&gt; values, most notably the cursed &lt;code&gt;NullReferenceException&lt;/code&gt;. Another feature that we like is having access to the vast wealth of .NET libraries that exist. However, many of these are written in C# and so they are often places where &lt;code&gt;null&lt;/code&gt; values can sneak into an F# program through the backdoor at runtime. &lt;/p&gt;

&lt;p&gt;One area where this was frequently biting us was the binding of configuration data using the &lt;code&gt;Microsoft.Extensions.Configuration&lt;/code&gt; library. Due to this and other problems that we'll go into below, we created a safer alternative for configuration binding for F# projects called &lt;code&gt;Symbolica.Extensions.Configuration.FSharp&lt;/code&gt; and open-sourced it on &lt;a href="https://github.com/Symbolica/Symbolica.Extensions.Configuration.FSharp" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&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/Symbolica" rel="noopener noreferrer"&gt;
        Symbolica
      &lt;/a&gt; / &lt;a href="https://github.com/Symbolica/Symbolica.Extensions.Configuration.FSharp" rel="noopener noreferrer"&gt;
        Symbolica.Extensions.Configuration.FSharp
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Provides a safe API for binding the dotnet IConfiguration to types in F#.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Symbolica.Extensions.Configuration.FSharp&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Provides a safe API for binding an F# type from the dotnet &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.configuration.iconfiguration?view=dotnet-plat-ext-5.0" rel="nofollow noopener noreferrer"&gt;&lt;code&gt;IConfiguration&lt;/code&gt;&lt;/a&gt; interface. It is an F#-friendly alternative to using the reflection-based &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.configuration.configurationbinder.bind?view=dotnet-plat-ext-5.0" rel="nofollow noopener noreferrer"&gt;&lt;code&gt;ConfigurationBinder.Bind&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Motivation&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Out-of-the-box dotnet provides what it calls the &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-5.0" rel="nofollow noopener noreferrer"&gt;"the Options pattern"&lt;/a&gt; which it describes as:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The options pattern uses classes to provide strongly typed access to groups of related settings.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Whilst this might be "strongly typed" in the sense that you're interacting with statically typed options objects, the binding mechanism is not strictly safe and so the static types are often a lie. This leads to a few notable problems, especially when working with it from F#.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It's a large source of &lt;code&gt;NullReferenceException&lt;/code&gt;s because the binder will hapily set a value to &lt;code&gt;null&lt;/code&gt; if it's missing in the underlying config. This means your F# type is probably lying to you about the fact its value cannot be null. F# developers would rather model…&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Symbolica/Symbolica.Extensions.Configuration.FSharp" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  The problems with &lt;code&gt;Microsoft.Extensions.Configuration.Binder&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The best way to highlight the shortcomings of the defacto config binder is with an example. Let's say we want to model some logging options in our code. We might start out with a simple record type like this to represent the options.&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;LoggingOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; 
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Level&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;Sink&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;Level&lt;/code&gt; represents how verbose we want the logging output to be, e.g. &lt;code&gt;"Debug"&lt;/code&gt; or &lt;code&gt;"Error"&lt;/code&gt; etc and &lt;code&gt;Sink&lt;/code&gt; is where we want to send the logs, for example it might be &lt;code&gt;"Console"&lt;/code&gt; or &lt;code&gt;"File"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's test this out with a little fsx script that we can run with FSI.&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="p"&gt;#&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="s2"&gt;"nuget: Microsoft.Extensions.Configuration.Binder"&lt;/span&gt;

&lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="nn"&gt;Microsoft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Configuration&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;LoggingOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Level&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="nc"&gt;Sink&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="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="nc"&gt;ConfigurationBuilder&lt;/span&gt;&lt;span class="bp"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AddInMemoryCollection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"Logging:Level"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Debug"&lt;/span&gt;
              &lt;span class="s2"&gt;"Logging:Sink"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Console"&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;Map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ofList&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Build&lt;/span&gt;&lt;span class="bp"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Logging"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="nc"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LoggingOptions&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we're just seeding the in memory configuration provider with a dictionary of config data and then attempting to retrieve and bind the &lt;code&gt;LoggingOptions&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem 1: Mutable data and &lt;code&gt;null&lt;/code&gt; values
&lt;/h3&gt;

&lt;p&gt;If we run the above script you might be expecting it to print out a &lt;code&gt;LoggingOptions&lt;/code&gt; with a &lt;code&gt;Level&lt;/code&gt; of &lt;code&gt;"Debug"&lt;/code&gt; and a &lt;code&gt;Sink&lt;/code&gt; of &lt;code&gt;"Console"&lt;/code&gt;. However, we actually hit a different problem. The above script throws the following exception.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;System.InvalidOperationException: Cannot create instance of type 'FSI_0008+LoggingOptions' because it is missing a public parameterless constructor.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's because an F# record doesn't contain a parameterless constructor, because all of the record’s properties must be properly initialised and &lt;code&gt;null&lt;/code&gt; isn't an allowed value. To make matters worse, the defacto binder mandates that the properties of the type being bound must be settable too, breaking immutability and making the use of a record to model options kind of pointless.&lt;/p&gt;

&lt;p&gt;There are two typical workarounds to this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Define a mutable class instead of a record for the options type, like we would in C#.&lt;/li&gt;
&lt;li&gt;Add the &lt;code&gt;[&amp;lt;CLIMutable&amp;gt;]&lt;/code&gt; attribute to the &lt;code&gt;LoggingOptions&lt;/code&gt; record.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Neither of these are particularly pleasing. The first one means we have to give up on having immutable options types and the rest of the code base has to deal with the added complexity of potential mutability. The second is basically a hack which provides a mutable backdoor at runtime to our immutable type.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;[&amp;lt;CLIMutable&amp;gt;]&lt;/code&gt; actually opens up a can of worms because our types are now deceiving us. Our simple record purports to be immutable and never contain &lt;code&gt;null&lt;/code&gt; values and so in the rest of the code base we program as if this is the case. On the other hand the config binder isn’t abiding by these compile time invariants and may in fact initialise the record’s properties as &lt;code&gt;null&lt;/code&gt; at runtime.&lt;/p&gt;

&lt;p&gt;To see this in action, let's rerun the above example, but this time with the &lt;code&gt;[&amp;lt;CLIMutable&amp;gt;]&lt;/code&gt; attribute added to the &lt;code&gt;LoggingOptions&lt;/code&gt; and a missing value for the &lt;code&gt;Level&lt;/code&gt; In the raw config. The modified script 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="p"&gt;#&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="s2"&gt;"nuget: Microsoft.Extensions.Configuration.Binder"&lt;/span&gt;

&lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="nn"&gt;Microsoft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Configuration&lt;/span&gt;

&lt;span class="p"&gt;[&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CLIMutable&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;LoggingOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Level&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="nc"&gt;Sink&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="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="nc"&gt;ConfigurationBuilder&lt;/span&gt;&lt;span class="bp"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AddInMemoryCollection&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt; &lt;span class="s2"&gt;"Logging:Sink"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Console"&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;Map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ofList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Build&lt;/span&gt;&lt;span class="bp"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Logging"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="nc"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LoggingOptions&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running it produces this output.&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;val&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LoggingOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Level&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
                           &lt;span class="nc"&gt;Sink&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Console"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We see that the type system has lied to us because the value of &lt;code&gt;Level&lt;/code&gt; was actually &lt;code&gt;null&lt;/code&gt; at runtime. In this case it's relatively harmless, but in a real application it's likely that we'll have a more complex hierarchy of option types and so we'd end up trying to dereference a potentially &lt;code&gt;null&lt;/code&gt; object leading to the dreaded &lt;code&gt;NullReferenceException&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When working in F# we'd rather the config binder returned a &lt;code&gt;Result&lt;/code&gt; if the config couldn't be parsed and allow us to use an &lt;code&gt;Option&lt;/code&gt; type for config data that is, well, optional. Which leads us to the next problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem 2: No native support for binding DUs
&lt;/h3&gt;

&lt;p&gt;As the defacto binder uses reflection to bind the raw config to "strongly typed objects", it only has support for a limited set of types. This includes all the primitive types, like &lt;code&gt;int&lt;/code&gt; and &lt;code&gt;string&lt;/code&gt; and a few of the common BCL collection types like &lt;code&gt;List&lt;/code&gt; and &lt;code&gt;Dictionary&lt;/code&gt;. This is frustrating for both C# and F# developers that wish to use more complex types to model their options.&lt;/p&gt;

&lt;p&gt;Particularly frustrating for F# developers though is that this means it doesn't support discriminated unions (DUs) and therefore doesn't support types like &lt;code&gt;Option&lt;/code&gt;. To highlight this let's imagine we wanted to improve our &lt;code&gt;LoggingOptions&lt;/code&gt; so that the &lt;code&gt;Level&lt;/code&gt; was restricted to a discrete set of values. To do this we'll create a DU called &lt;code&gt;LoggingLevel&lt;/code&gt; and use it as the type for the &lt;code&gt;Level&lt;/code&gt; property.&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="p"&gt;#&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="s2"&gt;"nuget: Microsoft.Extensions.Configuration.Binder"&lt;/span&gt;

&lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="nn"&gt;Microsoft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Configuration&lt;/span&gt;

&lt;span class="p"&gt;[&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RequireQualifiedAccess&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;LogLevel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Debug&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Info&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Warning&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;

&lt;span class="p"&gt;[&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CLIMutable&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;LoggingOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LogLevel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nc"&gt;Sink&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="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="nc"&gt;ConfigurationBuilder&lt;/span&gt;&lt;span class="bp"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AddInMemoryCollection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"Logging:Level"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Debug"&lt;/span&gt;
              &lt;span class="s2"&gt;"Logging:Sink"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Console"&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;Map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ofList&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Build&lt;/span&gt;&lt;span class="bp"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Logging"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="nc"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LoggingOptions&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're now supplying a config dictionary that looks correct, it has properties for both of &lt;code&gt;"Logging:Level"&lt;/code&gt; and &lt;code&gt;"Logging:Sink"&lt;/code&gt;, so let's run it and see what the output is.&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;val&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LoggingOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Level&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
                           &lt;span class="nc"&gt;Sink&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Console"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we can see here that the binder has silently failed to bind the &lt;code&gt;Level&lt;/code&gt; property now that its type is &lt;code&gt;LoggingLevel&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;If we want to bind more complex type, we'll first have to bind to a simple type, like a &lt;code&gt;string&lt;/code&gt;, and then write a parser ourselves to turn that into a &lt;code&gt;LoggingLevel&lt;/code&gt;. That’s a slippery slope because it then probably means having something like a &lt;code&gt;ParsedLoggingConfig&lt;/code&gt; which we create from the more loosely typed &lt;code&gt;LoggingConfig&lt;/code&gt;. Resulting in us needing to define a fair amount of config parsing “boilerplate” anyway. &lt;/p&gt;

&lt;h3&gt;
  
  
  Problem 3: Parse, don't validate
&lt;/h3&gt;

&lt;p&gt;The defacto binder doesn't really give us much help when our configuration is faulty. We can write some &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-5.0#options-validation" rel="noopener noreferrer"&gt;options validators&lt;/a&gt; and wire these up with DI, but as Alexis King has taught us - &lt;a href="https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/" rel="noopener noreferrer"&gt;parse, don't validate&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In short, "parse, don't validate" tells us that it's better to parse data into a type, that once constructed must be valid, than it is to read the data into a more loosely typed object and then run some post-validation actions over the values to make sure they're correct. The primary reason being that if we know that our type only permits valid values, then we no longer have to wonder whether or not it's already been validated.&lt;/p&gt;

&lt;p&gt;The defacto configuration binder doesn't make it easy to adhere to this. It's easy to forget to register a validator for the options and then when they're accessed at runtime we instead get a rather unhelpful &lt;code&gt;null&lt;/code&gt; value, like we observed earlier. What we'd prefer is for the compiler to prevent us from making such a mistake, by enforcing validation through the type system.&lt;/p&gt;

&lt;p&gt;To give a specific example, let's imagine we want to be able to restrict the logging level to only have the values, &lt;code&gt;"Info"&lt;/code&gt;, &lt;code&gt;"Debug"&lt;/code&gt;, &lt;code&gt;"Warning"&lt;/code&gt; and &lt;code&gt;"Error"&lt;/code&gt;. We've already seen we can't use a DU to model this. So we have no way of knowing whether or not &lt;code&gt;Level&lt;/code&gt; is valid when we come to use it, all we know is that it's a string. So if we want to be sure, we're forced to keep validating the logging level at every point of use.&lt;/p&gt;

&lt;h2&gt;
  
  
  A better binder for F#
&lt;/h2&gt;

&lt;p&gt;Given these shortcomings we decided to write our own config binder with the following design goals in mind:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Binding failures should be expected and be reflected in the type returned from the binder. We should be made to deal with the unhappy path.&lt;/li&gt;
&lt;li&gt;Binding should not break immutability.&lt;/li&gt;
&lt;li&gt;Binding should work for all types including complex user defined types.&lt;/li&gt;
&lt;li&gt;Binding should be composable, such that if I can bind a type &lt;code&gt;X&lt;/code&gt; which is then later used within &lt;code&gt;Y&lt;/code&gt;, I should be able to reuse the binder for &lt;code&gt;X&lt;/code&gt; when defining the binder for &lt;code&gt;Y&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Error reporting should be greedy and descriptive so that developers can quickly fix as many errors as possible when binding fails.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To that end we opted to write a binder that didn't use any reflection. The trade-off we're making here is that we're forced to be much more explicit when we bind a type and so we end up with what some people might consider to be boilerplate. However, we'd personally rather have code that is explicit than have to read through documentation to discover the implicit behaviours of something magic, because when the magic thing breaks we usually spend more time debugging that than we would have spent writing the explicit "boilerplate" to begin with.&lt;/p&gt;

&lt;p&gt;Also, thanks to the composable nature of functional programming languages and the power of F#'s computation expressions it's possible to be both explicit and terse. It's probably best appreciated with an example. So let's see how we'd bind the above &lt;code&gt;LoggingOptions&lt;/code&gt; using our new approach.&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="p"&gt;#&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="s2"&gt;"nuget: Symbolica.Extensions.Configuration.FSharp"&lt;/span&gt;

&lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="nn"&gt;Microsoft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Configuration&lt;/span&gt;
&lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="nn"&gt;Symbolica&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FSharp&lt;/span&gt;

&lt;span class="p"&gt;[&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RequireQualifiedAccess&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;LogLevel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Debug&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Info&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Warning&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;LogLevel&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;bind&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="nc"&gt;Binder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;fun&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="kt"&gt;string&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;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ToLowerInvariant&lt;/span&gt;&lt;span class="bp"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;&amp;gt;&amp;gt;&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="s2"&gt;"info"&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Success&lt;/span&gt; &lt;span class="nn"&gt;LogLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Info&lt;/span&gt;
            &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s2"&gt;"debug"&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Success&lt;/span&gt; &lt;span class="nn"&gt;LogLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Debug&lt;/span&gt;
            &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s2"&gt;"warning"&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Success&lt;/span&gt; &lt;span class="nn"&gt;LogLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Warning&lt;/span&gt;
            &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s2"&gt;"error"&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Success&lt;/span&gt; &lt;span class="nn"&gt;LogLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Error&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="nc"&gt;Failure&lt;/span&gt; &lt;span class="nn"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invalidType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LogLevel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;LoggingOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LogLevel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nc"&gt;Sink&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="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;bindConfig&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="nn"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;section&lt;/span&gt;
        &lt;span class="s2"&gt;"Logging"&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt; &lt;span class="p"&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;level&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;valueAt&lt;/span&gt; &lt;span class="s2"&gt;"Level"&lt;/span&gt; &lt;span class="nn"&gt;LogLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;
            &lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="n"&gt;sink&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;valueAt&lt;/span&gt; &lt;span class="s2"&gt;"Sink"&lt;/span&gt; &lt;span class="nn"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Level&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nc"&gt;Sink&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sink&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
         &lt;span class="o"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="nc"&gt;ConfigurationBuilder&lt;/span&gt;&lt;span class="bp"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AddInMemoryCollection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"Logging:Level"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Debug"&lt;/span&gt;
              &lt;span class="s2"&gt;"Logging:Sink"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Console"&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;Map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ofList&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Build&lt;/span&gt;&lt;span class="bp"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;bindConfig&lt;/span&gt;
&lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Binder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eval&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;
&lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;BindResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapFailure&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;e&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ToString&lt;/span&gt;&lt;span class="bp"&gt;()&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running this script produces the following output.&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;val&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BindResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LoggingOptions&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;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; 
    &lt;span class="nc"&gt;Success&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Level&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Debug&lt;/span&gt;
              &lt;span class="nc"&gt;Sink&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Console"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From this example we can see that it's successfully bound our more complex &lt;code&gt;LoggingOptions&lt;/code&gt; type that contains a &lt;code&gt;DU&lt;/code&gt;. There's also zero magic, the binding process is clear to see and simple to customise. Let's check that it's met our design goals.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Failures are expected - We can see this by the fact that right at the end, after we've called &lt;code&gt;eval&lt;/code&gt; on the &lt;code&gt;Binder&lt;/code&gt;, it's produced a &lt;code&gt;BindResult&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Binding doesn't break immutability - No &lt;code&gt;[&amp;lt;CLIMutable&amp;gt;]&lt;/code&gt; required here.&lt;/li&gt;
&lt;li&gt;Binding works for complex types - Binding a DU was no problem. We were also able to make it case insensitive just through a little function composition with &lt;code&gt;ToLowerInvariant&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Binding is composable - We defined the binder for the &lt;code&gt;LogLevel&lt;/code&gt; in isolation to the overall config binder.&lt;/li&gt;
&lt;li&gt;Error reporting is greedy and informative - Let's simulate some failures and see what happens.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's run the script again but this time with the following input config.&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="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"Logging:Level"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Critical"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So that the &lt;code&gt;Level&lt;/code&gt; is invalid and the &lt;code&gt;Sink&lt;/code&gt; is missing. We get the following output.&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="nc"&gt;Failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="nc"&gt;Logging'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="n"&gt;these&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="nc"&gt;Level'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nc"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="nc"&gt;Critical'&lt;/span&gt;
        &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nc"&gt;Could&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="n"&gt;parse&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="nn"&gt;LogLevel'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
      &lt;span class="err"&gt;@'&lt;/span&gt;&lt;span class="nc"&gt;Sink'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nc"&gt;The&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="n"&gt;was&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's shown us all of the paths in the config for which it found errors and what those errors are.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Implementation Details
&lt;/h2&gt;

&lt;p&gt;At the heart of all of this is a &lt;code&gt;Binder&amp;lt;'config, 'value, 'error&amp;gt;&lt;/code&gt; type. This type is just a wrapper around a function of the form &lt;code&gt;'config -&amp;gt; BindResult&amp;lt;'a,'error&amp;gt;&lt;/code&gt;. For the category theory inclined, it's just a &lt;a href="https://dev.to/choc13/grokking-the-reader-monad-4f45"&gt;reader monad&lt;/a&gt; whose return type has been specialised to a &lt;code&gt;BindResult&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;BindResult&lt;/code&gt; type is very similar to a regular F# &lt;code&gt;Result&lt;/code&gt; except that its applicative instance will accumulate errors, whereas the regular &lt;code&gt;Result&lt;/code&gt; will typically short-circuit on the first error it encounters.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Binder&lt;/code&gt; and &lt;code&gt;BindResult&lt;/code&gt; are defined generically to keep them as flexible as possible. However at some point we want to provide some specialisations for the common binding scenarios. There are really two primary specialisations to consider; one for binding sections and another for binding values.&lt;/p&gt;

&lt;p&gt;Section binders are of the form &lt;code&gt;Binder&amp;lt;#IConfiguration, 'a, Error&amp;gt;&lt;/code&gt; and value binders are of the form &lt;code&gt;Binder&amp;lt;string, 'a, ValueError&amp;gt;&lt;/code&gt;. By fixing &lt;code&gt;'error&lt;/code&gt; to the custom types &lt;code&gt;Error&lt;/code&gt; and &lt;code&gt;ValueError&lt;/code&gt; it's easy to compose &lt;code&gt;Binder&lt;/code&gt;s and also ensure that the errors can be properly accumulated in both applicative and alternative computations.&lt;/p&gt;

&lt;p&gt;One of the primary specialisations comes from the &lt;code&gt;bind&lt;/code&gt; &lt;a href="https://docs.microsoft.com/en-us/dotnet/fsharp/whats-new/fsharp-50#applicative-computation-expressions" rel="noopener noreferrer"&gt;applicative computation expression&lt;/a&gt;. We saw in the example above how &lt;code&gt;bind&lt;/code&gt; lets us compose a &lt;code&gt;Binder&lt;/code&gt; for an &lt;code&gt;IConfigurationSection&lt;/code&gt; by binding its properties using existing &lt;code&gt;Binder&lt;/code&gt;s and at the same time ensures all binding errors from this section are accumulated. The &lt;code&gt;bind&lt;/code&gt; CE gives us a declarative looking DSL for defining new binders for our application specific config objects.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://github.com/Symbolica/Symbolica.Extensions.Configuration.FSharp/blob/master/src/Symbolica.Extensions.Configuration.FSharp/Bind.fs" rel="noopener noreferrer"&gt;&lt;code&gt;Bind&lt;/code&gt;&lt;/a&gt; module the library also provides various combinators for building new &lt;code&gt;Binder&lt;/code&gt;s. Such as &lt;code&gt;Bind.section&lt;/code&gt; and &lt;code&gt;Bind.valueAt&lt;/code&gt; which take an existing &lt;code&gt;Binder&lt;/code&gt; and bind them to a section or a value at a particular key, which are typically used inside a &lt;code&gt;bind&lt;/code&gt; CE. It also contains many binders for types like &lt;code&gt;int&lt;/code&gt;, &lt;code&gt;bool&lt;/code&gt; &lt;code&gt;System.DateTime&lt;/code&gt; and &lt;code&gt;System.Uri&lt;/code&gt; as well as more complex structures like &lt;code&gt;List&lt;/code&gt; and &lt;code&gt;IDictionary&lt;/code&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Try it out
&lt;/h2&gt;

&lt;p&gt;The code is available on GitHub and you can install the library via &lt;a href="https://www.nuget.org/packages/Symbolica.Extensions.Configuration.FSharp/0.3.0" rel="noopener noreferrer"&gt;NuGet&lt;/a&gt;. If you want to see even more sophisticated examples that shows how to do things like handle optional values, deal with alternatives and bind units of measure then check out the &lt;a href="https://github.com/Symbolica/Symbolica.Extensions.Configuration.FSharp/blob/master/tests/Symbolica.Extensions.Configuration.FSharp.Tests/IntegrationTests.fs" rel="noopener noreferrer"&gt;IntegrationTests&lt;/a&gt;. Of course if there's something that you think is missing then open an issue or a pull request. I'm sure there are plenty of other &lt;code&gt;Binder&lt;/code&gt;s that we can add to the &lt;code&gt;Bind&lt;/code&gt; module to cover other common .NET types.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Improvements
&lt;/h2&gt;

&lt;p&gt;If you want to use things like &lt;code&gt;IOptionsSnapshot&lt;/code&gt; then it requires interaction with the &lt;code&gt;IServiceCollection&lt;/code&gt; and a call to &lt;code&gt;Configure&amp;lt;MyOptionsType&amp;gt;(configureAction)&lt;/code&gt;. Unfortunately the way that Microsoft have designed this means that a parameterless public constructor is required on the options type being configured so that an instance can be passed to &lt;code&gt;configureAction&lt;/code&gt;, which goes against our design principles here. So currently this library won't play nicely with things like reactive options updates. If this is something that you'd like then it should be possible to provide a way around this by providing an alternative &lt;code&gt;IOptionsFactory&lt;/code&gt;, so please open an issue and let us know. See the &lt;a href="https://github.com/Symbolica/Symbolica.Extensions.Configuration.FSharp#usage-with-di" rel="noopener noreferrer"&gt;README&lt;/a&gt; for more details.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>fsharp</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Symbolica's Console Newsletter Interview</title>
      <dc:creator>Matt Thornton</dc:creator>
      <pubDate>Tue, 26 Oct 2021 17:03:27 +0000</pubDate>
      <link>https://dev.to/symbolica/symbolicas-console-newsletter-interview-10f2</link>
      <guid>https://dev.to/symbolica/symbolicas-console-newsletter-interview-10f2</guid>
      <description>&lt;p&gt;At &lt;a href="https://www.symbolica.dev"&gt;Symbolica&lt;/a&gt; we’re building a cloud-hosted symbolic execution service. Symbolic execution lets you explore every reachable state of your program so that you can write tests without worrying about missing any edge cases. As a bonus we also automatically detect if any states can cause invalid memory access and other undefined behaviours, like divide by zero, without you having to write any additional tests.&lt;/p&gt;

&lt;p&gt;The core of our symbolic executor is open source and this week we got to chat with Jackson Kelley for the &lt;a href="https://console.substack.com/p/console-76"&gt;latest edition of the Console newsletter&lt;/a&gt; about our project. We talk about our backgrounds and influences and get into the details of some of the technical challenges that we’ve been tackling as we continue to build Symbolica.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i3JOwpme--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Symbolica"&gt;
        Symbolica
      &lt;/a&gt; / &lt;a href="https://github.com/Symbolica/Symbolica"&gt;
        Symbolica
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Symbolica's open-source symbolic execution engine.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
Symbolica&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://github.com/SymbolicaDev/Symbolica/actions"&gt;&lt;img src="https://camo.githubusercontent.com/faabed919a78317f4086859f2bf9c2ca4a97f2ea0745f3aae138e66e22244e0e/68747470733a2f2f6275696c6473746174732e696e666f2f6769746875622f63686172742f53796d626f6c6963614465762f53796d626f6c6963613f6272616e63683d6d6173746572" alt="Build history"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
NuGet Packages&lt;/h2&gt;
&lt;h3&gt;
Symbolica.Abstraction&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.nuget.org/packages/Symbolica.Abstraction/" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/ef30687c1e10de8510691ce4669c770e76dc26d16ef0a8768ebd905d37359dfd/68747470733a2f2f6275696c6473746174732e696e666f2f6e756765742f53796d626f6c6963612e4162737472616374696f6e" alt="NuGet Badge"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
Symbolica.Collection&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.nuget.org/packages/Symbolica.Collection/" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/452f418ebe5027260414c95be655fda79f322bfe5d16a26f0abf6aefa16f8bd9/68747470733a2f2f6275696c6473746174732e696e666f2f6e756765742f53796d626f6c6963612e436f6c6c656374696f6e" alt="NuGet Badge"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
Symbolica.Computation&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.nuget.org/packages/Symbolica.Computation/" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/052ed18e27d2266a63c41abaeccb183bd70a74d205996e955d5fe300733f7132/68747470733a2f2f6275696c6473746174732e696e666f2f6e756765742f53796d626f6c6963612e436f6d7075746174696f6e" alt="NuGet Badge"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
Symbolica.Deserialization&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.nuget.org/packages/Symbolica.Deserialization/" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/d57af60deafb06e93f285da3b24549294e6928151404ce4bb0205f71784814a0/68747470733a2f2f6275696c6473746174732e696e666f2f6e756765742f53796d626f6c6963612e446573657269616c697a6174696f6e" alt="NuGet Badge"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
Symbolica.Expression&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.nuget.org/packages/Symbolica.Expression/" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/6b94cdce7c7959405e1f8ff55e9403e0ee682069c753b3f5d59a3257541d5c8a/68747470733a2f2f6275696c6473746174732e696e666f2f6e756765742f53796d626f6c6963612e45787072657373696f6e" alt="NuGet Badge"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
Symbolica.Implementation&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.nuget.org/packages/Symbolica.Implementation/" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/8a5ed3aa45f92345c8e7434f112f27b8c044c07755d74579e6d80c50a4bd323e/68747470733a2f2f6275696c6473746174732e696e666f2f6e756765742f53796d626f6c6963612e496d706c656d656e746174696f6e" alt="NuGet Badge"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
Symbolica.Representation&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.nuget.org/packages/Symbolica.Representation/" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/d6dd021ab2f6f7ef00c08cd2fc64380e1288f646c4886a22c81e3a5c44696bec/68747470733a2f2f6275696c6473746174732e696e666f2f6e756765742f53796d626f6c6963612e526570726573656e746174696f6e" alt="NuGet Badge"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
Docker Images&lt;/h2&gt;
&lt;h3&gt;
symbolica/build&lt;/h3&gt;
&lt;div class="highlight highlight-source-shell position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;docker build lib/build -t symbolica/build:latest&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;docker run -v &lt;span class="pl-k"&gt;&amp;lt;&lt;/span&gt;path-to-user-code&lt;span class="pl-k"&gt;&amp;gt;&lt;/span&gt;:/code symbolica/build:latest&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://hub.docker.com/repository/docker/symbolica/build" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/2e00d6426a7e90047e909bb89b1ca8e25f84716e8e028de556e508911f9145f0/68747470733a2f2f696d672e736869656c64732e696f2f646f636b65722f762f73796d626f6c6963612f6275696c643f736f72743d73656d766572266c6f676f3d446f636b6572" alt="Docker Image Version (latest semver)"&gt;&lt;/a&gt;
&lt;a href="https://hub.docker.com/repository/docker/symbolica/build" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/c889f12c678b3c379d1deda3faa3f3ee4cd38cc987f65f0f0e1fed1e2a86e264/68747470733a2f2f696d672e736869656c64732e696f2f646f636b65722f70756c6c732f73796d626f6c6963612f6275696c643f6c6f676f3d446f636b6572266c6162656c3d70756c6c73" alt="Docker Pulls"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
symbolica/translate&lt;/h3&gt;
&lt;div class="highlight highlight-source-shell position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;docker build lib/translate -t symbolica/translate:latest&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;docker run -v &lt;span class="pl-k"&gt;&amp;lt;&lt;/span&gt;path-to-user-code&lt;span class="pl-k"&gt;&amp;gt;&lt;/span&gt;:/code symbolica/translate:latest &lt;span class="pl-k"&gt;&amp;lt;&lt;/span&gt;declarations&lt;span class="pl-k"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://hub.docker.com/repository/docker/symbolica/translate" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/7bb4372bd776427862c7f089cceb52fca6f830c5f8ea478b3a4a22cb5fa6b603/68747470733a2f2f696d672e736869656c64732e696f2f646f636b65722f762f73796d626f6c6963612f7472616e736c6174653f736f72743d73656d766572266c6f676f3d446f636b6572" alt="Docker Image Version (latest semver)"&gt;&lt;/a&gt;
&lt;a href="https://hub.docker.com/repository/docker/symbolica/translate" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/5eca152ece2866a688fe4275b436f8a92332688ad53f5bca4fab836a8d4f3ac7/68747470733a2f2f696d672e736869656c64732e696f2f646f636b65722f70756c6c732f73796d626f6c6963612f7472616e736c6174653f6c6f676f3d446f636b6572266c6162656c3d70756c6c73" alt="Docker Pulls"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Note, we don't publish a &lt;code&gt;latest&lt;/code&gt; tag because we prefer to use SemVer instead, even if Docker doesn't natively support it.
Our stable images will be tagged according to the tags in this repository and the versions are aligned with the NuGet package versions.
If you're using the docker images in conjunction with the NuGet packages, it's best to make sure you're using a tag for the docker images that matches the NuGet package version you're using.&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Symbolica/Symbolica"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;Each edition of &lt;a href="https://console.substack.com/"&gt;Console&lt;/a&gt; is a curated list of interesting open source projects along with an interview of the creators of one of the projects, delivered to your inbox every week. It’s a great way to discover new open source projects and hear from the creators behind the code. If you like open source it’s worth signing up.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>watercooler</category>
      <category>githunt</category>
    </item>
  </channel>
</rss>
