Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Suppose a function has side effects. If we take all the effects it produces as the input and output parameters, then the function is pure to the outside world.</p> <p>So for an impure function </p> <pre class="lang-hs prettyprint-override"><code>f' :: Int -&gt; Int </code></pre> <p>we add the RealWorld to the consideration </p> <pre><code>f :: Int -&gt; RealWorld -&gt; (Int, RealWorld) -- input some states of the whole world, -- modify the whole world because of the a side effects, -- then return the new world. </code></pre> <p>then <code>f</code> is pure again. We define a parametrized data type <code>IO a = RealWorld -&gt; (a, RealWorld)</code>, so we don't need to type RealWorld so many times</p> <pre><code>f :: Int -&gt; IO Int </code></pre> <p>To the programmer, handling a RealWorld directly is too dangerous&mdash;in particular, if a programmer gets their hands on a value of type RealWorld, they might try to <em>copy</em> it, which is basically impossible. (Think of trying to copy the entire filesystem, for example. Where would you put it?) Therefore, our definition of IO encapsulates the states of the whole world as well. </p> <p>These impure functions are useless if we can't chain them together. Consider </p> <pre class="lang-hs prettyprint-override"><code>getLine :: IO String = RealWorld -&gt; (String, RealWorld) getContents :: String -&gt; IO String = String -&gt; RealWorld -&gt; (String, RealWorld) putStrLn :: String -&gt; IO () = String -&gt; RealWorld -&gt; ((), RealWorld) </code></pre> <p>We want to get a filename from the console, read that file, then print the content out. How would we do it if we can access the real world states?</p> <pre><code>printFile :: RealWorld -&gt; ((), RealWorld) printFile world0 = let (filename, world1) = getLine world0 (contents, world2) = (getContents filename) world1 in (putStrLn contents) world2 -- results in ((), world3) </code></pre> <p>We see a pattern here: the functions are called like this:</p> <pre><code>... (&lt;result-of-f&gt;, worldY) = f worldX (&lt;result-of-g&gt;, worldZ) = g &lt;result-of-f&gt; worldY ... </code></pre> <p>So we could define an operator <code>~~~</code> to bind them:</p> <pre><code>(~~~) :: (IO b) -&gt; (b -&gt; IO c) -&gt; IO c (~~~) :: (RealWorld -&gt; (b, RealWorld)) -&gt; (b -&gt; RealWorld -&gt; (c, RealWorld)) -&gt; RealWorld -&gt; (c, RealWorld) (f ~~~ g) worldX = let (resF, worldY) = f worldX in g resF worldY </code></pre> <p>then we could simply write</p> <pre><code>printFile = getLine ~~~ getContents ~~~ putStrLn </code></pre> <p>without touching the real world.</p> <hr> <p>Now suppose we want to make the file content uppercase as well. Uppercasing is a pure function</p> <pre><code>upperCase :: String -&gt; String </code></pre> <p>But to make it into the real world, it has to return an <code>IO String</code>. It is easy to lift such a function:</p> <pre><code>impureUpperCase :: String -&gt; RealWorld -&gt; (String, RealWorld) impureUpperCase str world = (upperCase str, world) </code></pre> <p>this can be generalized:</p> <pre><code>impurify :: a -&gt; IO a impurify :: a -&gt; RealWorld -&gt; (a, RealWorld) impurify a world = (a, world) </code></pre> <p>so that <code>impureUpperCase = impurify . upperCase</code>, and we can write</p> <pre><code>printUpperCaseFile = getLine ~~~ getContents ~~~ (impurify . upperCase) ~~~ putStrLn </code></pre> <p><sub>(Note: Normally we write <code>getLine ~~~ getContents ~~~ (putStrLn . upperCase)</code>)</sub></p> <hr> <p>Now let's see what we've done: </p> <ol> <li>We defined an operator <code>(~~~) :: IO b -&gt; (b -&gt; IO c) -&gt; IO c</code> which chains two impure functions together</li> <li>We defined a function <code>impurify :: a -&gt; IO a</code> which converts a pure value to impure.</li> </ol> <p>Now we make the identification <code>(&gt;&gt;=) = (~~~)</code> and <code>return = impurify</code>, and see? We've got a monad.</p> <hr> <p>(To check whether it's really a monad there's few axioms should be satisfied:</p> <p>(1) <code>return a &gt;&gt;= f = f a</code></p> <pre><code> impurify a = (\world -&gt; (a, world)) (impurify a ~~~ f) worldX = let (resF, worldY) = (\world -&gt; (a, world)) worldX in f resF worldY = let (resF, worldY) = (a, worldX)) in f resF worldY = f a worldX </code></pre> <p>(2) <code>f &gt;&gt;= return = f</code></p> <pre><code> (f ~~~ impurify) a worldX = let (resF, worldY) = impuify a worldX in f resF worldY = let (resF, worldY) = (a, worldX) in f resF worldY = f a worldX </code></pre> <p>(3) <code>f &gt;&gt;= (\x -&gt; g x &gt;&gt;= h) = (f &gt;&gt;= g) &gt;&gt;= h</code></p> <p>Exercise.)</p>
 

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