Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <blockquote> <p>The point is so there can be clean error handling in a chain of functions, containers, and side effects. Is this a correct interpretation?</p> </blockquote> <p>Not really. You've mentioned a lot of concepts that people cite when trying to explain monads, including side effects, error handling and non-determinism, but it sounds like you've gotten the incorrect sense that all of these concepts apply to all monads. But there's one concept you mentioned that does: <strong>chaining</strong>.</p> <p>There are two different flavors of this, so I'll explain it two different ways: one without side effects, and one with side effects.</p> <h1>No Side Effects:</h1> <p>Take the following example:</p> <pre><code>addM :: (Monad m, Num a) =&gt; m a -&gt; m a -&gt; m a addM ma mb = do a &lt;- ma b &lt;- mb return (a + b) </code></pre> <p>This function adds two numbers, with the twist that they are wrapped in some monad. Which monad? Doesn't matter! In all cases, that special <code>do</code> syntax de-sugars to the following:</p> <pre><code>addM ma mb = ma &gt;&gt;= \a -&gt; mb &gt;&gt;= \b -&gt; return (a + b) </code></pre> <p>... or, with operator precedence made explicit:</p> <pre><code>ma &gt;&gt;= (\a -&gt; mb &gt;&gt;= (\b -&gt; return (a + b))) </code></pre> <p>Now you can really see that this is a chain of little functions, all composed together, and its behavior will depend on how <code>&gt;&gt;=</code> and <code>return</code> are defined for each monad. If you're familiar with polymorphism in object-oriented languages, this is essentially the same thing: one common interface with multiple implementations. It's slightly more mind-bending than your average OOP interface, since the interface represents a <em>computation policy</em> rather than, say, an animal or a shape or something.</p> <p>Okay, let's see some examples of how <code>addM</code> behaves across different monads. The <code>Identity</code> monad is a decent place to start, since its definition is trivial:</p> <pre><code>instance Monad Identity where return a = Identity a -- create an Identity value (Identity a) &gt;&gt;= f = f a -- apply f to a </code></pre> <p>So what happens when we say:</p> <pre><code>addM (Identity 1) (Identity 2) </code></pre> <p>Expanding this, step by step:</p> <pre><code>(Identity 1) &gt;&gt;= (\a -&gt; (Identity 2) &gt;&gt;= (\b -&gt; return (a + b))) (\a -&gt; (Identity 2) &gt;&gt;= (\b -&gt; return (a + b)) 1 (Identity 2) &gt;&gt;= (\b -&gt; return (1 + b)) (\b -&gt; return (1 + b)) 2 return (1 + 2) Identity 3 </code></pre> <p>Great. Now, since you mentioned clean error handling, let's look at the <code>Maybe</code> monad. Its definition is only slightly trickier than <code>Identity</code>:</p> <pre><code>instance Monad Maybe where return a = Just a -- same as Identity monad! (Just a) &gt;&gt;= f = f a -- same as Identity monad again! Nothing &gt;&gt;= _ = Nothing -- the only real difference from Identity </code></pre> <p>So you can imagine that if we say <code>addM (Just 1) (Just 2)</code> we'll get <code>Just 3</code>. But for grins, let's expand <code>addM Nothing (Just 1)</code> instead:</p> <pre><code>Nothing &gt;&gt;= (\a -&gt; (Just 1) &gt;&gt;= (\b -&gt; return (a + b))) Nothing </code></pre> <p>Or the other way around, <code>addM (Just 1) Nothing</code>:</p> <pre><code>(Just 1) &gt;&gt;= (\a -&gt; Nothing &gt;&gt;= (\b -&gt; return (a + b))) (\a -&gt; Nothing &gt;&gt;= (\b -&gt; return (a + b)) 1 Nothing &gt;&gt;= (\b -&gt; return (1 + b)) Nothing </code></pre> <p>So the <code>Maybe</code> monad's definition of <code>&gt;&gt;=</code> was tweaked to account for failure. When a function is applied to a <code>Maybe</code> value using <code>&gt;&gt;=</code>, you get what you'd expect.</p> <p>Okay, so you mentioned non-determinism. Yes, the list monad can be thought of as modeling non-determinism in a sense... It's a little weird, but think of the list as representing alternative possible values: <code>[1, 2, 3]</code> is not a collection, it's a single non-deterministic number that could be either one, two or three. That sounds dumb, but it starts to make some sense when you think about how <code>&gt;&gt;=</code> is defined for lists: it applies the given function to each possible value. So <code>addM [1, 2] [3, 4]</code> is actually going to compute all possible sums of those two non-deterministic values: <code>[4, 5, 5, 6]</code>.</p> <p>Okay, now to address your second question...</p> <h1>Side Effects:</h1> <p>Let's say you apply <code>addM</code> to two values in the <code>IO</code> monad, like:</p> <pre><code>addM (return 1 :: IO Int) (return 2 :: IO Int) </code></pre> <p>You don't get anything special, just 3 in the <code>IO</code> monad. <code>addM</code> does not read or write any mutable state, so it's kind of no fun. Same goes for the <code>State</code> or <code>ST</code> monads. No fun. So let's use a different function:</p> <pre><code>fireTheMissiles :: IO Int -- returns the number of casualties </code></pre> <p>Clearly the world will be different each time missiles are fired. Clearly. Now let's say you're trying to write some totally innocuous, side effect free, non-missile-firing code. Perhaps you're trying once again to add two numbers, but this time without any monads flying around:</p> <pre><code>add :: Num a =&gt; a -&gt; a -&gt; a add a b = a + b </code></pre> <p>and all of a sudden your hand slips, and you accidentally typo:</p> <pre><code>add a b = a + b + fireTheMissiles </code></pre> <p>An honest mistake, really. The keys were so close together. Fortunately, because <code>fireTheMissiles</code> was of type <code>IO Int</code> rather than simply <code>Int</code>, the compiler is able to avert disaster.</p> <p>Okay, totally contrived example, but the point is that in the case of <code>IO</code>, <code>ST</code> and friends, the type system keeps effects isolated to some specific context. It doesn't magically eliminate side effects, making code referentially transparent that shouldn't be, but it does make it clear at compile time what scope the effects are limited to.</p> <p>So getting back to the original point: what does this have to do with chaining or composition of functions? Well, in this case, it's just a handy way of expressing a sequence of effects:</p> <pre><code>fireTheMissilesTwice :: IO () fireTheMissilesTwice = do a &lt;- fireTheMissiles print a b &lt;- fireTheMissiles print b </code></pre> <h1>Summary:</h1> <p>A monad represents some policy for chaining computations. <code>Identity</code>'s policy is pure function composition, <code>Maybe</code>'s policy is function composition with failure propogation, <code>IO</code>'s policy is <em>impure</em> function composition and so on.</p>
    singulars
    1. This table or related slice is empty.
    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