on master help sign in
Improve hints for "rigid type variables"

Always call things “user-defined type variables” instead of “rigid type variables” Try to be more non-specific in the case of a rigid/rigid clash, just link out to hint documentation.

commit90dc847a374a1d9f256594f9ce5a9ef38aadb32c authorEvan Czaplicki <info@elm-lang.org> dateTue 27 Oct 2015, 15:59 (11 years ago) parent55c1099
2 files changed+81−26
hints/type-annotations.md+66 src/Reporting/Error/Type.hs+15−26
hints/type-annotations.mdnew file+66
@@ -0,0 +1,66 @@
1+# Hints for Type Annotation Problems
2+
3+At the root of this kind of issue is always the fact that a type annotation in your code does not match the corresponding definition. Now that may mean that the type annotation is "wrong" or it may mean that the definition is "wrong". The compiler cannot figure out your intent, only that there is some mismatch.
4+
5+This document is going to outline the various things that can go wrong and show some examples.
6+
7+
8+## Annotation vs. Definition
9+
10+The most common issue is with user-defined type variables that are too general. So lets say you have defined a function like this:
11+
12+```elm
13+addPair : (a, a) -> a
14+addPair (x, y) =
15+ x + y
16+```
17+
18+The issue is that the type annotation is saying "I will accept a tuple containing literally *anything*" but the definition is using `(+)` which requires things to be numbers. So the compiler is going to infer that the true type of the definition is this:
19+
20+```elm
21+addPair : (number, number) -> number
22+```
23+
24+So you will probably see an error saying "I cannot match `a` with `number`" which is essentially saying, you are trying to provide a type annotation that is **too general**. You are saying `addPair` accepts anything, but in fact, it can only handle numbers.
25+
26+In cases like this, you want to go with whatever the compiler inferred. It is good at figuring this kind of stuff out ;)
27+
28+
29+## Annotation vs. Itself
30+
31+It is also possible to have a type annotation that clashes with itself. This is probably more rare, but someone will run into it eventually. Let's use another version of `addPair` with problems:
32+
33+```elm
34+addPair : (Int, Int) -> number
35+addPair (x, y) =
36+ x + y
37+```
38+
39+In this case the annotation says we should get a `number` out, but because we were specific about the inputs being `Int`, the output should also be an `Int`.
40+
41+
42+## Annotation vs. Internal Annotation
43+
44+A quite tricky case is when an outer type annotation clashes with an inner type annotation. Here is an example of this:
45+
46+```elm
47+filter : (a -> Bool) -> List a -> List a
48+filter isOkay list =
49+ let
50+ keepIfOkay : a -> Maybe a
51+ keepIfOkay x =
52+ if isOkay x then Just x else Nothing
53+ in
54+ List.filterMap keepIfOkay list
55+```
56+
57+This case is very unfortunate because all the type annotations are correct, but there is a detail of how type inference works right now that **user-defined type variables are not shared between annotations**. This can lead to probably the worst type error messages we have because the problem here is that `a` in the outer annotation does not equal `a` in the inner annotation.
58+
59+For now the best route is to leave off the inner annotation. It is unfortunate, and hopefully we will be able to do a nicer thing in future releases.
60+
61+
62+
63+
64+
65+
66+
src/Reporting/Error/Type.hs+15−26
@@ -508,41 +508,30 @@ reasonToString reason =
508508 go "Only strings and lists are both comparable and appendable."
509509
510510 BadVar (Just (Rigid _)) (Just (Rigid _)) ->
511− Just $ Help.stack $
512− [ hintDoc <> text "This usually happens when a few factors come together."
513− , indent 4 $ vcat $
514− [ text "1. You have a generic function with a type annotation."
515− , text "2. In its definition, there is a `let` with generic type annotations."
516− ]
517− , Help.reflowParagraph
518− "The issue is that these type variables are not actually shared between\
519− \ outer and inner type annotations right now. You can usually fix this by\
520− \ commenting out the inner type annotations."
521− ]
522−
523− BadVar (Just (Rigid (Just name))) _ ->
524− go $
525− "Looks like the type annotation is too generic. Type variable\
526− \ `" ++ name ++ "` is saying *many* kinds of value can be given, but the\
527− \ actual definition needs a more specific type. I am not sure whether the\
528− \ type annotation or the definition is \"right\" though, you may have to\
529− \ make the annotation more specific or make the definition more generic."
511+ go doubleRigidError
530512
531513 BadVar (Just (Rigid _)) _ ->
532− go genericRigidError
514+ go singleRigidError
533515
534516 BadVar _ (Just (Rigid _)) ->
535− go genericRigidError
517+ go singleRigidError
536518
537519 BadVar _ _ ->
538520 Nothing
539521
540522
541−genericRigidError :: String
542−genericRigidError =
543− "There is some problem with a rigid type variable. It is very likely that a\
544− \ type annotation is too generic. One type is saying *many* kinds of value\
545− \ can be given, but the other needs a more specific type."
523+singleRigidError :: String
524+singleRigidError =
525+ "A type annotation is too generic. You can probably just switch to the\
526+ \ type I inferred. These issues can be subtle though, so read more about it. "
527+ ++ Help.hintLink "type-annotations"
528+
529+
530+doubleRigidError :: String
531+doubleRigidError =
532+ "A type annotation is clashing with itself or with a sub-annotation.\
533+ \ This can be particularly tricky, so read more about it. "
534+ ++ Help.hintLink "type-annotations"
546535
547536
548537hintDoc :: Doc