Skip to content

Commit 16449d8

Browse files
authored
Remove the Partial type class guide, refs documentationjs#96 (documentationjs#240)
Link to the purescript-partial library instead, which is where the guide now lives. I've kept this page for now instead of removing it completely, because the compiler's Prim docs page still links to it as of 0.12.2 (see: <https://pursuit.purescript.org/builtins/docs/Prim#t:Partial>)
1 parent ed2bcd9 commit 16449d8

File tree

3 files changed

+4
-214
lines changed

3 files changed

+4
-214
lines changed

README.md

-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ This repository is a collaborative effort, so please feel free to make a pull re
3434
- [Contrib Library Guidelines](guides/Contrib-Guidelines.md)
3535
- [Error Suggestions](guides/Error-Suggestions.md)
3636
- [psc-ide FAQ](guides/psc-ide-FAQ.md)
37-
- [The `Partial` type class](guides/The-Partial-type-class.md)
3837
- [Try PureScript Help](https://github.com/purescript/trypurescript/blob/gh-pages/README.md)
3938

4039

errors/NoInstanceFound.md

+2-7
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,9 @@ To fix this, consider adding a type signature:
5353
better = show (mempty :: String)
5454
```
5555

56-
### Partial type class
56+
### Partial type class & non-exhaustive patterns
5757

58-
59-
(Please contribute an example)
60-
61-
### Partial type class & Non-exhaustive patterns
62-
63-
This error can occur if your code fails to propagate `Partial` constraints properly. For an introduction to the `Partial` type class, please see [The Partial type class](../guides/The-Partial-type-class.md) or the note [below](#Exhaustivity-Errors).
58+
This error can occur if your code fails to propagate `Partial` constraints properly. For an introduction to the `Partial` type class, please see the [purescript-partial library](https://pursuit.purescript.org/packages/purescript-partial).
6459

6560
As an example of this situation, consider the following function definition:
6661

guides/The-Partial-type-class.md

+2-206
Original file line numberDiff line numberDiff line change
@@ -1,207 +1,3 @@
1-
## Why have a Partial type class?
1+
# This guide has moved to the [purescript-partial library][partial]
22

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

0 commit comments

Comments
 (0)