Skip to content
AgileLogics
5 min read

Modules

Prerequisites: Algebraic data types

A module is one .hs file with a name. Modules group related code, control what's visible to other code, and make up libraries.

Importing

import Data.List (sort, nub)
import qualified Data.Map as Map
import Data.Char hiding (isSpace)
import qualified Data.Set as S

main :: IO ()
main = do
  print (sort (nub [3, 1, 3, 2]))
  print (Map.toList (Map.fromList [(1, "one"), (2, "two")]))
  print (toUpper 'q')
  print (S.member 2 (S.fromList [1, 2, 3]))
[1,2,3]
[(1,"one"),(2,"two")]
'Q'
True
Form Effect
import M Everything M exports, unqualified
import M (a, b) Only a and b
import M hiding (c) Everything except c
import qualified M as N Everything, but only as N.name

Modules like Data.Map reuse names from the Prelude (filter, map, lookup), so they're almost always imported qualified.

Writing a module

A module file starts with module Name (exports) where, and its path must match its name: Geometry.Shapes lives in Geometry/Shapes.hs.

-- illustrative: file Geometry/Shapes.hs
module Geometry.Shapes
  ( Shape (..)
  , area
  , Point
  , mkPoint
  ) where

data Shape = Circle Double | Square Double
  deriving (Show)

data Point = Point Double Double
  deriving (Show)

mkPoint :: Double -> Double -> Maybe Point
mkPoint x y
  | x >= 0 && y >= 0 = Just (Point x y)
  | otherwise = Nothing

area :: Shape -> Double
area (Circle r) = pi * r * r
area (Square s) = s * s

helper :: Int
helper = 42 -- not exported: private to this module
-- illustrative: file Main.hs, next to the Geometry folder
module Main (main) where

import Geometry.Shapes

main :: IO ()
main = do
  print (map area [Circle 1, Square 2])
  print (mkPoint 1 2)
[3.141592653589793,4.0]
Just (Point 1.0 2.0)
  • Shape (..) exports the type and its constructors.
  • Point without (..) exports only the type: other modules can't use the Point constructor, so every Point must go through mkPoint, which validates it. This is the smart constructor pattern — the Haskell way to enforce invariants.
  • A file without a module line is module Main (main) where.

Run the example from the folder containing Main.hs with runghc Main.hs — GHC finds Geometry/Shapes.hs automatically. In a cabal project, you list modules in the .cabal file instead (see the last lesson of this level).

Packages

Modules come from packages. base (the Prelude, Data.List, Data.Char, Control.Monad, ...) is always available. Packages such as containers (Data.Map, Data.Set), text, bytestring, mtl, and stm ship with GHC; everything else comes from Hackage, Haskell's package repository, via cabal.