summaryrefslogtreecommitdiff
path: root/2024/10/18/Dealing-with-complex-dependency-injection-in-FSharp/index.html
diff options
context:
space:
mode:
Diffstat (limited to '2024/10/18/Dealing-with-complex-dependency-injection-in-FSharp/index.html')
-rw-r--r--2024/10/18/Dealing-with-complex-dependency-injection-in-FSharp/index.html6
1 files changed, 3 insertions, 3 deletions
diff --git a/2024/10/18/Dealing-with-complex-dependency-injection-in-FSharp/index.html b/2024/10/18/Dealing-with-complex-dependency-injection-in-FSharp/index.html
index 0516c6bd..eb1d0c53 100644
--- a/2024/10/18/Dealing-with-complex-dependency-injection-in-FSharp/index.html
+++ b/2024/10/18/Dealing-with-complex-dependency-injection-in-FSharp/index.html
@@ -227,7 +227,7 @@
<li>Maybe aside of the logger we may be needing a separate telemetry mechanism to count number of incoming request or password validation failures? That means another parameter.</li>
<li>Salt generation is pseudo-random process - it we want our function to be deterministic, we should probably parametrize it over explicitly passed <code>Random</code> as well.</li>
</ul>
-<p>As you see, what seemed to be simple task at the beginning can quickly blow up out of proportion. As the number of arguments grows, the more nasty our wiring code eventually becomes. Quite common pattern is to hide all of that nastiness under the carpet a.k.a. <strong>composition root</strong>. However this doesn’t have to be the case.</p>
+<p>As you see, what seemed to be simple task at the beginning can quickly blow up out of proportion. As the number of arguments grows, the more nasty our wiring code eventually becomes. Quite common pattern is to hide all of that nastiness under the carpet a.k.a. composition root. However this doesn’t have to be the case.</p>
<p>Below we’ll cover another approach for dealing with dependencies - inspired by Scala <a target="_blank" rel="noopener" href="https://medium.com/@pascal.mengelt/what-are-the-benefits-of-the-zio-modules-with-zlayers-3bf6cc064a9b?ref=bartoszsypytkowski.com">ZIO</a> library - using incremental steps, from first principles to monadic bindings.</p>
<h2 id="Managing-dependencies-beyond-partial-application"><a href="#Managing-dependencies-beyond-partial-application" class="headerlink" title="Managing dependencies beyond partial application"></a>Managing dependencies beyond partial application</h2><p>Let’s start from how our code from above will eventually look like at the end of this step:</p>
<figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> changePass env <span class="operator">=</span> <span class="keyword">fun</span> req <span class="operator">-&gt;</span> <span class="keyword">task</span> &#123;</span><br><span class="line"> <span class="keyword">let!</span> user <span class="operator">=</span> Db.fetchUser env req.UserId</span><br><span class="line"> <span class="keyword">if</span> user.Hash <span class="operator">=</span> bcrypt user.Salt req.OldPass <span class="keyword">then</span></span><br><span class="line"> <span class="keyword">let</span> salt <span class="operator">=</span> Random.bytes env <span class="number">32</span></span><br><span class="line"> <span class="keyword">do!</span> Db.updateUser <span class="keyword">env</span> &#123; user <span class="keyword">with</span> Salt <span class="operator">=</span> salt; Hash <span class="operator">=</span> bcrypt salt req.NewPass &#125;</span><br><span class="line"> Log.info env <span class="string">&quot;Changed password for user %i&quot;</span> user.Id</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">Ok</span> ()</span><br><span class="line"> <span class="keyword">else</span> </span><br><span class="line"> Log.error env <span class="string">&quot;Password change unauthorized: user %i&quot;</span> user.Id</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">Error</span> <span class="string">&quot;Old password is invalid&quot;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
@@ -255,8 +255,8 @@
<li>It doesn’t impose specific restrictions on libraries and frameworks.</li>
</ul>
<p>Now we could as well stop here - IMHO this approach is already good and useful for most cases. We can also try to push it further. As you’ve seen, our code now requires quite a lot of <code>env</code> passing around. Could we do something about this? It turns out that yes, we could.</p>
-<h2 id="Reader-monad"><a href="#Reader-monad" class="headerlink" title="Reader monad"></a>Reader monad</h2><p>Before we continue: <strong>what we’re going to cover now is less useful in terms of current state of F# ecosystem for the reasons I’ll mention later</strong>.</p>
-<p>The pattern we’ll use here is known as a <strong><a target="_blank" rel="noopener" href="https://fsharpforfunandprofit.com/posts/elevated-world-6/?ref=bartoszsypytkowski.com">Reader Monad</a></strong>. While it’s useful in certain situations, it’s not widely used - IMO it’s fault lies in the name itself, which somehow managed to sound both borderline meaningless and scary in ears of many developers.</p>
+<h2 id="Reader-monad"><a href="#Reader-monad" class="headerlink" title="Reader monad"></a>Reader monad</h2><p>Before we continue: what we’re going to cover now is less useful in terms of current state of F# ecosystem for the reasons I’ll mention later.</p>
+<p>The pattern we’ll use here is known as a <a target="_blank" rel="noopener" href="https://fsharpforfunandprofit.com/posts/elevated-world-6/?ref=bartoszsypytkowski.com">Reader Monad</a>. While it’s useful in certain situations, it’s not widely used - IMO it’s fault lies in the name itself, which somehow managed to sound both borderline meaningless and scary in ears of many developers.</p>
<p>The rest of this blog post will be introduction to this style in F#, however focused solely around problem of dependency management - we’ll ignore other aspects of monads.</p>
<p>We’ll going to reuse our environment type from above, but now encode it directly into another type we’ll call <code>Effect</code>. Since I’ve mentioned that our pattern has M-word in it, you can safely assume that our handler’s logic will be defined as a lazy sequence of steps to be executed (sounds almost like async&#x2F;await). In F# we’ll sugar them by using custom computation expression (I’m going to call it <code>effect &#123; ... &#125;</code>) returning our effect type, which we’ll define as:</p>
<figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">[&lt;Struct&gt;]</span> <span class="keyword">type</span> <span class="title class_">Effect</span><span class="operator">&lt;</span><span class="symbol">&#x27;env</span>, <span class="symbol">&#x27;out</span><span class="operator">&gt;</span> <span class="operator">=</span> Effect <span class="keyword">of</span> (<span class="symbol">&#x27;env</span> <span class="operator">-&gt;</span> <span class="symbol">&#x27;out</span>)</span><br></pre></td></tr></table></figure>