Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p><a href="https://stackoverflow.com/questions/17409260/what-advantage-does-monad-give-us-over-an-applicative#comment25280636_17409260">As Stephen Tetley said in a comment</a>, that example doesn't actually use context-sensitivity. One way to think about context-sensitivity is that it lets use choose which actions to take depending on monadic values. Applicative computations must always have the same "shape", in a certain sense, regardless of the values involved; monadic computations need not. I personally think this is easier to understand with a concrete example, so let's look at one. Here's two versions of a simple program which ask you to enter a password, check that you entered the right one, and print out a response depending on whether or not you did.</p> <pre><code>import Control.Applicative checkPasswordM :: IO () checkPasswordM = do putStrLn "What's the password?" pass &lt;- getLine if pass == "swordfish" then putStrLn "Correct. The secret answer is 42." else putStrLn "INTRUDER ALERT! INTRUDER ALERT!" checkPasswordA :: IO () checkPasswordA = if' . (== "swordfish") &lt;$&gt; (putStrLn "What's the password?" *&gt; getLine) &lt;*&gt; putStrLn "Correct. The secret answer is 42." &lt;*&gt; putStrLn "INTRUDER ALERT! INTRUDER ALERT!" if' :: Bool -&gt; a -&gt; a -&gt; a if' True t _ = t if' False _ f = f </code></pre> <p>Let's load this into GHCi and check what happens with the monadic version:</p> <pre><code>*Main&gt; checkPasswordM What's the password? swordfish Correct. The secret answer is 42. *Main&gt; checkPasswordM What's the password? zvbxrpl INTRUDER ALERT! INTRUDER ALERT! </code></pre> <p>So far, so good. But if we use the applicative version:</p> <pre><code>*Main&gt; checkPasswordA What's the password? hunter2 Correct. The secret answer is 42. INTRUDER ALERT! INTRUDER ALERT! </code></pre> <p>We entered the wrong password, but we still got the secret! <em>And</em> an intruder alert! This is because <code>&lt;$&gt;</code> and <code>&lt;*&gt;</code>, or equivalently <code>liftA<em>n</em></code>/<code>liftM<em>n</em></code>, <em>always</em> execute the effects of <em>all</em> their arguments. The applicative version translates, in <code>do</code> notation, to</p> <pre><code>do pass &lt;- putStrLn "What's the password?" *&gt; getLine) unit1 &lt;- putStrLn "Correct. The secret answer is 42." unit2 &lt;- putStrLn "INTRUDER ALERT! INTRUDER ALERT!" pure $ if' (pass == "swordfish") unit1 unit2 </code></pre> <p>And it should be clear why this has the wrong behavior. In fact, <em>every</em> use of applicative functors is equivalent to monadic code of the form</p> <pre><code>do val1 &lt;- app1 val2 &lt;- app2 ... valN &lt;- appN pure $ f val1 val2 ... valN </code></pre> <p>(where some of the <code>appI</code> are allowed to be of the form <code>pure xI</code>). And equivalently, any monadic code in that form can be rewritten as</p> <pre><code>f &lt;$&gt; app1 &lt;*&gt; app2 &lt;*&gt; ... &lt;*&gt; appN </code></pre> <p>or equivalently as</p> <pre><code>liftAN f app1 app2 ... appN </code></pre> <p>To think about this, consider <code>Applicative</code>'s methods:</p> <pre><code>pure :: a -&gt; f a (&lt;$&gt;) :: (a -&gt; b) -&gt; f a -&gt; f b (&lt;*&gt;) :: f (a -&gt; b) -&gt; f a -&gt; f b </code></pre> <p>And then consider what <code>Monad</code> adds:</p> <pre><code>(=&lt;&lt;) :: (a -&gt; m b) -&gt; m a -&gt; m b join :: m (m a) -&gt; m a </code></pre> <p>(Remember that you only need one of those.)</p> <p>Handwaving a lot, if you think about it, the only way we can put together the applicative functions is to construct chains of the form <code>f &lt;$&gt; app1 &lt;*&gt; ... &lt;*&gt; appN</code>, and possibly nest those chains (<em>e.g.</em>, <code>f &lt;$&gt; (g &lt;$&gt; x &lt;*&gt; y) &lt;*&gt; z</code>). However, <code>(=&lt;&lt;)</code> (or <code>(&gt;&gt;=)</code>) allows us to take a value and produce <em>different</em> monadic computations depending on that value, that could be constructed on the fly. This is what we use to decide whether to compute "print out the secret", or compute "print out an intruder alert", and why we can't make that decision with applicative functors alone; none of the types for applicative functions allow you to consume a plain value.</p> <p>You can think about <code>join</code> in concert with <code>fmap</code> in a similar way: <a href="https://stackoverflow.com/questions/17409260/what-advantage-does-monad-give-us-over-an-applicative#comment25285244_17409567">as I mentioned in a comment</a>, you <em>can</em> do something like</p> <pre><code>checkPasswordFn :: String -&gt; IO () checkPasswordFn pass = if pass == "swordfish" then putStrLn "Correct. The secret answer is 42." else putStrLn "INTRUDER ALERT! INTRUDER ALERT!" checkPasswordA' :: IO (IO ()) checkPasswordA' = checkPasswordFn &lt;$&gt; (putStrLn "What's the password?" *&gt; getLine) </code></pre> <p>This is what happens when we want to pick a different computation depending on the value, but only have applicative functionality available us. We can pick two different computations to return, but they're wrapped inside the outer layer of the applicative functor. To actually use the computation we've picked, we need <code>join</code>:</p> <pre><code>checkPasswordM' :: IO () checkPasswordM' = join checkPasswordA' </code></pre> <p>And this does the same thing as the previous monadic version (as long as we <code>import Control.Monad</code> first, to get <code>join</code>):</p> <pre><code>*Main&gt; checkPasswordM' What's the password? 12345 INTRUDER ALERT! INTRUDER ALERT! </code></pre>
    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. 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.
 

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