on master help sign in
Add hints for incomplete pattern matches
commite6d02c5fe2e29d6689e08ebce71dc57361e238cb authorEvan Czaplicki <info@elm-lang.org> dateTue 27 Oct 2015, 18:25 (11 years ago) parentbe1239a
1 file changed+142
hints/missing-patterns.md+142
hints/missing-patterns.mdnew file+142
@@ -0,0 +1,142 @@
1+# Hints for Missing Patterns
2+
3+Elm 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 every going to crash because data had an unexpected shape.
4+
5+There are a couple techniques for making this work for you in every scenario.
6+
7+
8+## The danger of wildcard patterns
9+
10+A 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:
11+
12+```elm
13+type Widget
14+ = ScatterPlot ...
15+ | Table ...
16+
17+
18+isGraph : Widget -> Bool
19+isGraph widget =
20+ case widget of
21+ ScatterPlot _ ->
22+ True
23+
24+ _ ->
25+ False
26+```
27+
28+Notice 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.
29+
30+It is better to explicitly list all possible patterns, like this:
31+
32+```elm
33+isGraph : Widget -> Bool
34+isGraph widget =
35+ case widget of
36+ ScatterPlot _ ->
37+ True
38+
39+ Table _ ->
40+ False
41+```
42+
43+So 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!
44+
45+
46+## I want to go fast!
47+
48+Okay, 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.
49+
50+```elm
51+type Widget
52+ = ScatterPlot ...
53+ | Table ...
54+
55+
56+isGraph : Widget -> Bool
57+isGraph widget =
58+ case widget of
59+ ScatterPlot _ ->
60+ True
61+
62+ Table _ ->
63+ False
64+
65+
66+-- and maybe a bunch of other things
67+```
68+
69+So 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.
70+
71+To 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:
72+
73+```elm
74+type Widget
75+ = ScatterPlot ...
76+ | Table ...
77+ | BarChart ...
78+
79+
80+isGraph : Widget -> Bool
81+isGraph widget =
82+ case widget of
83+ ScatterPlot _ ->
84+ True
85+
86+ Table _ ->
87+ False
88+
89+ BarChart _ ->
90+ Debug.crash "TODO"
91+
92+
93+-- and maybe a bunch of other things
94+```
95+
96+Rather 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:
97+
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.
101+
102+From that information you have a pretty good idea of what went wrong and can go fix it.
103+
104+I 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.
105+
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.
107+
108+
109+## A list that definitely is not empty
110+
111+This can come up from time to time, but Elm **will not** let you write code like this:
112+
113+```elm
114+last : List a -> a
115+last list =
116+ case list of
117+ [x] ->
118+ x
119+
120+ _ :: rest ->
121+ last rest
122+```
123+
124+This is no good. It does not handle the empty list. The recommended definition would be:
125+
126+```elm
127+last : List a -> Maybe a
128+last list =
129+ case list of
130+ [] ->
131+ Nothing
132+
133+ [x] ->
134+ Just x
135+
136+ _ :: rest ->
137+ last rest
138+```
139+
140+This is nice because it lets users know that there might be a failure, so they can recover from it however they want.
141+
142+**Note:** You can define helpers like [`(?)`](https://github.com/elm-lang/core/issues/216) or [`unsafe`](https://github.com/elm-lang/core/issues/215) to make it easier to work with `Maybe` values a bunch. These helpers are being considered for inclusion in core, but it's not hard to put them in your own code and see if they make things nicer for you.