Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>You <em>do</em> give everything the return type of <code>Reader Env a</code>, although this isn't as bad as you think. The reason that everything needs this tag is that if <code>f</code> depends on the environment:</p> <pre><code>type Env = Int f :: Int -&gt; Reader Int Int f x = do env &lt;- ask return (x + env) </code></pre> <p>and <code>g</code> calls <code>f</code>:</p> <pre><code>g x = do y &lt;- f x return (x + y) </code></pre> <p>then <code>g</code> also depends on the environment - the value bound in the line <code>y &lt;- f x</code> can be different, depending on what environment is passed in, so the appropriate type for <code>g</code> is</p> <pre><code>g :: Int -&gt; Reader Int Int </code></pre> <p>This is actually a good thing! The type system is forcing you to explicitly recognise the places where your functions depend on the global environment. You can save yourself some typing pain by defining a shortcut for the phrase <code>Reader Int</code>:</p> <pre><code>type Global = Reader Int </code></pre> <p>so that now your type annotations are:</p> <pre><code>f, g :: Int -&gt; Global Int </code></pre> <p>which is a little more readable.</p> <hr> <p>The alternative to this is to explicitly pass the environment around to all of your functions:</p> <pre><code>f :: Env -&gt; Int -&gt; Int f env x = x + env g :: Env -&gt; Int -&gt; Int g x = x + (f env x) </code></pre> <p>This can work, and in fact syntax-wise it's not any worse than using the <code>Reader</code> monad. The difficulty comes when you want to extend the semantics. Say you also depend on having an updatable state of type <code>Int</code> which counts function applications. Now you have to change your functions to:</p> <pre><code>type Counter = Int f :: Env -&gt; Counter -&gt; Int -&gt; (Int, Counter) f env counter x = (x + env, counter + 1) g :: Env -&gt; Counter -&gt; Int -&gt; (Int, Counter) g env counter x = let (y, newcounter) = f env counter x in (x + y, newcounter + 1) </code></pre> <p>which is decidedly less pleasant. On the other hand, if we are taking the monadic approach, we simply redefine</p> <pre><code>type Global = ReaderT Env (State Counter) </code></pre> <p>The old definitions of <code>f</code> and <code>g</code> continue to work without any trouble. To update them to have application-counting semantics, we simply change them to</p> <pre><code>f :: Int -&gt; Global Int f x = do modify (+1) env &lt;- ask return (x + env) g :: Int -&gt; Global Int g x = do modify(+1) y &lt;- f x return (x + y) </code></pre> <p>and they now work perfectly. Compare the two methods:</p> <ul> <li><p>Explicitly passing the environment and state required a complete rewrite when we wanted to add new functionality to our program.</p></li> <li><p>Using a monadic interface required a change of three lines - and the program continued to work even after we had changed the first line, meaning that we could do the refactoring incrementally (and test it after each change) which reduces the likelihood that the refactor introduces new bugs.</p></li> </ul>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload