Modules and Project Structure

15.1 Modules and Imports

Smart constructors plus hidden data constructors enforce invariants (only valid values can be created).

-- src/Geometry/Shapes.hs
module Geometry.Shapes
  ( Shape (..)          -- export the type and all constructors
  , Point               -- export the type only (abstract: constructors hidden)
  , mkPoint             -- smart constructor
  , area
  ) where

data Shape = Circle Double | Square Double deriving Show
data Point = Point Double Double deriving Show

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

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

helper :: Int          -- not exported: private to the module
helper = 42
import Geometry.Shapes                     -- everything exported
import Geometry.Shapes (Shape (..), area)  -- specific names
import qualified Data.Map.Strict as Map    -- qualified: Map.insert, Map.lookup
import Data.Map.Strict (Map)               -- import the type unqualified alongside
import Data.List hiding (insert)           -- everything except insert
import Prelude hiding (lookup)

15.2 A cabal Project

my-project/
├── my-project.cabal
├── cabal.project            -- optional: multi-package config
├── app/Main.hs              -- executable
├── src/MyLib.hs             -- library
├── test/Spec.hs             -- test suite
└── bench/Bench.hs           -- benchmarks
cabal-version:      3.0
name:               my-project
version:            0.1.0.0
build-type:         Simple

common shared
    default-language: GHC2021
    ghc-options:      -Wall -Wcompat -Wincomplete-uni-patterns -Wredundant-constraints
    build-depends:    base >=4.17 && <5

library
    import:           shared
    hs-source-dirs:   src
    exposed-modules:  MyLib
    build-depends:    containers, text

executable my-project
    import:           shared
    hs-source-dirs:   app
    main-is:          Main.hs
    build-depends:    my-project

test-suite spec
    import:           shared
    type:             exitcode-stdio-1.0
    hs-source-dirs:   test
    main-is:          Spec.hs
    build-depends:    my-project, hspec, QuickCheck
cabal build
cabal run my-project -- --some-arg
cabal test
cabal repl                 # GHCi with the project loaded
cabal update               # refresh the Hackage index
cabal freeze               # pin versions (cabal.project.freeze)
cabal haddock              # generate documentation