@@ -11,6 +11,214 @@ Utilities for working with partial functions.
1111bower install purescript-partial
1212```
1313
14- ## Documentation
14+ ## Why have a Partial type class?
1515
16- Module documentation is [ published on Pursuit] ( http://pursuit.purescript.org/packages/purescript-partial ) .
16+ Every now and then, you will want to use * partial functions;* that is,
17+ functions which don't handle every possible case of their inputs. For example,
18+ there is a function ` fromJust :: ∀ a. Partial ⇒ Maybe a → a ` in ` Data.Maybe ` ,
19+ which gives you the value inside a ` Just ` value, or throws an error if given
20+ ` Nothing ` .
21+
22+ It's important that types tell the truth wherever possible, because this is a
23+ large part of what allows us to understand PureScript code easily and refactor
24+ it fearlessly. However, in certain contexts, you know that e.g. an ` Either `
25+ value is always going to be ` Right ` , but you can't prove that to the type
26+ checker, and so you want an escape hatch so that you can write a function that
27+ doesn't have to deal with the ` Left ` case. This is often the case when
28+ performance is important, for instance.
29+
30+ Previously, partial functions have been indicated by putting the word "unsafe"
31+ at the start of their names, or by putting them in an "Unsafe" module. For
32+ instance, there was previously an ` unsafeIndex ` function in
33+ ` Data.Array.Unsafe ` , and ` fromJust ` used to be in ` Data.Maybe.Unsafe ` . However,
34+ this is not ideal, because the fact that these functions are partial, and
35+ therefore unsafe if used carelessly, does not appear in the type. Consequently,
36+ there is little to stop you from using it in an inappropriate manner by
37+ accident.
38+
39+ The Partial type class allows us to put this information back into the types,
40+ and thereby allows us to clearly demarcate which parts of your code are
41+ responsible for making that sure unsafe functions are used in a safe manner.
42+
43+ ## I just want to use a partial function, please
44+
45+ If you try to just use a partial function, you'll most likely get an error
46+ about no instance being found for the ` Partial ` class. Take this program, for
47+ instance:
48+
49+ ``` purescript
50+ module Main where
51+
52+ import Prelude
53+ import Data.Maybe (Maybe(..), fromJust)
54+ import Effect (Effect)
55+ import Effect.Console (logShow)
56+
57+ main :: Effect Unit
58+ main = logShow (fromJust (Just 3))
59+ ```
60+
61+ Because ` fromJust ` is partial, and because the partiality hasn't been
62+ explicitly handled, you'll get an error:
63+
64+ ```
65+ at src/Main.purs line 8, column 1 - line 8, column 56
66+
67+ No type class instance was found for
68+
69+ Prim.Partial
70+ ```
71+
72+ * Aside: Yes, this is not a fantastic error. It's going to get better soon.*
73+
74+ The solution is usually to add an application of ` unsafePartial ` somewhere,
75+ like this:
76+
77+ ``` purescript
78+ module Main where
79+
80+ import Prelude
81+ import Data.Maybe (Maybe(..), fromJust)
82+ import Effect (Effect)
83+ import Effect.Console (logShow)
84+ import Partial.Unsafe (unsafePartial)
85+
86+ main :: Effect Unit
87+ main = logShow (unsafePartial (fromJust (Just 3)))
88+ ```
89+
90+ ## Where should I put unsafePartial?
91+
92+ The rule of thumb is to put ` unsafePartial ` at the level of your program such
93+ that the types tell the truth, and the part of your program responsible for
94+ making sure a use of a partial function is safe is also the part where the
95+ ` unsafePartial ` is. This is perhaps best demonstrated with an example.
96+
97+ Imagine that we want to represent vectors in 3D with an array containing
98+ exactly 3 values (perhaps we want to use them with some other API that expects
99+ this representation, and we don't want to be converting back and forth all the
100+ time). In this case, we would usually use a ` newtype ` and avoid exporting the
101+ constructor:
102+
103+ ``` purescript
104+ module Data.V3
105+ ( V3()
106+ , makeV3
107+ , runV3
108+ ) where
109+
110+ newtype V3 = V3 (Array Number)
111+
112+ makeV3 :: Number -> Number -> Number -> V3
113+ makeV3 x y z = V3 [x, y, z]
114+
115+ runV3 :: V3 -> Array Number
116+ runV3 (V3 v) = v
117+ ```
118+
119+ This way, all of the functions are safe; the code will guarantee that any ` V3 `
120+ does contain exactly 3 values (although the type checker is not aware of this).
121+
122+ Now imagine we want to write a dot product function:
123+
124+ ``` purescript
125+ dot :: V3 -> V3 -> Number
126+ dot (V3 [x1, x2, x3]) (V3 [y1, y2, y3]) = x1*y1 + x2*y2 + x3*y3
127+ ```
128+
129+ We know this is ok, but the compiler disallows it:
130+
131+ ```
132+ A case expression could not be determined to cover all inputs.
133+ The following additional cases are required to cover all inputs:
134+
135+ (V3 _) _
136+ _ (V3 _)
137+
138+ Alternatively, add a Partial constraint to the type of the enclosing value.
139+
140+ in value declaration dot
141+ ```
142+
143+ In this case, we can use ` unsafePartial ` to explicitly say that we don't
144+ actually need to worry about those other cases, and therefore we don't want to
145+ propagate a ` Partial ` constraint; users of this ` dot ` function should not have
146+ to worry about this partiality. For example:
147+
148+ ``` purescript
149+ dot :: V3 -> V3 -> Number
150+ dot x y = Partial.Unsafe.unsafePartial (go x y)
151+ where
152+ go :: Partial => V3 -> V3 -> Number
153+ go (V3 [x1, x2, x3]) (V3 [y1, y2, y3]) = x1*y1 + x2*y2 + x3*y3
154+ -- This second pattern can be omitted, but provides a better error message
155+ -- in case we do get an invalid argument at runtime.
156+ go _ _ = Partial.crash "Bad argument: expected exactly 3 elements."
157+ ```
158+
159+ The ` unsafePartial ` function comes from the ` Partial.Unsafe ` module, in the
160+ ` purescript-partial ` package.
161+
162+ In this case, we could also use ` Partial.Unsafe.unsafeCrashWith ` :
163+
164+ ``` purescript
165+ dot :: V3 -> V3 -> Number
166+ dot (V3 [x1, x2, x3]) (V3 [y1, y2, y3]) = x1*y1 + x2*y2 + x3*y3
167+ dot _ _ = unsafeCrashWith "Bad argument: expected exactly 3 elements."
168+ ```
169+
170+ Both implementations will behave in the same way.
171+
172+ In this case, we know our ` dot ` implementation is fine, and so users of it
173+ should not have to worry about its partiality, so it makes sense to avoid
174+ propagating the constraint. Now, we will see another case where a ` Partial `
175+ constraint * should* be propagated.
176+
177+ Let us suppose we want a ` foldr1 ` function, which works in a very similar way
178+ to ` foldr ` on Lists, except that it doesn't require an initial value to be
179+ passed, and instead requires that the list argument contains at least one
180+ element.
181+
182+ We can implement it like this:
183+
184+ ``` purescript
185+ foldr1 f (Cons x xs) = foldr f x xs
186+ ```
187+
188+ The compiler infers the correct type here, which is:
189+
190+ ``` purescript
191+ foldr1 :: forall a. Partial => (a -> a -> a) -> List a -> a
192+ ```
193+
194+ Now imagine we want a version of ` Data.Foldable.minimum ` which returns an ` a `
195+ instead of a ` Maybe a ` , and is therefore partial. We can implement it in terms
196+ of our new ` foldr1 ` function:
197+
198+ ``` purescript
199+ minimumP = foldr1 min
200+ ```
201+
202+ Again, the compiler infers the correct type:
203+
204+ ``` purescript
205+ minimumP :: forall a. (Partial, Ord a) => List a -> a
206+ ```
207+
208+ Notice that the ` Partial ` constraint is automatically propagated to the
209+ ` minimumP ` function because of the use of another partial function in its
210+ definition, namely ` foldr1 ` . In this case, this is what we want; we should
211+ propagate the ` Partial ` constraint, because it is still the caller's
212+ responsibility to make sure they supply a non-empty list.
213+
214+ So hopefully it is now clear why this partiality checking is implemented in
215+ terms of a type class: it allows us to elegantly reuse existing machinery in
216+ the type checker in order to check that a Partial constraint is either
217+ explictly handled or propagated. This should help ensure that when you're
218+ reading the code a few months later, it remains clear which part of the code is
219+ responsible for ensuring that any assumed invariants which cannot be encoded in
220+ the type system do hold.
221+
222+ ## API Documentation
223+
224+ * API documentation is [ published on Pursuit] ( http://pursuit.purescript.org/packages/purescript-partial ) .
0 commit comments