on 8f7c3cd help sign in
elk/docs/hints/missing-patterns.md
142 lines 4.3 kB history raw
1# Hints for Missing Patterns
3Elm checks to make sure that all possible inputs to a function or `case` are handled. This gives us the guarantee that no Elm code is ever going to crash because data had an unexpected shape.
5There are a couple techniques for making this work for you in every scenario.
8## The danger of wildcard patterns
10A common scenario is that you want to add a tag to a union type that is used in a bunch of places. For example, maybe you are working with some widgets:
12```elm
13type Widget
14 = ScatterPlot ...
15 | Table ...
18isGraph : Widget -> Bool
19isGraph widget =
20 case widget of
21 ScatterPlot _ ->
22 True
24 _ ->
25 False
26```
28Notice the wildcard pattern in `isGraph`. This will hurt us! When you add `BarChart` as a new kind of `Widget` you are introducing a bug that the compiler cannot catch. A `BarChart` is a graph, but the code deciding that is just lumping a bunch of stuff together.
30It is better to explicitly list all possible patterns, like this:
32```elm
33isGraph : Widget -> Bool
34isGraph widget =
35 case widget of
36 ScatterPlot _ ->
37 True
39 Table _ ->
40 False
41```
43So now when you want to add `BarChart`, the compiler will say "hey, what should `isGraph` do when it sees a bar chart?" This is a tiny bit of extra work, but it is very worth it!
46## I want to go fast!
48Okay, so we are still editing that `Widget` code. We are not using any dangerous wildcards anymore, but there are 20 or 30 functions all pattern matching on `Widget` and doing something.
50```elm
51type Widget
52 = ScatterPlot ...
53 | Table ...
56isGraph : Widget -> Bool
57isGraph widget =
58 case widget of
59 ScatterPlot _ ->
60 True
62 Table _ ->
63 False
66-- and maybe a bunch of other things
67```
69So now you add a new tag `BarChart` to `Widget`. This will trigger errors in all the functions that pattern match on widgets. In a big project, maybe this is a ton of functions.
71To get things running quickly, it can be helpful to use [`Debug.crash`](http://package.elm-lang.org/packages/elm-lang/core/latest/Debug#crash) temporarily. So you might update the code to this:
73```elm
74type Widget
75 = ScatterPlot ...
76 | Table ...
77 | BarChart ...
80isGraph : Widget -> Bool
81isGraph widget =
82 case widget of
83 ScatterPlot _ ->
84 True
86 Table _ ->
87 False
89 BarChart _ ->
90 Debug.crash "TODO"
93-- and maybe a bunch of other things
94```
96Rather than figure out the exact details, just put in `Debug.crash "TODO"` for now. The Elm compiler is actually aware of `Debug.crash` so when it sees it in a `case` like this, it will augment the crash message with a bunch of helpful information. It will tell you:
98 1. The name of the module that contains the offending code.
99 2. The line numbers of the `case` containing the crash.
100 3. The particular value that led to this crashing branch.
102From that information you have a pretty good idea of what went wrong and can go fix it.
104I tend to use `TODO` as the message when my goal is to go quick because it makes it easy to go and find all remaining todos in my code before a release.
106> And in 99.999% of cases, a `Debug.crash` should never make it to a release. It is nice for debugging (like it says in the name) but it is not really a good idea to use it in other ways. When you hit a `Debug.crash` the program really does crash. It is just done working.
109## A list that definitely is not empty
111This can come up from time to time, but Elm **will not** let you write code like this:
113```elm
114last : List a -> a
115last list =
116 case list of
117 [x] ->
118 x
120 _ :: rest ->
121 last rest
122```
124This is no good. It does not handle the empty list. The recommended definition would be:
126```elm
127last : List a -> Maybe a
128last list =
129 case list of
130 [] ->
131 Nothing
133 [x] ->
134 Just x
136 _ :: rest ->
137 last rest
138```
140This is nice because it lets users know that there might be a failure, so they can recover from it however they want.
142> An alternative would be to represent a non-empty list explicitly. For example, the type `(a, List a)` guarantees that there is at least one `a` value, so you could alternatively create a function `last : (a, List a) -> a` or `last : a -> List a -> a` to demand at least one item.