# Modules, Packages, Text and Containers — Haskell

Source: https://www.geekswithgeeks.com/en/haskell/r-modules

> 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.

![A package box containing a large library block with module tiles, connected to a small executable block and a test block.](assets/figures/haskell/section-6-map.svg) — Figure 6.1 — Library, executable and test suite.

## Summarising a CSV with Text and Map

Qualified imports, OverloadedStrings and fromListWith.

```haskell
{-# 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.

**Quiz:** Why prefer Data.Text over String in real applications?

- [ ] String does not support Unicode
- [ ] Text is required by GHC
- [x] 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.
