MODULE Basics
Haskell's module system organizes functions, types, and type classes into namespaces and lets you precisely control what is visible to the outside. One .hs file corresponds to one module. The module declaration states the module name and lists the symbols to export. In import statements, you can combine qualified for name-qualified access, hiding to exclude specific names, and as for aliases — letting you bring in only the symbols you need while avoiding name collisions.
module declaration syntax
| Syntax | Description |
|---|---|
module ModName where | Declares the module. Omitting the export list makes all top-level definitions public. |
module ModName (f, g) where | Exports only functions f and g. Symbols not in the list cannot be accessed from outside the module. |
module ModName (T(..)) where | Exports type T along with all of its constructors. |
module ModName (T) where | Exports only the type name T while hiding its constructors (the smart constructor pattern). |
module ModName (module SubMod) where | Re-exports all public symbols of another module as-is. |
import statement syntax
| Syntax | Description |
|---|---|
import ModName | Brings all exported symbols of the module into scope. |
import ModName (f, T(..)) | Brings only the specified symbols into scope. |
import qualified ModName as M | Allows only qualified references. Call symbols as M.f. |
import ModName hiding (f) | Brings everything into scope except f. |
import ModName as M | Creates an alias that lets you access symbols both with and without the qualifier. |
Commonly used modules
| Module | Key exports | Description |
|---|---|---|
Data.List | sort, nub, group, intercalate | Extended list manipulation functions not found in the standard Prelude. |
Data.Map.Strict | Map k v, fromList, lookup, insert | Provides a balanced-tree-based associative array (dictionary). The convention is to import it qualified. |
Data.Set | Set a, fromList, member, union | Provides an ordered set with no duplicates. The convention is also to import it qualified. |
Data.Maybe | fromMaybe, mapMaybe, isJust | Utility functions for working with Maybe values. |
Data.Char | isAlpha, toUpper, ord, chr | Functions for classifying and converting characters. |
System.IO | hSetBuffering, Handle, BufferMode | Provides buffering control and other file handle operations. |
Control.Monad | when, unless, forM_, replicateM | Helper functions for working with monads. |
Sample code
rgg_module_export.hs
-- rgg_module_export.hs — sample showing module definition and export control
-- Uses yakuza organizations as sample data
-- Demonstrates how to make symbols public or private in a module declaration
--
-- Compile and run:
-- ghc rgg_module_export.hs -o rgg_module_export && ./rgg_module_export
-- -----------------------------------------------
-- Sub-module equivalent definitions (simulated in a single file)
-- -----------------------------------------------
-- In a real project you would split this into a separate file and declare:
-- module Yakuza (Clan(..), makeClan, clanName, memberCount) where
-- Here everything is merged into Main for easy testing.
module Main where
import Data.List (intercalate, sortBy)
import Data.Ord (comparing, Down(..))
-- -----------------------------------------------
-- Data type definitions
-- -----------------------------------------------
-- Data type representing a yakuza organization
-- The constructor is kept private; use the smart constructor to create values
data Clan = Clan
{ clanName :: String -- organization name
, clanLeader :: String -- boss / chairman name
, memberCount :: Int -- number of members
} deriving (Show)
-- -----------------------------------------------
-- Smart constructor (creates a value with validation)
-- -----------------------------------------------
-- Smart constructor that only creates a Clan when memberCount is >= 0
-- This function is exported instead of the raw Clan constructor
makeClan :: String -> String -> Int -> Maybe Clan
makeClan name leader count
| count >= 0 = Just (Clan name leader count)
| otherwise = Nothing -- invalid values are rejected as Nothing
-- -----------------------------------------------
-- Public functions
-- -----------------------------------------------
-- Returns a summary string for a Clan
summaryClan :: Clan -> String
summaryClan c =
clanName c ++ " (" ++ clanLeader c ++ ") / Members: "
++ show (memberCount c)
-- Sorts clans by member count in descending order and returns their summaries
rankClans :: [Clan] -> [String]
rankClans clans =
map summaryClan -- convert each Clan to a summary string
$ sortBy (comparing (Down . memberCount)) clans -- sort by member count descending
-- -----------------------------------------------
-- Entry point
-- -----------------------------------------------
main :: IO ()
main = do
putStrLn "===== Omi Alliance — Affiliated Organizations ====="
putStrLn ""
-- Create Clan values using the smart constructor
-- The raw Clan{..} constructor is treated as unavailable by design
let clans =
[ makeClan "Tojo Clan" "Kazuma Kiryu" 25800
, makeClan "Omi Alliance" "Omi Ren" 31200
, makeClan "Daigo Family" "Daigo" 6400
, makeClan "Dojima Family" "Dojima Tsuyoki" 3200
, makeClan "3rd Katsuki Clan" "Katsuki" 1100
, makeClan "Majima Clan" "Goro Majima" 980
]
-- Unwrap Just values and discard Nothing
let validClans = [ c | Just c <- clans ]
-- Display ranking by member count descending
putStrLn "--- Member Count Ranking ---"
mapM_ (\(i, s) -> putStrLn $ show i ++ ": " ++ s)
(zip [1..] (rankClans validClans))
putStrLn ""
putStrLn "\"If you live by the yakuza code, live it with conviction.\""
ghc rgg_module_export.hs -o rgg_module_export && ./rgg_module_export [1 of 1] Compiling Main ( rgg_module_export.hs, rgg_module_export.o ) Linking rgg_module_export ... ===== Omi Alliance — Affiliated Organizations ===== --- Member Count Ranking --- 1: Omi Alliance (Omi Ren) / Members: 31200 2: Tojo Clan (Kazuma Kiryu) / Members: 25800 3: Daigo Family (Daigo) / Members: 6400 4: Dojima Family (Dojima Tsuyoki) / Members: 3200 5: 3rd Katsuki Clan (Katsuki) / Members: 1100 6: Majima Clan (Goro Majima) / Members: 980 "If you live by the yakuza code, live it with conviction."
rgg_import_qualified.hs
-- rgg_import_qualified.hs — sample showing qualified import / hiding / as
-- Uses characters from Like a Dragon as sample data
-- Demonstrates using Data.Map.Strict and Data.Set with qualified imports
--
-- Compile and run:
-- ghc rgg_import_qualified.hs -o rgg_import_qualified && ./rgg_import_qualified
module Main where
-- Import Data.Map.Strict qualified as Map
-- Using fromList etc. without a qualifier would clash with Data.List, so qualified is the convention
import qualified Data.Map.Strict as Map
-- Import Data.Set qualified as Set
import qualified Data.Set as Set
-- Import only intercalate from Data.List (selective import to avoid name collisions)
import Data.List (intercalate)
-- Import only fromMaybe from Data.Maybe
import Data.Maybe (fromMaybe)
-- -----------------------------------------------
-- Data definitions
-- -----------------------------------------------
-- Manages each character's signature weapon in a Map
-- Map.fromList :: [(k, v)] -> Map k v
weaponMap :: Map.Map String String
weaponMap = Map.fromList
[ ("Kazuma Kiryu", "Bare hands (Brawler style)")
, ("Goro Majima", "Knife / bat (Mad Dog style)")
, ("Shun Akiyama", "Kicks (Like a Dragon style)")
, ("Taiga Saejima", "Kusarigama (Slug style)")
, ("Tatsuo Shinada","Handgun (Gunman style)")
, ("Makoto Date", "Knife (Capoeira style)")
]
-- Manages Tojo Clan and Omi Alliance members in Sets
-- Set.fromList :: [a] -> Set a
tojokaiMembers :: Set.Set String
tojokaiMembers = Set.fromList
[ "Kazuma Kiryu", "Akira Nishikiyama", "Daigo Dojima", "Makoto Date", "Taiga Saejima" ]
omikaiMembers :: Set.Set String
omikaiMembers = Set.fromList
[ "Omi Ren", "Goro Majima", "Tatsuo Shinada" ]
-- -----------------------------------------------
-- Map / Set operations
-- -----------------------------------------------
-- Looks up a character's signature weapon by name
-- Map.lookup :: Ord k => k -> Map k v -> Maybe v
getWeapon :: String -> String
getWeapon name =
-- fromMaybe provides a default value in case lookup returns Nothing
fromMaybe "Unknown" (Map.lookup name weaponMap)
-- Returns all members from both organizations (union)
-- Set.union :: Ord a => Set a -> Set a -> Set a
allMembers :: Set.Set String
allMembers = Set.union tojokaiMembers omikaiMembers
-- Returns characters not belonging to either organization (difference)
-- Set.difference :: Ord a => Set a -> Set a -> Set a
independents :: [String] -> Set.Set String
independents names =
Set.difference (Set.fromList names) allMembers
-- -----------------------------------------------
-- Entry point
-- -----------------------------------------------
main :: IO ()
main = do
putStrLn "===== Yakuza Database ====="
putStrLn ""
-- Display each character's weapon using Map
putStrLn "--- Signature Weapons (Map.lookup) ---"
let characters = Map.keys weaponMap
mapM_ (\name -> putStrLn $ " " ++ name ++ ": " ++ getWeapon name) characters
putStrLn ""
-- Display all members from both organizations using Set union
putStrLn "--- All Members from Both Organizations (Set.union) ---"
-- Set.toAscList :: Set a -> [a] converts the set to an ascending list
putStrLn $ " " ++ intercalate ", " (Set.toAscList allMembers)
putStrLn ""
-- Find characters not belonging to either organization
putStrLn "--- Independent Characters (Set.difference) ---"
let allChars = Map.keys weaponMap
let loners = independents allChars
putStrLn $ " " ++ intercalate ", " (Set.toAscList loners)
putStrLn ""
-- Check the size of the Map
-- Map.size :: Map k v -> Int
putStrLn $ "Registered characters: " ++ show (Map.size weaponMap)
putStrLn "\"Life is a battle. Don't run from it.\""
ghc rgg_import_qualified.hs -o rgg_import_qualified && ./rgg_import_qualified [1 of 1] Compiling Main ( rgg_import_qualified.hs, rgg_import_qualified.o ) Linking rgg_import_qualified ... ===== Yakuza Database ===== --- Signature Weapons (Map.lookup) --- Kazuma Kiryu: Bare hands (Brawler style) Goro Majima: Knife / bat (Mad Dog style) Shun Akiyama: Kicks (Like a Dragon style) Taiga Saejima: Kusarigama (Slug style) Tatsuo Shinada: Handgun (Gunman style) Makoto Date: Knife (Capoeira style) --- All Members from Both Organizations (Set.union) --- Akira Nishikiyama, Daigo Dojima, Goro Majima, Kazuma Kiryu, Makoto Date, Omi Ren, Taiga Saejima, Tatsuo Shinada --- Independent Characters (Set.difference) --- Shun Akiyama Registered characters: 6 "Life is a battle. Don't run from it."
rgg_module_hiding.hs
-- rgg_module_hiding.hs — sample showing import control with hiding and as
-- Uses the battle system from Like a Dragon as sample data
-- Demonstrates coexisting a custom function with a same-named Prelude function
--
-- Compile and run:
-- ghc rgg_module_hiding.hs -o rgg_module_hiding && ./rgg_module_hiding
module Main where
-- Import sort and intercalate from Data.List, but hide lookup
-- lookup exists in Prelude too, so we exclude it here to avoid ambiguity
import Data.List (sort, intercalate)
-- Import Data.Map.Strict qualified so its lookup does not clash with Prelude's lookup
import qualified Data.Map.Strict as Map
-- Import System.IO under the short alias IO (using as for renaming)
import qualified System.IO as IO
-- -----------------------------------------------
-- Data definitions
-- -----------------------------------------------
-- Type representing a Heat Action move
data HeatAction = HeatAction
{ haName :: String -- move name
, haDamage :: Int -- damage value
, haStyle :: String -- style used
} deriving (Show)
-- Manage move data in a Map
heatActions :: Map.Map String HeatAction
heatActions = Map.fromList
[ ("Tiger Drop",
HeatAction "Tiger Drop" 9800 "Brawler style")
, ("Final Slugger",
HeatAction "Final Slugger" 8500 "Like a Dragon style")
, ("Wall Break",
HeatAction "Wall Break" 7200 "Rush style")
, ("Essence of Demon",
HeatAction "Essence of Demon" 11000 "Dragon of Dojima style")
, ("Raging Bind",
HeatAction "Raging Bind" 9300 "Slug style")
]
-- -----------------------------------------------
-- Function definitions
-- -----------------------------------------------
-- Returns the top N moves sorted by damage value
-- sort is Data.List's sort (making it explicit that this is not from Prelude)
topHeatActions :: Int -> [HeatAction]
topHeatActions n =
take n
-- Sort in descending order by damage value (pass a comparison function to sort)
$ reverse
$ sort
$ map snd (Map.toList heatActions) -- extract HeatAction list from Map
-- Returns a display string for a HeatAction
showHA :: HeatAction -> String
showHA ha =
haName ha ++ " [" ++ haStyle ha ++ "] Damage: " ++ show (haDamage ha)
-- -----------------------------------------------
-- Ord instance (required for sort)
-- -----------------------------------------------
instance Eq HeatAction where
a == b = haDamage a == haDamage b
instance Ord HeatAction where
compare a b = compare (haDamage a) (haDamage b)
-- -----------------------------------------------
-- Entry point
-- -----------------------------------------------
main :: IO ()
main = do
-- IO.hSetBuffering is the same as System.IO.hSetBuffering (using the alias)
IO.hSetBuffering IO.stdout IO.LineBuffering
putStrLn "===== Heat Action Ranking ====="
putStrLn ""
-- Search for a specific move with Map.lookup (distinct from Prelude's lookup)
putStrLn "--- Search move with Map.lookup ---"
case Map.lookup "Essence of Demon" heatActions of
Nothing -> putStrLn " Move not found"
Just ha -> putStrLn $ " " ++ showHA ha
putStrLn ""
-- Display the top 3 moves by damage
putStrLn "--- Damage TOP 3 ---"
let top3 = topHeatActions 3
mapM_ (\(i, ha) -> putStrLn $ " " ++ show i ++ ": " ++ showHA ha)
(zip [1..] top3)
putStrLn ""
-- Display all move names in ascending order (using Data.List.sort)
putStrLn "--- All move names (ascending) ---"
let names = sort (Map.keys heatActions)
putStrLn $ " " ++ intercalate " / " names
putStrLn ""
putStrLn "\"This is my Heat Action!\""
ghc rgg_module_hiding.hs -o rgg_module_hiding && ./rgg_module_hiding [1 of 1] Compiling Main ( rgg_module_hiding.hs, rgg_module_hiding.o ) Linking rgg_module_hiding ... ===== Heat Action Ranking ===== --- Search move with Map.lookup --- Essence of Demon [Dragon of Dojima style] Damage: 11000 --- Damage TOP 3 --- 1: Essence of Demon [Dragon of Dojima style] Damage: 11000 2: Tiger Drop [Brawler style] Damage: 9800 3: Raging Bind [Slug style] Damage: 9300 --- All move names (ascending) --- Essence of Demon / Final Slugger / Raging Bind / Tiger Drop / Wall Break "This is my Heat Action!"
Summary
In Haskell's module system, you declare a module name and its exported symbols with module ModName (export list) where. A common pattern is to hide constructors in the export list and expose only a smart constructor function that performs validation. In import statements, the convention is to use qualified ... as alias to enforce name-qualified access and prevent collisions — especially for Data.Map.Strict and Data.Set. You can also use hiding (symbol) to exclude a specific name from the import, which is useful when you want a custom function to coexist with a same-named Prelude function. For file I/O across multiple module files, see File read/write. For more on Data.Map.Strict, see Defining data types.
If you find any errors or copyright issues, please contact us.