|
| 1 | +<!doctype html> |
| 2 | +<html lang="en"> |
| 3 | +<head> |
| 4 | + |
| 5 | + <meta charset="utf-8" /> |
| 6 | + <meta name="viewport" content="width=device-width, initial-scale=1" /> |
| 7 | + |
| 8 | + <title>PureScript – Learn – The Partial type class</title> |
| 9 | + |
| 10 | + <link rel="stylesheet" href="../../css/style.css" /> |
| 11 | + <link rel="stylesheet" href="//fonts.googleapis.com/css?family=Roboto:400,300,700" /> |
| 12 | + |
| 13 | +</head> |
| 14 | + |
| 15 | +<body class="learn"> |
| 16 | + |
| 17 | + <script>document.body.className = "learn hasJS";</script> |
| 18 | + |
| 19 | + <header> |
| 20 | + <div class="wrap"> |
| 21 | + <h1><a href="../">PureScript</a></h1> |
| 22 | + <nav> |
| 23 | + <h2>Menu</h2> |
| 24 | + <ul> |
| 25 | +<li><a href="../../">Home</a></li> |
| 26 | +<li><a href="../../download/">Download</a></li> |
| 27 | +<li><a href="../../learn/" class="active">Learn</a></li> |
| 28 | +<li><a href="../../community/">Community</a></li> |
| 29 | +<li><a href="../../projects/">Projects</a></li> |
| 30 | +</ul> |
| 31 | + |
| 32 | + </nav> |
| 33 | + </div> |
| 34 | + </header> |
| 35 | + |
| 36 | + <main> |
| 37 | + <section class="article"> |
| 38 | + <h2>The Partial type class</h2> |
| 39 | + <div class="meta">By Harry Garrood, published on May 22, 2016</div> |
| 40 | + |
| 41 | + <p><em>Note: This tutorial is written with version 0.9 of the compiler and versions 1.0.0 (or higher) of the core libraries in mind. While much of this tutorial is still applicable to earlier versions, a few functions have moved around or have been renamed.</em></p> |
| 42 | +<h4 id="why-have-a-partial-type-class">Why have a Partial type class?</h4> |
| 43 | +<p>Every now and then, you will want to use <em>partial functions;</em> that is, functions which don’t handle every possible case of their inputs. For example, there is a function <code>fromJust :: ∀ a. Partial ⇒ Maybe a → a</code> in <code>Data.Maybe</code>, which gives you the value inside a <code>Just</code> value, or throws an error if given <code>Nothing</code>.</p> |
| 44 | +<p>It’s important that types tell the truth wherever possible, because this is a large part of what allows us to understand PureScript code easily and refactor it fearlessly. However, in certain contexts, you know that e.g. an <code>Either</code> value is always going to be <code>Right</code>, but you can’t prove that to the type checker, and so you want an escape hatch so that you can write a function that doesn’t have to deal with the <code>Left</code> case. This is often the case when performance is important, for instance.</p> |
| 45 | +<p>Previously, partiality of functions was indicated by putting the word “unsafe” at the start of the functions’ names, or by putting them in an “Unsafe” module. For instance, there was previously an <code>unsafeIndex</code> function in <code>Data.Array.Unsafe</code>, and <code>fromJust</code> used to be in <code>Data.Maybe.Unsafe</code>. However, this is not ideal, because the fact that these functions are partial, and therefore unsafe if used carelessly, does not appear in the type. Consequently, there is little to stop you from using it in an inappropriate manner by accident.</p> |
| 46 | +<p>The Partial type class allows us to put this information into the types, and thereby allows us to clearly demarcate which parts of your code are responsible for making that sure unsafe functions are used in a safe manner.</p> |
| 47 | +<h4 id="i-just-want-to-use-a-partial-function-please">I just want to use a partial function, please</h4> |
| 48 | +<p>If you try to just use a partial function, you’ll most likely get an error about no instance being found for the <code>Partial</code> class. Take this program, for instance:</p> |
| 49 | +<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">module</span> <span class="dt">Main</span> <span class="kw">where</span> |
| 50 | + |
| 51 | +<span class="kw">import </span><span class="dt">Prelude</span> |
| 52 | +<span class="kw">import </span><span class="dt">Data.Maybe</span> (<span class="dt">Maybe</span>(..), fromJust) |
| 53 | +<span class="kw">import </span><span class="dt">Control.Monad.Eff</span> (<span class="dt">Eff</span>) |
| 54 | +<span class="kw">import </span><span class="dt">Control.Monad.Eff.Console</span> (<span class="dt">CONSOLE</span>, logShow) |
| 55 | + |
| 56 | +<span class="ot">main ::</span> forall eff<span class="fu">.</span> <span class="dt">Eff</span> (<span class="ot">console ::</span> <span class="dt">CONSOLE</span> <span class="fu">|</span> eff) <span class="dt">Unit</span> |
| 57 | +main <span class="fu">=</span> logShow (fromJust (<span class="dt">Just</span> <span class="dv">3</span>))</code></pre></div> |
| 58 | +<p>Because <code>fromJust</code> is partial, and because the partiality hasn’t been explicitly handled, you’ll get an error:</p> |
| 59 | +<pre><code>at src/Main.purs line 8, column 1 - line 8, column 56 |
| 60 | + |
| 61 | + No type class instance was found for |
| 62 | + |
| 63 | + Prim.Partial</code></pre> |
| 64 | +<p><em>Aside: Yes, this is not a fantastic error. It’s going to get better soon.</em></p> |
| 65 | +<p>The solution is usually to add an application of <code>unsafePartial</code> somewhere, like this:</p> |
| 66 | +<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">module</span> <span class="dt">Main</span> <span class="kw">where</span> |
| 67 | + |
| 68 | +<span class="kw">import </span><span class="dt">Prelude</span> |
| 69 | +<span class="kw">import </span><span class="dt">Data.Maybe</span> (<span class="dt">Maybe</span>(..), fromJust) |
| 70 | +<span class="kw">import </span><span class="dt">Control.Monad.Eff</span> (<span class="dt">Eff</span>) |
| 71 | +<span class="kw">import </span><span class="dt">Control.Monad.Eff.Console</span> (<span class="dt">CONSOLE</span>, logShow) |
| 72 | +<span class="kw">import </span><span class="dt">Partial.Unsafe</span> (unsafePartial) |
| 73 | + |
| 74 | +<span class="ot">main ::</span> forall eff<span class="fu">.</span> <span class="dt">Eff</span> (<span class="ot">console ::</span> <span class="dt">CONSOLE</span> <span class="fu">|</span> eff) <span class="dt">Unit</span> |
| 75 | +main <span class="fu">=</span> logShow (unsafePartial (fromJust (<span class="dt">Just</span> <span class="dv">3</span>)))</code></pre></div> |
| 76 | +<h4 id="where-should-i-put-unsafepartial">Where should I put unsafePartial?</h4> |
| 77 | +<p>The rule of thumb is to put <code>unsafePartial</code> at the level of your program such that the types tell the truth, and the part of your program responsible for making sure a use of a partial function is safe is also the part where the <code>unsafePartial</code> is. This is perhaps best demonstrated with an example.</p> |
| 78 | +<p>Imagine that we want to represent vectors in 3D with an array containing exactly 3 values (perhaps we want to use them with some other API that expects this representation, and we don’t want to be converting back and forth all the time). In this case, we would usually use a <code>newtype</code> and avoid exporting the constructor:</p> |
| 79 | +<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">module</span> <span class="dt">Data.V3</span> |
| 80 | + ( <span class="dt">V3</span> |
| 81 | + , makeV3 |
| 82 | + , runV3 |
| 83 | + ) <span class="kw">where</span> |
| 84 | + |
| 85 | +<span class="kw">newtype</span> <span class="dt">V3</span> <span class="fu">=</span> <span class="dt">V3</span> (<span class="dt">Array</span> <span class="dt">Number</span>) |
| 86 | + |
| 87 | +<span class="ot">makeV3 ::</span> <span class="dt">Number</span> <span class="ot">-></span> <span class="dt">Number</span> <span class="ot">-></span> <span class="dt">Number</span> <span class="ot">-></span> <span class="dt">V3</span> |
| 88 | +makeV3 x y z <span class="fu">=</span> <span class="dt">V3</span> [x, y, z] |
| 89 | + |
| 90 | +<span class="ot">runV3 ::</span> <span class="dt">V3</span> <span class="ot">-></span> <span class="dt">Array</span> <span class="dt">Number</span> |
| 91 | +runV3 (<span class="dt">V3</span> v) <span class="fu">=</span> v</code></pre></div> |
| 92 | +<p>This way, all of the functions are safe; the code will guarantee that any <code>V3</code> does contain exactly 3 values (although the type checker is not aware of this).</p> |
| 93 | +<p>Now imagine we want to write a dot product function:</p> |
| 94 | +<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">dot ::</span> <span class="dt">V3</span> <span class="ot">-></span> <span class="dt">V3</span> <span class="ot">-></span> <span class="dt">Number</span> |
| 95 | +dot (<span class="dt">V3</span> [x1, x2, x3]) (<span class="dt">V3</span> [y1, y2, y3]) <span class="fu">=</span> x1<span class="fu">*</span>y1 <span class="fu">+</span> x2<span class="fu">*</span>y2 <span class="fu">+</span> x3<span class="fu">*</span>y3</code></pre></div> |
| 96 | +<p>We know this is ok, but the compiler disallows it:</p> |
| 97 | +<pre><code>A case expression could not be determined to cover all inputs. |
| 98 | +The following additional cases are required to cover all inputs: |
| 99 | + |
| 100 | + (V3 _) _ |
| 101 | + _ (V3 _) |
| 102 | + |
| 103 | +Alternatively, add a Partial constraint to the type of the enclosing value. |
| 104 | + |
| 105 | +in value declaration dot</code></pre> |
| 106 | +<p>In this case, we can use <code>unsafePartial</code> to explicitly say that we don’t actually need to worry about those other cases, and therefore we don’t want to propagate a <code>Partial</code> constraint; users of this <code>dot</code> function should not have to worry about this partiality. For example:</p> |
| 107 | +<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">import </span><span class="dt">Partial</span> (crash) |
| 108 | +<span class="kw">import </span><span class="dt">Partial.Unsafe</span> (unsafePartial) |
| 109 | + |
| 110 | +<span class="ot">dot ::</span> <span class="dt">V3</span> <span class="ot">-></span> <span class="dt">V3</span> <span class="ot">-></span> <span class="dt">Number</span> |
| 111 | +dot x y <span class="fu">=</span> unsafePartial (go x y) |
| 112 | + <span class="kw">where</span> |
| 113 | +<span class="ot"> go ::</span> <span class="dt">Partial</span> <span class="ot">=></span> <span class="dt">V3</span> <span class="ot">-></span> <span class="dt">V3</span> <span class="ot">-></span> <span class="dt">Number</span> |
| 114 | + go (<span class="dt">V3</span> [x1, x2, x3]) (<span class="dt">V3</span> [y1, y2, y3]) <span class="fu">=</span> x1<span class="fu">*</span>y1 <span class="fu">+</span> x2<span class="fu">*</span>y2 <span class="fu">+</span> x3<span class="fu">*</span>y3 |
| 115 | + <span class="co">-- This second pattern can be omitted, but provides a better error message</span> |
| 116 | + <span class="co">-- in case we do get an invalid argument at runtime.</span> |
| 117 | + go _ _ <span class="fu">=</span> crash <span class="st">"Bad argument: expected exactly 3 elements."</span></code></pre></div> |
| 118 | +<p>The <code>Partial</code> and <code>Partial.Unsafe</code> modules come from the <code>purescript-partial</code> package.</p> |
| 119 | +<p>In this case, we could also use <code>unsafeCrashWith</code>:</p> |
| 120 | +<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">import </span><span class="dt">Partial.Unsafe</span> (unsafeCrashWith) |
| 121 | + |
| 122 | +<span class="ot">dot ::</span> <span class="dt">V3</span> <span class="ot">-></span> <span class="dt">V3</span> <span class="ot">-></span> <span class="dt">Number</span> |
| 123 | +dot (<span class="dt">V3</span> [x1, x2, x3]) (<span class="dt">V3</span> [y1, y2, y3]) <span class="fu">=</span> x1<span class="fu">*</span>y1 <span class="fu">+</span> x2<span class="fu">*</span>y2 <span class="fu">+</span> x3<span class="fu">*</span>y3 |
| 124 | +dot _ _ <span class="fu">=</span> unsafeCrashWith <span class="st">"Bad argument: expected exactly 3 elements."</span></code></pre></div> |
| 125 | +<p>Both implementations will behave in the same way.</p> |
| 126 | +<p>In this case, we know our <code>dot</code> implementation is fine, and so users of it should not have to worry about its partiality, so it makes sense to avoid propagating the constraint. Now, we will see another case where a <code>Partial</code> constraint <em>should</em> be propagated.</p> |
| 127 | +<p>Let us suppose we want a <code>foldr1</code> function, which works in a very similar way to <code>foldr</code> on Lists, except that it doesn’t require an initial value to be passed, and instead requires that the list argument contains at least one element.</p> |
| 128 | +<p>We can implement it like this:</p> |
| 129 | +<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell">foldr1 f (<span class="dt">Cons</span> x xs) <span class="fu">=</span> foldr f x xs</code></pre></div> |
| 130 | +<p>The compiler infers the correct type here, which is:</p> |
| 131 | +<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell">foldr1<span class="ot"> ::</span> forall a<span class="fu">.</span> <span class="dt">Partial</span> <span class="ot">=></span> (a <span class="ot">-></span> a <span class="ot">-></span> a) <span class="ot">-></span> <span class="dt">List</span> a <span class="ot">-></span> a</code></pre></div> |
| 132 | +<p>Now imagine we want a version of <code>Data.Foldable.minimum</code> which returns an <code>a</code> instead of a <code>Maybe a</code>, and is therefore partial. We can implement it in terms of our new <code>foldr1</code> function:</p> |
| 133 | +<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell">minimumP <span class="fu">=</span> foldr1 min</code></pre></div> |
| 134 | +<p>Again, the compiler infers the correct type:</p> |
| 135 | +<div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="ot">minimumP ::</span> forall a<span class="fu">.</span> (<span class="dt">Partial</span>, <span class="dt">Ord</span> a) <span class="ot">=></span> <span class="dt">List</span> a <span class="ot">-></span> a</code></pre></div> |
| 136 | +<p>Notice that the <code>Partial</code> constraint is automatically propagated to the <code>minimumP</code> function because of the use of another partial function in its definition, namely <code>foldr1</code>. In this case, this is what we want; we should propagate the <code>Partial</code> constraint, because it is still the caller’s responsibility to make sure they supply a non-empty list.</p> |
| 137 | +<p>So hopefully it is now clear why this partiality checking is implemented in terms of a type class: it allows us to elegantly reuse existing machinery in the type checker in order to check that a Partial constraint is either explictly handled or propagated. This should help ensure that when you’re reading the code a few months later, it remains clear which part of the code is responsible for ensuring that any assumed invariants which cannot be encoded in the type system do hold.</p> |
| 138 | +</section> |
| 139 | + |
| 140 | + </main> |
| 141 | + |
| 142 | + <footer> |
| 143 | + <nav> |
| 144 | + <ul> |
| 145 | +<li><a href="../../">Home</a></li> |
| 146 | +<li><a href="../../download/">Download</a></li> |
| 147 | +<li><a href="../../learn/" class="active">Learn</a></li> |
| 148 | +<li><a href="../../community/">Community</a></li> |
| 149 | +<li><a href="../../projects/">Projects</a></li> |
| 150 | +</ul> |
| 151 | + |
| 152 | + <ul class="external"> |
| 153 | + <li class="github"><a href="https://github.com/purescript/purescript" title="GitHub">GitHub</a></li> |
| 154 | + <li class="twitter"><a href="https://twitter.com/purescript" title="Twitter">Twitter</a></li> |
| 155 | + </ul> |
| 156 | + </nav> |
| 157 | + </footer> |
| 158 | + |
| 159 | + <script src="js/lib/zepto-1.1.4.min.js"></script> |
| 160 | + <script src="js/home.js"></script> |
| 161 | + |
| 162 | +</body> |
| 163 | +</html> |
| 164 | + |
0 commit comments