पाठ 16 / 25
Modules, Packages, Text and Containers
Organise code into modules and use Text, Map and Set efficiently.
Structuring programs
Each file is a module (module Shop.Pricing (withGst, Paise (..)) where), whose export list controls the public API; hiding constructors behind smart constructors enforces invariants. Imports can be qualified to avoid name clashes, the standard practice for containers: import qualified Data.Map.Strict as Map. Key libraries beyond base: text (Data.Text, efficient Unicode strings; enable OverloadedStrings so string literals can be Text), bytestring for binary data, containers (Data.Map, Data.Set, Data.Sequence, Data.IntMap), unordered-containers (hash maps), vector (arrays), time, aeson (JSON) and mtl/transformers. Use String only for simple scripts; Text is much faster and more memory-efficient. Declare dependencies with version bounds in the .cabal file (containers >= 0.6 && < 0.8) so builds remain reproducible, and add a cabal.project.freeze file or use Stack snapshots for applications. Organise larger applications into a library with most code and a thin executable, which also makes testing easier.
A Cabal package
A library holds most modules; executables and tests depend on it.
Summarising a CSV with Text and Map
Qualified imports, OverloadedStrings and fromListWith.
{-# LANGUAGE OverloadedStrings #-}
module Main (main) where
import qualified Data.Map.Strict as Map
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import Text.Read (readMaybe)
parseRow :: Text -> Maybe (Text, Int)
parseRow line = case T.splitOn "," line of
[_orderId, city, amount] -> (,) (T.strip city) <$> readMaybe (T.unpack (T.strip amount))
_ -> Nothing
revenueByCity :: [Text] -> Map.Map Text Int
revenueByCity rows = Map.fromListWith (+) [r | Just r <- map parseRow rows]
main :: IO ()
main = do
contents <- TIO.readFile "orders.csv"
let rows = drop 1 (T.lines contents) -- skip the header
totals = revenueByCity rows
mapM_ (\(c, t) -> TIO.putStrLn (c <> ": " <> T.pack (show t))) (Map.toList totals)
TIO.putStrLn ("cities: " <> T.intercalate ", " (Map.keys totals))Qualify container imports
Data.Map exports filter, map, lookup and null, which clash with the Prelude. Importing it qualified (Map.lookup) is the community convention and keeps code readable.
त्वरित जाँच: Why prefer Data.Text over String in real applications?
- String does not support Unicode
- Text is required by GHC
- Text is a compact, efficient representation, while String is a linked list of characters
- String cannot be printed
Answer
Text is a compact, efficient representation, while String is a linked list of characters — String is [Char], which is slow and memory-hungry for large text.