Modules and imports
| A module file | |
| The export list | |
| Import forms | |
| Qualified imports |
A module file
A file is a module, and the module name must match the path from the project root. A file at src/Lib/Text.hs starts with the matching name:
module Lib.Text where
shout :: String -> String
shout = map toUpper
Without an export list every top-level name is public. That is fine for a small project and a trap for a library, where the internal helpers become part of the contract by accident.
The export list
The list after the module name decides what the outside world sees. Type(..) exports a type together with all its constructors:
module Lib.Text
( shout
, Config(..)
) where
data Config = Config { width :: Int }
helper = "private" -- not exported
Exporting Config without (..) hides the constructor, so callers can receive a value but cannot build one. That is how a module enforces a smart constructor.
Import forms
Three forms cover almost everything: import everything, import a list, or import everything except a list:
import Data.List -- everything
import Data.List (sort, nub) -- just these
import Data.List hiding (sort) -- everything else
import Data.Char (toUpper)
Explicit import lists are documentation. They also keep names from clashing when two modules export the same thing, which happens with map in Prelude and Data.Map.
Qualified imports
A qualified import forces a prefix at every use, which resolves clashes and makes the origin of a function obvious:
import qualified Data.Map as M
import qualified Data.Set as S
m = M.fromList [("a", 1), ("b", 2)]
M.lookup "a" m -- Just 1
S.fromList [3,1,3] -- fromList [1,3]
The short alias keeps lines readable. Import both qualified and
unqualified only when you genuinely need both, and expect the
compiler to warn about ambiguity when you do not.
Next:
Cabal projects
Article author: Arthur Isaev