on master help sign in
Add a hint for canonicalization errors
61e7508Evan Czaplicki
hints/imports.mdnew file+115
@@ -0,0 +1,115 @@
1+# Hints for Imports
2+
3+When getting started with Elm, it is pretty common to have questions about how the `import` declarations work exactly. These questions usually arise when you start playing with the `Html` library so we will focus on that.
4+
5+
6+## `import`
7+
8+An Elm file is called a **module**. To access code in other files, you need to `import` it!
9+
10+So say you want to use the [`div`](http://package.elm-lang.org/packages/elm-lang/html/latest/Html#div) function from the [`elm-lang/html`](http://package.elm-lang.org/packages/elm-lang/html/latest) package. The simplest way is to import it like this:
11+
12+```elm
13+import Html
14+
15+main =
16+ Html.div [] []
17+```
18+
19+After saying `import Html` we can refer to anything inside that module as long as it is *qualified*. This works for:
20+
21+ - **Values** — we can refer to `Html.text`, `Html.h1`, etc.
22+ - **Types** — We can refer to [`Attribute`](http://package.elm-lang.org/packages/elm-lang/html/latest/Html#Attribute) as `Html.Attribute`.
23+
24+So if we add a type annotation to `main` it would look like this:
25+
26+```elm
27+import Html
28+
29+main : Html.Html msg
30+main =
31+ Html.div [] []
32+```
33+
34+We are referring to the [`Html`](http://package.elm-lang.org/packages/elm-lang/html/latest/Html#Html) type, using its *qualified* name `Html.Html`. This can feel weird at first, but it starts feeling natural quite quickly!
35+
36+> **Note:** Modules do not contain other modules. So the `Html` module *does not* contain the `Html.Attributes` module. Those are separate names that happen to have some overlap. So if you say `import Html` you *do not* get access to `Html.Attributes.style`. You must `import Html.Attributes` module separately.
37+
38+
39+## `as`
40+
41+It is best practice to always use *qualified* names, but sometimes module names are so long that it becomes unweildy. This is common for the `Html.Attributes` module. We can use the `as` keyword to help with this:
42+
43+```elm
44+import Html
45+import Html.Attributes as A
46+
47+main =
48+ Html.div [ A.style "color" "red" ] [ Html.text "Hello!" ]
49+```
50+
51+Saying `import Html.Attributes as A` lets us refer to any value or type in `Html.Attributes` as long as it is qualified with an `A`. So now we can refer to [`style`](http://package.elm-lang.org/packages/elm-lang/html/latest/Html-Attributes#style) as `A.style`.
52+
53+
54+## `exposing`
55+
56+In quick drafts, maybe you want to use *unqualified* names. You can do that with the `exposing` keyword like this:
57+
58+```elm
59+import Html exposing (..)
60+import Html.Attributes exposing (style)
61+
62+main : Html msg
63+main =
64+ div [ style "color" "red" ] [ text "Hello!" ]
65+```
66+
67+Saying `import Html exposing (..)` means I can refer to any value or type from the `Html` module without qualification. Notice that I use the `Html` type, the `div` function, and the `text` function without qualification in the example above.
68+
69+> **Note:** It seems neat to expose types and values directly, but it can get out of hand. Say you `import` ten modules `exposing` all of their content. It quickly becomes difficult to figure out what is going on in your code. “Wait, where is this function from?” And then trying to sort through all the imports to find it. Point is, use `exposing (..)` sparingly!
70+
71+Saying `import Html.Attributes exposing (style)` is a bit more reasonable. It means I can refer to the `style` function without qualification, but that is it. You are still importing the `Html.Attributes` module like normal though, so you would say `Html.Attributes.class` or `Html.Attributes.id` to refer to other values and types from that module.
72+
73+
74+## `as` and `exposing`
75+
76+There is one last way to import a module. You can combine `as` and `exposing` to try to get a nice balance of qualified names:
77+
78+```elm
79+import Html exposing (Html, div, text)
80+import Html.Attributes as A exposing (style)
81+
82+main : Html msg
83+main =
84+ div [ A.class "greeting", style "color" "red" ] [ text "Hello!" ]
85+```
86+
87+Notice that I refer to `A.class` which is qualified and `style` which is unqualified.
88+
89+
90+## Default Imports
91+
92+We just learned all the variations of the `import` syntax in Elm. You will use some version of that syntay to `import` any module you ever write.
93+
94+It would be the best policy to make it so every module in the whole ecosystem works this way. We thought so in the past at least, but there are some modules that are so commonly used that the Elm compiler automatically adds the imports to every file. These default imports include:
95+
96+```elm
97+import Basics exposing (..)
98+import List exposing ( List, (::) )
99+import Maybe exposing ( Maybe( Just, Nothing ) )
100+import Result exposing ( Result( Ok, Err ) )
101+import String
102+import Tuple
103+
104+import Debug
105+
106+import Platform exposing ( Program )
107+import Platform.Cmd as Cmd exposing ( Cmd )
108+import Platform.Sub as Sub exposing ( Sub )
109+```
110+
111+You can think of these imports being at the top of any module you write.
112+
113+One could argue that `Maybe` fundamental to how we handle errors in Elm code that it is *basically* part of the language. One could also argue that it is extraordinarily annoying to have to import `Maybe` once you get past your first couple weeks with Elm. Either way, we know that default imports are not ideal in some sense, so we have tried to keep the default imports as minimal as possible.
114+
115+> **Note:** Elm performs dead code elimination, so if you do not use something from a module, it is not included in the generated code. So if you `import` a module with hundreds of functions, you do not need to worry about the size of your assets. You will only get what you use!
src/AST/Module.hs+1−1
@@ -133,7 +133,7 @@ type Optimized =
133-- IMPORTS
133-- IMPORTS
134
134
135
135
136−type UserImport = A.Located (Name.Raw, ImportMethod)
136+type UserImport = A.Located (A.Located Name.Raw, ImportMethod)
137
137
138
138
139type DefaultImport = (Name.Raw, ImportMethod)
139type DefaultImport = (Name.Raw, ImportMethod)
src/Canonicalize.hs+1−1
@@ -112,7 +112,7 @@ filterImports
112 -> Result [ModuleName.Raw]
112 -> Result [ModuleName.Raw]
113filterImports uses (defaults, imports) =
113filterImports uses (defaults, imports) =
114 let
114 let
115− checkImport (A.A region (name, _method)) =
115+ checkImport (A.A region (A.A _ name, _method)) =
116 if Set.member name uses then
116 if Set.member name uses then
117 return (Just name)
117 return (Just name)
118
118
src/Canonicalize/Setup.hs+12−3
@@ -42,7 +42,7 @@ environment importDict interfaces (Module.Module name info) =
42 info
42 info
43
43
44 allImports =
44 allImports =
45− imports ++ map (A.A (error "default import not found")) defaults
45+ imports ++ map toPretendUserImport defaults
46
46
47 getImportPatches =
47 getImportPatches =
48 traverse (importToPatches importDict interfaces) allImports
48 traverse (importToPatches importDict interfaces) allImports
@@ -62,6 +62,15 @@ environment importDict interfaces (Module.Module name info) =
62 addTypeAliases name typeAliasNodes env
62 addTypeAliases name typeAliasNodes env
63
63
64
64
65+toPretendUserImport :: Module.DefaultImport -> Module.UserImport
66+toPretendUserImport (name, method) =
67+ let
68+ errLoc =
69+ error "default import not found"
70+ in
71+ A.A errLoc ( A.A errLoc name, method )
72+
73+
65
74
66-- PATCHES FOR IMPORTS
75-- PATCHES FOR IMPORTS
67
76
@@ -69,9 +78,9 @@ environment importDict interfaces (Module.Module name info) =
69importToPatches
78importToPatches
70 :: Map.Map ModuleName.Raw ModuleName.Canonical
79 :: Map.Map ModuleName.Raw ModuleName.Canonical
71 -> Module.Interfaces
80 -> Module.Interfaces
72− -> A.Located (ModuleName.Raw, Module.ImportMethod)
81+ -> Module.UserImport
73 -> Result [Env.Patch]
82 -> Result [Env.Patch]
74−importToPatches importDict allInterfaces (A.A region (rawImportName, method)) =
83+importToPatches importDict allInterfaces (A.A _ (A.A region rawImportName, method)) =
75 let
84 let
76 maybeInterface =
85 maybeInterface =
77 do canonicalName <- Map.lookup rawImportName importDict
86 do canonicalName <- Map.lookup rawImportName importDict
src/Elm/Compiler.hs+2−2
@@ -187,8 +187,8 @@ toHeaderSummary pkgName (Module.Header maybeHeaderDecl imports) =
187 let
187 let
188 dependencies =
188 dependencies =
189 if pkgName == Package.core
189 if pkgName == Package.core
190− then map (fst . A.drop) imports
190+ then map (A.drop . fst . A.drop) imports
191− else map (fst . A.drop) imports ++ map fst Imports.defaults
191+ else map (A.drop . fst . A.drop) imports ++ map fst Imports.defaults
192 in
192 in
193 case maybeHeaderDecl of
193 case maybeHeaderDecl of
194 Nothing ->
194 Nothing ->
src/Parse/Kernel.hs+1−1
@@ -91,7 +91,7 @@ type Table =
91
91
92
92
93importToTable :: Module.UserImport -> Table
93importToTable :: Module.UserImport -> Table
94−importToTable (A.A _ ( fullName, Module.ImportMethod maybeAlias exposed )) =
94+importToTable (A.A _ ( A.A _ fullName, Module.ImportMethod maybeAlias exposed )) =
95 let
95 let
96 shortName =
96 shortName =
97 case maybeAlias of
97 case maybeAlias of
src/Parse/Module.hs+4−4
@@ -164,7 +164,7 @@ chompImports imports =
164 keyword "import"
164 keyword "import"
165 pushContext start E.Import
165 pushContext start E.Import
166 spaces
166 spaces
167− name <- qualifiedCapVar
167+ name <- addLocation qualifiedCapVar
168 end <- getPosition
168 end <- getPosition
169 pos <- whitespace
169 pos <- whitespace
170 oneOf
170 oneOf
@@ -182,7 +182,7 @@ chompImports imports =
182 ]
182 ]
183
183
184
184
185−chompAs :: R.Position -> Text -> [Module.UserImport] -> Parser [Module.UserImport]
185+chompAs :: R.Position -> A.Located Text -> [Module.UserImport] -> Parser [Module.UserImport]
186chompAs start name imports =
186chompAs start name imports =
187 do keyword "as"
187 do keyword "as"
188 spaces
188 spaces
@@ -199,7 +199,7 @@ chompAs start name imports =
199 ]
199 ]
200
200
201
201
202−chompExposing :: R.Position -> Text -> Maybe Text -> [Module.UserImport] -> Parser [Module.UserImport]
202+chompExposing :: R.Position -> A.Located Text -> Maybe Text -> [Module.UserImport] -> Parser [Module.UserImport]
203chompExposing start name maybeAlias imports =
203chompExposing start name maybeAlias imports =
204 do keyword "exposing"
204 do keyword "exposing"
205 spaces
205 spaces
@@ -211,7 +211,7 @@ chompExposing start name maybeAlias imports =
211 chompImports (userImport:imports)
211 chompImports (userImport:imports)
212
212
213
213
214−method :: R.Position -> R.Position -> Text -> Maybe Text -> Exposing.Raw -> Module.UserImport
214+method :: R.Position -> R.Position -> A.Located Text -> Maybe Text -> Exposing.Raw -> Module.UserImport
215method start end name maybeAlias exposed =
215method start end name maybeAlias exposed =
216 A.at start end ( name, Module.ImportMethod maybeAlias exposed )
216 A.at start end ( name, Module.ImportMethod maybeAlias exposed )
217
217
src/Reporting/Error/Canonicalize.hs+35−31
@@ -133,41 +133,47 @@ port name tipe maybeMessage =
133-- TO REPORT
133-- TO REPORT
134
134
135
135
136−namingError :: Text -> Text -> Report.Report
137−namingError pre post =
138− Report.report "NAMING ERROR" Nothing pre (text post)
139−
140−
141toReport :: RenderType.Localizer -> Error -> Report.Report
136toReport :: RenderType.Localizer -> Error -> Report.Report
142toReport localizer err =
137toReport localizer err =
143 case err of
138 case err of
144 Var (VarError kind name problem suggestions) ->
139 Var (VarError kind name problem suggestions) ->
140+ let
141+ variableName =
145− let var = kind <> " `" <> name <> "`"
142+ kind <> " `" <> name <> "`"
143+
144+ learnMore =
145+ Help.toHint $
146+ "New to Elm? Unsure exactly how `import` works? Learn all about it here: "
147+ <> Help.hintLink "imports"
148+
149+ namingError overview maybeStarter specializedSuggestions =
150+ Report.report "NAMING ERROR" Nothing overview $
151+ maybeYouWant maybeStarter specializedSuggestions (Just learnMore)
146 in
152 in
147 case problem of
153 case problem of
148 Ambiguous ->
154 Ambiguous ->
149 namingError
155 namingError
150− ("This usage of " <> var <> " is ambiguous.")
156+ ("This usage of " <> variableName <> " is ambiguous.")
157+ Nothing
151− (maybeYouWant suggestions)
158+ suggestions
152
159
153 UnknownQualifier qualifier localName ->
160 UnknownQualifier qualifier localName ->
154 namingError
161 namingError
155− ("Cannot find " <> var <> ".")
162+ ("Cannot find " <> variableName)
156− ( "No module called `" <> qualifier <> "` has been imported. "
163+ (Just $ text $ "No module called `" <> qualifier <> "` has been imported.")
157− <> maybeYouWant (map (\modul -> modul <> "." <> localName) suggestions)
164+ (map (\modul -> modul <> "." <> localName) suggestions)
158− )
159
165
160 QualifiedUnknown qualifier localName ->
166 QualifiedUnknown qualifier localName ->
161 namingError
167 namingError
162− ("Cannot find " <> var <> ".")
168+ ("Cannot find " <> variableName)
163− ( "`" <> qualifier <> "` does not expose `" <> localName <> "`. "
169+ (Just $ text $ "`" <> qualifier <> "` does not expose `" <> localName <> "`.")
164− <> maybeYouWant (map (\v -> qualifier <> "." <> v) suggestions)
170+ (map (\v -> qualifier <> "." <> v) suggestions)
165− )
166
171
167 ExposedUnknown ->
172 ExposedUnknown ->
168 namingError
173 namingError
169− ("Cannot find " <> var)
174+ ("Cannot find " <> variableName)
175+ Nothing
170− (maybeYouWant suggestions)
176+ suggestions
171
177
172 BadRecursion region def defs ->
178 BadRecursion region def defs ->
173 case defs of
179 case defs of
@@ -234,27 +240,25 @@ toReport localizer err =
234 )
240 )
235
241
236 Import name importError ->
242 Import name importError ->
237− let moduleName = ModuleName.toText name
238− in
239 case importError of
243 case importError of
240 ModuleNotFound suggestions ->
244 ModuleNotFound suggestions ->
241− namingError
245+ Report.report "IMPORT ERROR" Nothing
242− ("Could not find a module named `" <> moduleName <> "`")
246+ ("Could not find a module named `" <> ModuleName.toText name <> "`")
243− (maybeYouWant suggestions)
247+ (maybeYouWant Nothing suggestions Nothing)
244
248
245 ValueNotFound value suggestions ->
249 ValueNotFound value suggestions ->
246− namingError
250+ Report.report "IMPORT ERROR" Nothing
247− ("Module `" <> moduleName <> "` does not expose `" <> value <> "`")
251+ ("Module `" <> ModuleName.toText name <> "` does not expose `" <> value <> "`")
248− (maybeYouWant suggestions)
252+ (maybeYouWant Nothing suggestions Nothing)
249
253
250 Export name suggestions ->
254 Export name suggestions ->
251− namingError
255+ Report.report "EXPOSING ERROR" Nothing
252− ("Could not export `" <> name <> "` which is not defined in this module.")
256+ ("Could not expose `" <> name <> "` which is not defined in this module.")
253− (maybeYouWant suggestions)
257+ (maybeYouWant Nothing suggestions Nothing)
254
258
255 DuplicateExport name ->
259 DuplicateExport name ->
256− namingError
260+ Report.report "EXPOSING ERROR" Nothing
257− ("You are trying to export `" <> name <> "` multiple times!")
261+ ("You are trying to expose `" <> name <> "` multiple times!")
258 "Remove duplicates until there is only one listed."
262 "Remove duplicates until there is only one listed."
259
263
260 Port (PortError name tipe maybeMessage) ->
264 Port (PortError name tipe maybeMessage) ->
src/Reporting/Error/Docs.hs+1−3
@@ -42,9 +42,7 @@ toReport err =
42 Nothing
42 Nothing
43 ("Your module documentation includes `" <> name <> "` which is not exported."
43 ("Your module documentation includes `" <> name <> "` which is not exported."
44 )
44 )
45− ( text $
46− "Is it misspelled? Should it be exported? "
45+ ( Help.maybeYouWant (Just "Is it misspelled? Should it be exported?") suggestions Nothing
47− <> Help.maybeYouWant suggestions
48 )
46 )
49
47
50 OnlyInExports names ->
48 OnlyInExports names ->
src/Reporting/Error/Type.hs+2−12
@@ -24,8 +24,8 @@ import qualified Reporting.Render.Type as RenderType
24import qualified Reporting.Report as Report
24import qualified Reporting.Report as Report
25import qualified Reporting.Helpers as Help
25import qualified Reporting.Helpers as Help
26import Reporting.Helpers
26import Reporting.Helpers
27− ( Doc, (<>), (<+>), capitalize, dullyellow, fillSep, functionName
27+ ( Doc, (<>), (<+>), capitalize, dullyellow, functionName
28− , i2t, indent, ordinalize, reflowParagraph, stack, text, underline, vcat
28+ , i2t, indent, ordinalize, reflowParagraph, stack, text, toHint, vcat
29 )
29 )
30
30
31
31
@@ -845,16 +845,6 @@ doubleRigidError maybeLeftName maybeRightName =
845 weirdRigidError
845 weirdRigidError
846
846
847
847
848−hintDoc :: Doc
849−hintDoc =
850− underline (text "Hint") <> ":"
851−
852−
853−toHint :: Text -> Doc
854−toHint txt =
855− fillSep (hintDoc : map text (Text.words txt))
856−
857−
858messyFieldsHelp :: [Text] -> [Text] -> [Text] -> Maybe (Text, [Doc])
848messyFieldsHelp :: [Text] -> [Text] -> [Text] -> Maybe (Text, [Doc])
859messyFieldsHelp both leftOnly rightOnly =
849messyFieldsHelp both leftOnly rightOnly =
860 case (leftOnly, rightOnly) of
850 case (leftOnly, rightOnly) of
src/Reporting/Helpers.hs+33−7
@@ -8,7 +8,7 @@ module Reporting.Helpers
8 -- custom helpers
8 -- custom helpers
9 , i2t
9 , i2t
10 , functionName, args
10 , functionName, args
11− , hintLink, stack, reflowParagraph
11+ , toHint, hintLink, stack, reflowParagraph
12 , commaSep, capitalize, ordinalize, drawCycle
12 , commaSep, capitalize, ordinalize, drawCycle
13 , findPotentialTypos, findTypoPairs, vetTypos
13 , findPotentialTypos, findTypoPairs, vetTypos
14 , nearbyNames, distance, maybeYouWant
14 , nearbyNames, distance, maybeYouWant
@@ -72,6 +72,19 @@ args n =
72 i2t n <> if n == 1 then " argument" else " arguments"
72 i2t n <> if n == 1 then " argument" else " arguments"
73
73
74
74
75+
76+-- HINTS
77+
78+
79+toHint :: Text -> Doc
80+toHint txt =
81+ let
82+ hint =
83+ underline (text "Hint") <> ":"
84+ in
85+ fillSep (hint : map text (Text.words txt))
86+
87+
75hintLink :: Text -> Text
88hintLink :: Text -> Text
76hintLink fileName =
89hintLink fileName =
77 "<https://github.com/elm-lang/elm-compiler/blob/"
90 "<https://github.com/elm-lang/elm-compiler/blob/"
@@ -221,14 +234,27 @@ distance x y =
221 (Text.unpack y)
234 (Text.unpack y)
222
235
223
236
224−maybeYouWant :: [Text] -> Text
237+maybeYouWant :: Maybe Doc -> [Text] -> Maybe Doc -> Doc
225−maybeYouWant suggestions =
238+maybeYouWant maybeStarter suggestions maybeEnder =
226 case suggestions of
239 case suggestions of
227 [] ->
240 [] ->
228− ""
241+ case (maybeStarter, maybeEnder) of
242+ (Nothing, Nothing) ->
243+ P.empty
244+
245+ (Just starter, Nothing) ->
246+ starter
247+
248+ (Nothing, Just ender) ->
249+ ender
250+
251+ (Just starter, Just ender) ->
252+ stack [ starter, ender ]
229
253
230 _:_ ->
254 _:_ ->
255+ stack $
231− "Maybe you want one of the following?\n"
256+ [ maybe id (<+>) maybeStarter "Maybe you want one of the following?"
232− <>
233− Text.concat (map ("\n " <>) (take 4 suggestions))
257+ , P.indent 4 (P.vcat (map text (take 4 suggestions)))
258+ ]
259+ ++ maybe [] pure maybeEnder
234
260