packages feed

clod-0.1.0: HASKELL_PATTERNS.md

# Haskell Patterns and Best Practices

This document contains common Haskell patterns and best practices for efficient and idiomatic Haskell development. These patterns are particularly useful for human-AI collaboration, where clear communication and code understanding are essential.

## Functional Programming Patterns

### Pure Functions and Side Effects

- **Pure Functions**: Prefer pure functions over impure operations whenever possible. Pure functions are easier to reason about, test, and compose.
  ```haskell
  -- Pure function
  calculateTotal :: [Item] -> Price
  calculateTotal items = sum (map itemPrice items)
  
  -- Instead of this impure approach
  calculateTotal :: [Item] -> IO Price
  calculateTotal items = do
    forM items $ \item -> do
      logItemProcess item  -- Side effect!
      return (itemPrice item)
    ...
  ```

- **Effect Localization**: When side effects are necessary, localize them to the boundaries of your application. Keep your core logic pure.
  ```haskell
  -- Good: Centralized effects at the edge
  main :: IO ()
  main = do
    input <- readInput        -- IO at the boundary
    let result = process input -- Pure core logic
    writeOutput result        -- IO at the boundary
  ```

- **Resource Management**: Use higher-order functions like `bracket`, `withFile`, or `ResourceT` to ensure resources are properly acquired and released.
  ```haskell
  -- Ensures file is closed even if an exception occurs
  withConfigFile :: FilePath -> (Handle -> IO a) -> IO a
  withConfigFile path action = bracket 
    (openFile path ReadMode)  -- acquire
    hClose                    -- release
    action                    -- use
  ```

- **Error Handling**: Use types to represent errors rather than exceptions. Wrap impure code with `try`/`catch` and convert exceptions to domain-specific error types.
  ```haskell
  -- Domain-specific error type
  data AppError = FileNotFound FilePath | ParseError String | NetworkError
  
  -- Convert IO exceptions to domain errors
  readConfig :: FilePath -> IO (Either AppError Config)
  readConfig path = do
    result <- try (readFile path)
    case result of
      Left e -> return $ Left $ FileNotFound path
      Right content -> 
        case parseConfig content of
          Nothing -> return $ Left $ ParseError "Invalid config"
          Just config -> return $ Right config
  ```

### Type-Driven Development

- **Newtype Wrappers**: Use `newtype` to create distinct types for values that might otherwise be confused.
  ```haskell
  -- Without newtypes
  processUser :: String -> Int -> String -> IO ()  -- What do these mean?
  
  -- With newtypes
  newtype UserId = UserId String
  newtype Age = Age Int
  newtype Email = Email String
  
  processUser :: UserId -> Age -> Email -> IO ()  -- Much clearer!
  ```
  
- **Smart Constructors**: Use smart constructors to enforce invariants and hide implementation details.
  ```haskell
  module Email (Email, mkEmail, emailToText) where
  
  newtype Email = Email { _unEmail :: Text } -- Private constructor
  
  -- Smart constructor with validation
  mkEmail :: Text -> Either String Email
  mkEmail txt
    | "@" `isInfixOf` txt = Right (Email txt)
    | otherwise = Left "Email must contain @"
    
  -- Accessor function
  emailToText :: Email -> Text
  emailToText (Email t) = t
  ```

- **Phantom Types**: Use phantom types to encode additional information in the type.
  ```haskell
  -- Phantom type for file access permissions
  data Permission = Read | Write | ReadWrite
  
  newtype File (p :: Permission) = File FilePath
  
  -- Operations that respect permissions
  readFile :: File p -> IO String
  readFile (File path) = -- ...
  
  writeFile :: File 'Write -> String -> IO ()
  writeFile (File path) content = -- ...
  
  withWritableFile :: File 'Read -> (File 'Write -> IO a) -> IO a
  ```

## Module Organization and API Design

### Clean Module Structure

- **Hierarchical Module Structure**: Organize modules hierarchically (e.g., `App.Module.Submodule`) to make the codebase easier to navigate.
  ```
  MyApp/
    Core.hs         -- Core functionality
    Core/           -- Implementation details
      Types.hs
      Operations.hs
    Database.hs     -- Database facade
    Database/       -- Database implementations
      MySQL.hs
      PostgreSQL.hs
  ```

- **Facade Modules**: Create facade modules that re-export functionality from specialized modules. This allows implementation changes without affecting users of your API.
  ```haskell
  -- Database.hs (facade module)
  module Database 
    ( Connection
    , QueryResult
    , connect
    , disconnect
    , query
    ) where
  
  import Database.Internal.Types
  import Database.Internal.Connection
  import Database.Internal.Query
  ```

- **Re-export Pattern**: Use selective re-exports to create a clean, focused API while hiding implementation details.
  ```haskell
  -- Clear separation between public API and internal details
  module MyLib
    ( -- * Core Types
      Widget(..)
    , WidgetId
      -- * Widget Creation
    , createWidget
    , defaultWidget
      -- * Widget Operations
    , updateWidget
    , renderWidget
    ) where
    
  import MyLib.Internal.Types
  import MyLib.Internal.Operations
  ```

### API Design for Human Understanding

- **Module Documentation**: Begin each module with a comprehensive Haddock comment that explains its purpose, main concepts, and usage examples.
  ```haskell
  {-|
  Module      : Data.Parser
  Description : Parser combinators for structured data
  
  This module provides parser combinators for processing structured data.
  It supports:
  
  * Basic parsers for primitive types
  * Combinators for sequence and choice
  * Error reporting with context
  
  Example usage:
  
  @
  parseJSON :: String -> Either ParseError JSONValue
  parseJSON input = runParser jsonValue input
  @
  -}
  ```

- **Function Grouping**: Group related functions together and use Haddock section headers to organize the module documentation.
  ```haskell
  -- | Core data types
  
  -- | @Widget@ represents a UI element
  data Widget = ...
  
  -- | Operations on widgets
  
  -- | Create a new widget
  createWidget :: WidgetConfig -> Widget
  
  -- | Update widget properties
  updateWidget :: Widget -> WidgetUpdate -> Widget
  ```

- **Type Signatures as Documentation**: Write expressive type signatures that communicate intent. Use meaningful type and function names.
  ```haskell
  -- Less clear
  process :: [a] -> [(a, b)] -> [b] -> [c]
  
  -- More clear
  reconcileInventory :: [Product] -> [(Product, Quantity)] -> [Adjustment] -> [StockChange]
  ```

## Advanced Type System Features for Safety and Clarity

### Type Classes and Constraints

- **Type Class Constraints**: Use type class constraints to make requirements explicit and enable polymorphism.
  ```haskell
  -- Generic function that works with any monoid
  combineAll :: Monoid a => [a] -> a
  combineAll = foldr (<>) mempty
  
  -- Use with different monoid instances
  sumAll :: [Int] -> Int
  sumAll = getSum . combineAll . map Sum
  
  concatAll :: [[a]] -> [a]
  concatAll = combineAll  -- Works because lists are monoids
  ```

- **Constraint Type Aliases**: Use ConstraintKinds to create aliases for common constraint combinations.
  ```haskell
  {-# LANGUAGE ConstraintKinds #-}
  
  -- Alias for common constraint combination
  type Serializable a = (ToJSON a, FromJSON a, Show a, Eq a)
  
  -- Simplified type signature
  storeEntity :: Serializable a => Connection -> a -> IO ()
  storeEntity conn entity = do
    let json = toJSON entity
    -- Store the entity...
  ```

- **Multi-Parameter Type Classes**: Use MPTCs with functional dependencies or associated types to express relationships between types.
  ```haskell
  {-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}
  
  -- Repository pattern with type safety
  class Repository r e | r -> e where
    save :: e -> r -> IO r
    findById :: Id e -> r -> IO (Maybe e)
    delete :: Id e -> r -> IO r
  
  -- Implementation for specific entity type
  instance Repository UserRepo User where
    save user repo = -- Implementation
    findById userId repo = -- Implementation
    delete userId repo = -- Implementation
  ```

### Advanced Type Safety Features

- **GADTs**: Use Generalized Algebraic Data Types to enforce invariants at the type level.
  ```haskell
  {-# LANGUAGE GADTs, DataKinds #-}
  
  -- Status for a request
  data Status = Pending | Approved | Rejected
  
  -- GADT that ensures status-specific operations
  data Request s where
    PendingRequest :: RequestId -> UserData -> Request 'Pending
    ApprovedRequest :: RequestId -> UserData -> ApproverInfo -> Request 'Approved
    RejectedRequest :: RequestId -> UserData -> RejectReason -> Request 'Rejected
  
  -- Type-safe operations
  approve :: ApproverInfo -> Request 'Pending -> Request 'Approved
  approve approver (PendingRequest id userData) = 
    ApprovedRequest id userData approver
    
  -- Won't compile:
  -- approve :: ApproverInfo -> Request 'Rejected -> Request 'Approved
  ```

- **Phantom Types**: Use phantom types to add type-level tags without runtime overhead.
  ```haskell
  {-# LANGUAGE RankNTypes, KindSignatures #-}
  
  -- Phantom type for validation state
  data Validated
  data Unvalidated
  
  -- Email with validation state in the type
  newtype Email (s :: Type) = Email Text
  
  -- Smart constructor that returns validated email
  validateEmail :: Email Unvalidated -> Either String (Email Validated)
  validateEmail (Email txt)
    | "@" `isInfixOf` txt = Right (Email txt)
    | otherwise = Left "Invalid email address"
  
  -- Only validated emails can be sent
  sendEmail :: Email Validated -> Message -> IO ()
  sendEmail (Email addr) msg = -- Implementation
  ```

- **Type Families**: Use type families to compute types based on other types.
  ```haskell
  {-# LANGUAGE TypeFamilies #-}
  
  -- Type family for result of an operation based on input type
  type family ResultOf a where
    ResultOf String = Int
    ResultOf Int = Double
    ResultOf (Maybe a) = Maybe (ResultOf a)
  
  -- Function with type that depends on input
  process :: a -> ResultOf a
  process = -- Implementation
  ```

### Expressive Deriving Mechanisms

- **DerivingVia**: Use DerivingVia for zero-boilerplate reuse of implementations.
  ```haskell
  {-# LANGUAGE DerivingVia, DerivingStrategies #-}
  
  -- Newtype wrapper for JSON serialization customization
  newtype UserName = UserName Text
    deriving stock (Show, Eq)
    deriving newtype (Semigroup, Monoid)
    deriving (ToJSON, FromJSON) via Text
  
  -- Composition of deriving strategies
  newtype UserId = UserId Int
    deriving stock (Show, Eq, Ord)
    deriving (ToJSON, FromJSON) via (Tagged "id" Int)
  ```

- **DerivingStrategies**: Be explicit about deriving mechanisms for clarity.
  ```haskell
  -- Explicitly specify deriving strategy
  data User = User
    { userId :: UserId
    , userName :: UserName
    , userEmail :: Email Validated
    }
    deriving stock (Show, Eq)
    deriving anyclass (ToJSON, FromJSON)
    deriving (Semigroup) via (GenericSemigroup User)
  ```

## Composition Patterns for Readability

### Kleisli Composition for Monadic Pipelines

Kleisli composition elegantly chains monadic operations, improving readability for complex workflows.

```haskell
import Control.Arrow ((>>>), (<<<), Kleisli(..), runKleisli)

-- Monadic functions (error handling, IO, etc.)
validateInput :: Input -> Either Error ValidInput
processData :: ValidInput -> Either Error ProcessedData
generateReport :: ProcessedData -> Either Error Report

-- Create Kleisli arrows for these functions
validateK = Kleisli validateInput
processK = Kleisli processData
reportK = Kleisli generateReport

-- Compose them into a clean pipeline
pipeline :: Kleisli (Either Error) Input Report
pipeline = validateK >>> processK >>> reportK

-- Run the pipeline
processBatch :: [Input] -> [Either Error Report]
processBatch inputs = map (runKleisli pipeline) inputs

-- Compare to nested approach:
processManually :: Input -> Either Error Report
processManually input = do
  validInput <- validateInput input
  processed <- processData validInput  
  generateReport processed  -- Less clear for complex pipelines
```

### Function Composition for Pure Pipelines

When working with pure functions, standard function composition offers clarity.

```haskell
-- Pure data transformations
normalize :: RawData -> NormalizedData
analyze :: NormalizedData -> AnalysisResult 
format :: AnalysisResult -> FormattedOutput

-- Direct composition
pipeline :: RawData -> FormattedOutput
pipeline = format . analyze . normalize

-- Data flows from right to left, which can be counterintuitive

-- Alternative: Forward composition with Data.Function
import Data.Function ((&))

pipeline' :: RawData -> FormattedOutput
pipeline' data = data 
  & normalize  -- First step
  & analyze    -- Second step
  & format     -- Final step
```

## Error Handling Patterns

### Typed Errors with Monad Transformers

Use explicit error types and monad transformers for comprehensive error handling.

```haskell
-- Define a clear error hierarchy
data AppError
  = FileSystemError FilePath IOError
  | ConfigError String
  | NetworkError ConnectionInfo String
  | ValidationError [String]
  | PermissionError UserId Resource
  deriving (Show, Eq)

-- Application monad with built-in error handling
type AppM a = ReaderT AppConfig (ExceptT AppError IO) a

-- Helper for running the monad stack
runAppM :: AppConfig -> AppM a -> IO (Either AppError a)
runAppM config action = runExceptT (runReaderT action config)

-- Convert IO exceptions to domain-specific errors
safeFileOperation :: FilePath -> AppM ByteString
safeFileOperation path = do
  result <- liftIO $ try $ readFile path
  case result of
    Left e -> throwError $ FileSystemError path e
    Right content -> return content
```

### Railway-Oriented Programming with Either

Use Either for explicit error handling in pure code without the complexity of monad transformers.

```haskell
-- Define error types
data ValidationError = 
    MissingField String 
  | InvalidFormat String String
  | OutOfRange String Int Int Int
  deriving (Show, Eq)

-- Input validation function returning Either
validateInput :: UserInput -> Either ValidationError ValidatedInput
validateInput input = do
  name <- validateName (inputName input)
  age <- validateAge (inputAge input)
  email <- validateEmail (inputEmail input)
  pure ValidatedInput
    { validName = name
    , validAge = age
    , validEmail = email
    }

-- Simple validation function
validateAge :: Maybe Int -> Either ValidationError Int
validateAge Nothing = Left (MissingField "age")
validateAge (Just age)
  | age < 18 = Left (OutOfRange "age" age 18 120)
  | age > 120 = Left (OutOfRange "age" age 18 120)
  | otherwise = Right age
```

### Smart Constructors for Validation

Use smart constructors to ensure valid data at the type level.

```haskell
-- Define a newtype with private constructor
module Email (Email, mkEmail, emailToText) where

newtype Email = Email { _unEmail :: Text } -- Private constructor

-- Smart constructor returns Either for explicit error handling
mkEmail :: Text -> Either EmailError Email
mkEmail txt
  | T.null txt = Left EmailEmpty
  | not ("@" `T.isInfixOf` txt) = Left EmailMissingAt
  | not (hasDomainPart txt) = Left EmailInvalidDomain
  | otherwise = Right (Email $ T.toLower txt)
  
-- Safe access functions  
emailToText :: Email -> Text
emailToText (Email t) = t

-- Because Email constructor is not exported, all Email values in your
-- program are guaranteed to be valid
```

### Nested Error Handling with MonadError

Use MonadError for cleaner nested error handling.

```haskell
import Control.Monad.Except

-- Function signatures are cleaner with constraints instead of concrete types
processTransaction :: (MonadError AppError m, MonadIO m) => Transaction -> m Receipt
processTransaction tx = do
  -- validate will throw an error on invalid transaction
  validTx <- validate tx
  
  -- attempt to process, may throw network error
  result <- processPayment validTx `catchError` \e -> 
    -- Add context to the error
    throwError $ PaymentError (transactionId tx) e
    
  -- generate receipt if successful
  generateReceipt tx result
```

## Resource Management and Safety Patterns

### Bracket Pattern for Resource Safety

Use the bracket pattern to ensure resources are properly acquired and released even when exceptions occur.

```haskell
import Control.Exception (bracket)
import System.IO

-- Generic template for resource handling
withResource :: IO a         -- acquire resource
             -> (a -> IO ()) -- release resource
             -> (a -> IO b)  -- use resource
             -> IO b
withResource acquire release use = bracket acquire release use

-- Example: File handling with automatic cleanup
withFile' :: FilePath -> IOMode -> (Handle -> IO a) -> IO a
withFile' path mode = bracket 
  (openFile path mode)  -- acquire
  hClose                -- release
  
-- Example: Database connection with transaction support
withTransaction :: Connection -> (Connection -> IO a) -> IO a
withTransaction conn action = bracket
  (do beginTransaction conn; return conn)  -- start transaction
  (\c -> do rollback c; return ())         -- rollback on exception
  (\c -> do result <- action c             -- run action
            commit c                       -- commit on success
            return result)
```

### Resource Management with ResourceT

For complex resource management scenarios, ResourceT from the resourcet package provides more flexibility.

```haskell
import Control.Monad.Trans.Resource

-- Create a computation that allocates and automatically frees resources
complexOperation :: ResourceT IO Result
complexOperation = do
  -- Register resources with cleanup actions
  (dbReleaseKey, dbConn) <- allocate 
    (connectDB "database.db")  -- acquire
    disconnectDB              -- release
  
  (fileReleaseKey, fileHandle) <- allocate
    (openFile "output.txt" WriteMode)  -- acquire
    hClose                            -- release
  
  -- Early release if needed
  release dbReleaseKey  
  
  -- Resources automatically released when ResourceT exits
  liftIO $ processWithResources dbConn fileHandle

-- Run the ResourceT computation
runResourceOperation :: IO Result
runResourceOperation = runResourceT complexOperation
```

### Capability-Based Security

Use the capability pattern to restrict access to sensitive operations.

```haskell
-- Define capability tokens
newtype FileReadCap = FileReadCap { allowedDirs :: [FilePath] } 
newtype FileWriteCap = FileWriteCap { writeDirs :: [FilePath] }

-- Operations require explicit capabilities
readFile' :: FileReadCap -> FilePath -> IO String
readFile' cap path = do
  -- Verify path is in allowed directories
  allowed <- isPathAllowed (allowedDirs cap) path
  if allowed 
    then readFile path
    else throwIO $ PermissionError $ "Cannot read: " ++ path
    
-- Restricted capability creation
rootCap :: IO FileReadCap
rootCap = do
  -- Check if user has admin rights
  isAdmin <- checkAdminRights
  if isAdmin
    then return $ FileReadCap ["/"]  -- Full access
    else return $ FileReadCap ["/home/user"]  -- Limited access
```

### Resource Pools for Performance

Use resource pooling for expensive resources like database connections.

```haskell
import Data.Pool

-- Create a connection pool
initConnectionPool :: Config -> IO (Pool Connection)
initConnectionPool config = createPool
  (connect (dbHost config) (dbUser config))  -- create resource
  close                                     -- destroy resource
  1           -- stripes (for concurrency)
  60          -- unused resource timeout (seconds)
  10          -- maximum resources per stripe

-- Use a resource from the pool
withConnection :: Pool Connection -> (Connection -> IO a) -> IO a
withConnection pool action = withResource pool action
```

## Testing Patterns for Robust Code

### Property-Based Testing

Use property-based testing to identify edge cases that unit tests might miss.

```haskell
import Test.QuickCheck
import Data.List (sort)

-- Define properties that should hold for any input
prop_reverseInvolutive :: [Int] -> Bool
prop_reverseInvolutive xs = reverse (reverse xs) == xs

prop_sortIdempotent :: [Int] -> Bool
prop_sortIdempotent xs = sort (sort xs) == sort xs

-- Test invariants that your functions should maintain
prop_parseRenderRoundtrip :: Config -> Property
prop_parseRenderRoundtrip config = 
  parseConfig (renderConfig config) === Just config

-- Run the tests
main :: IO ()
main = do
  quickCheck prop_reverseInvolutive
  quickCheck prop_sortIdempotent
  quickCheck prop_parseRenderRoundtrip
```

### Isolated Test Environments

Create isolated, reproducible test environments for reliable testing.

```haskell
import System.IO.Temp (withSystemTempDirectory)
import System.FilePath ((</>))

-- Helper for setting up a test environment
withTestEnvironment :: (FilePath -> IO ()) -> IO ()
withTestEnvironment runTest = 
  withSystemTempDirectory "test-dir" $ \tmpDir -> do
    -- Create test files and directories
    createDirectoryIfMissing True (tmpDir </> "src")
    createDirectoryIfMissing True (tmpDir </> "config")
    writeFile (tmpDir </> "src" </> "test.file") "test content"
    writeFile (tmpDir </> "config" </> "settings.json") "{\"mode\":\"test\"}"
    
    -- Run the test with the prepared environment
    runTest tmpDir

-- Use it in hspec tests
it "processes files correctly" $
  withTestEnvironment $ \tmpDir -> do
    -- Configure app to use the temp directory
    let config = defaultConfig { rootDir = tmpDir }
    
    -- Run the application
    result <- runApp config
    
    -- Make assertions
    result `shouldBe` Success
```

### Golden Tests for Output Verification

Use golden testing to verify your outputs match expected templates.

```haskell
import Test.Tasty.Golden (goldenVsString)
import qualified Data.ByteString.Lazy as BL

-- Test that generated output matches a "golden" file
goldenOutputTest :: TestTree
goldenOutputTest = goldenVsString
  "report generation"                         -- test name
  "test/golden/expected_report.json"          -- golden file path
  (BL.fromStrict <$> generateReport testData) -- actual output

-- For complex outputs like HTML, use a difference tool
htmlGoldenTest :: TestTree
htmlGoldenTest = goldenVsFileDiff
  "page rendering"              -- test name
  diffCommand                   -- diff command to use
  "test/golden/expected.html"   -- golden file
  "test/output/actual.html"     -- actual output file
  (renderPage testData)         -- action to generate actual output
  where
    diffCommand ref new = ["diff", "-u", ref, new]
```

### Table-Driven Testing

Use table-driven testing for testing multiple related cases concisely.

```haskell
import Test.Hspec

-- Define test cases as a list of input-output pairs
testCases :: [(String, Int)]
testCases = 
  [ ("123", 123)
  , ("0", 0)
  , ("00123", 123)
  , ("+123", 123)
  , ("-123", -123)
  ]

-- Test all cases using the same pattern
spec :: Spec
spec = describe "parseNumber" $ do
  forM_ testCases $ \(input, expected) ->
    it ("parses " ++ show input ++ " correctly") $ do
      parseNumber input `shouldBe` Right expected

-- For more complex test cases, use records
data ValidationTestCase = ValidationTestCase
  { testName :: String
  , testInput :: UserInput
  , expectedResult :: Either ValidationError ValidatedInput
  }

validationTests :: [ValidationTestCase]
validationTests = 
  [ ValidationTestCase 
      "valid input" 
      (UserInput "John" (Just 30) "john@example.com")
      (Right $ ValidatedInput "John" 30 "john@example.com")
  , ValidationTestCase
      "missing age"
      (UserInput "John" Nothing "john@example.com")
      (Left $ MissingField "age")
  ]
```

### Test Fixtures and Mocks

Use fixtures and mocks to test code that depends on external systems.

```haskell
-- Define a typecalss for database operations
class Monad m => MonadDB m where
  queryUsers :: m [User]
  saveUser :: User -> m ()
  
-- Production implementation
instance MonadDB IO where
  queryUsers = queryUsersFromDatabase
  saveUser = saveUserToDatabase
  
-- Test implementation 
instance MonadDB (State TestDB) where
  queryUsers = gets testDBUsers
  saveUser user = modify $ \db -> 
    db { testDBUsers = user : testDBUsers db }

-- Example test
it "creates user profile" $ do
  -- Set up initial DB state
  let initialDB = TestDB { testDBUsers = [] }
  
  -- Run operation with mock DB
  let (result, finalDB) = runState createUserProfile initialDB
  
  -- Assert the operation worked correctly
  length (testDBUsers finalDB) `shouldBe` 1
  
-- Function being tested uses constraint for testability
createUserProfile :: MonadDB m => m User
createUserProfile = do
  -- Implementation
```

## Debugging and Maintainability Patterns

### Function Decomposition for Testability

Break complex functions into smaller, testable parts that can be individually verified.

```haskell
-- Original monolithic function (hard to test and debug)
complexProcess :: Config -> [Input] -> IO [Output]
complexProcess config inputs = do
  -- 100+ lines of complex logic with multiple responsibilities
  -- and many potential failure points...

-- Refactored into testable components
validateInputs :: [Input] -> Either ValidationError [ValidInput]
validateInputs = traverse validateSingleInput

processValidInputs :: Config -> [ValidInput] -> IO [ProcessedData]
processValidInputs config = traverse (processOne config)  

generateOutputs :: [ProcessedData] -> [Output]
generateOutputs = map convertToOutput

-- Compose them back together with clear error handling
complexProcess :: Config -> [Input] -> IO (Either Error [Output])
complexProcess config inputs = do
  case validateInputs inputs of
    Left validationError -> 
      pure $ Left $ ValidationFailed validationError
      
    Right validInputs -> do
      processResult <- try $ processValidInputs config validInputs
      case processResult of
        Left ex -> 
          pure $ Left $ ProcessingFailed ex
          
        Right processed ->
          pure $ Right $ generateOutputs processed
```

### Layered Debugging Techniques

Use a combination of tracing approaches for effective debugging.

```haskell
import Debug.Trace (trace, traceShowId, traceM)
import qualified System.IO as IO

-- 1. Simple trace for basic logging (but doesn't clutter production code)
withTracing :: Bool -> a -> String -> a
withTracing True x msg = trace msg x
withTracing False x _ = x

-- 2. Effectful tracing for debugging monadic code
processItems :: [Item] -> IO [Result]
processItems = mapM $ \item -> do
  when debugMode $ traceM $ "Processing: " ++ show item
  result <- processItem item
  when debugMode $ traceM $ "Result: " ++ show result
  return result

-- 3. Conditional file logging when trace output is too large
logToFile :: String -> IO ()
logToFile msg = when debugMode $
  IO.appendFile "debug.log" (msg ++ "\n")
  
-- 4. TraceShowId for quick inspection of values in a pipeline
calculateResults :: [Input] -> [Output]
calculateResults = filter isValid 
                   >>> map preprocess 
                   >>> traceShowId  -- See the values mid-pipeline
                   >>> map calculate
                   >>> filter isSignificant

-- 5. Temporary function modification for deeper inspection
-- Original function
process :: Item -> Result
process = step1 >>> step2 >>> step3

-- Modified during debugging
process :: Item -> Result
process item = 
  let s1 = step1 item
      _ = trace ("After step1: " ++ show s1) ()
      s2 = step2 s1
      _ = trace ("After step2: " ++ show s2) ()
  in step3 s2
```

### Typed Holes for Guided Development

Use typed holes to let the compiler guide your implementation.

```haskell
-- Start with the function type signature
processTransaction :: UserId -> Transaction -> Either Error Receipt
processTransaction userId transaction = _implementThis

-- The compiler will tell you the expected type of _implementThis

-- Gradually fill in implementation guided by holes
processTransaction userId transaction = do
  user <- _getUser userId
  validated <- _validateTransaction user transaction
  _processPayment validated

-- Each hole tells you what you need to implement next
_getUser :: UserId -> Either Error User
_getUser = ...

_validateTransaction :: User -> Transaction -> Either Error ValidatedTransaction
_validateTransaction = ...
```

### Equational Reasoning and Step-by-Step Refactoring

Use equational reasoning to verify code transformations.

```haskell
-- Original code
sum (map square xs)

-- Step 1: Rewrite using function composition
sum . map square $ xs

-- Step 2: Introduce a specialized function
sumOfSquares = sum . map square

-- Verification:
-- sum (map square xs)
-- = sum . map square $ xs  -- By function composition
-- = sumOfSquares xs        -- By definition

-- More complex example:
processList xs = filter p1 (map f (filter p2 xs))

-- Transform step by step:
processList xs = (filter p1 . map f . filter p2) xs

-- Extract function:
processList = filter p1 . map f . filter p2

-- Each step preserves behavior but improves readability
```

## Performance Patterns and Optimizations

### Hash Function Selection and Implementation

Choose the appropriate hash function based on your actual requirements, not just defaults:

```haskell
-- Non-cryptographic fast hashing (xxHash) when you just need speed
import qualified Data.Digest.XXHash.FFI as XXH
import Data.Hashable (hash)

fastChecksum :: BS.ByteString -> Checksum
fastChecksum content =
  let -- Use XXH3 hash function (extremely fast)
      hashVal = hash (XXH.XXH3 content)
      -- Handle potential negative hash values
      absHash = abs hashVal
      -- Convert to hex string representation
      hexStr = showHex absHash ""
  in Checksum hexStr

-- Cryptographic hashing when security is required
import qualified Crypto.Hash.SHA256 as SHA256
import qualified Data.ByteString.Base16 as Base16

secureChecksum :: BS.ByteString -> Checksum
secureChecksum content =
  let hash = SHA256.hash content
      hexHash = Base16.encode hash
  in Checksum (show hexHash)
```

Performance considerations:
- XXH3 can be 5-15x faster than cryptographic hashes like SHA-256
- For content identification, non-cryptographic hashes are usually sufficient
- Be careful with hash values: they may be negative and need absolute value conversion
- Abstract hash implementation behind a consistent interface

### Efficient ByteString Usage

Use ByteString for efficient text and binary data handling.

```haskell
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BS8
import qualified Data.ByteString.Lazy as BL
import Data.Word (Word8)

-- Converting between ByteString and String (avoid in performance-critical code)
stringToBS :: String -> BS.ByteString
stringToBS = BS8.pack  -- For ASCII-only text

-- For general Unicode text, use Text instead of String/ByteString
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE

textToBS :: T.Text -> BS.ByteString
textToBS = TE.encodeUtf8

bsToText :: BS.ByteString -> Either String T.Text
bsToText bs = case TE.decodeUtf8' bs of
  Left err -> Left $ "UTF-8 decoding error: " ++ show err
  Right text -> Right text

-- Efficient file reading
readLargeFile :: FilePath -> IO BL.ByteString
readLargeFile = BL.readFile  -- Lazy reading for large files

-- Stream processing for large data
processLargeFile :: FilePath -> FilePath -> IO ()
processLargeFile input output = do
  contents <- BL.readFile input
  BL.writeFile output $ BL.filter (/= 0) contents
```

### Strict Fields for Memory Efficiency

Use strictness annotations to avoid space leaks.

```haskell
-- Without strictness, can cause space leaks
data Configuration = Configuration
  { configPort :: Int
  , configHost :: String
  , configTimeout :: Int
  }

-- With strictness annotations, more memory-efficient
data Configuration' = Configuration'
  { configPort' :: !Int  -- Strict field
  , configHost' :: !String
  , configTimeout' :: !Int
  }

-- Strictness and UNPACK for numeric data
data Point = Point
  { x :: {-# UNPACK #-} !Double  -- Unpacked strict field
  , y :: {-# UNPACK #-} !Double
  }

-- For record types with many fields
{-# LANGUAGE StrictData #-}  -- All fields strict by default
data User = User
  { userId :: Int
  , userName :: String
  , userEmail :: String
  }
```

### Fusion and Deforestation

Take advantage of list fusion to eliminate intermediate data structures.

```haskell
-- This will create an intermediate list
naiveProcess :: [Int] -> Int
naiveProcess xs = sum (filter even (map (*2) xs))

-- GHC can optimize this with list fusion
fusedProcess :: [Int] -> Int
fusedProcess = sum . filter even . map (*2)

-- Even better: use foldr to fuse everything into a single pass
singlePassProcess :: [Int] -> Int
singlePassProcess = foldr (\x acc -> if even (x*2) then acc + (x*2) else acc) 0

-- For more control, use a specialized streaming library
import qualified Streamly.Prelude as S

streamProcess :: [Int] -> IO Int
streamProcess xs = S.fold S.sum 
                 $ S.filter even 
                 $ S.map (*2) 
                 $ S.fromList xs
```

### Handling Numeric Edge Cases

Be aware of edge cases when working with numeric computations, especially hash functions:

```haskell
-- Example: Converting hash values to hex strings
import Data.Hashable (hash)
import Numeric (showHex)

-- INCORRECT: May fail on negative hash values
toHexStringUnsafe :: Hashable a => a -> String
toHexStringUnsafe x = showHex (hash x) ""  -- Fails if hash x is negative

-- CORRECT: Handle negative hash values
toHexString :: Hashable a => a -> String
toHexString x = 
  let hashVal = hash x
      -- Take absolute value to ensure showHex works correctly
      absHash = abs hashVal
  in showHex absHash ""

-- Alternative: Use Data.Bits for bit manipulation
import Data.Bits ((.&.))
import Data.Word (Word64)

-- This avoids negative numbers entirely by using Word64
toHexStringBits :: Hashable a => a -> String
toHexStringBits x =
  let hashVal = hash x
      -- Convert to Word64 by masking with all bits set
      -- This preserves the exact bit pattern
      wordVal = fromIntegral hashVal .&. (maxBound :: Word64)
  in showHex wordVal ""
```

Common numeric pitfalls to handle:
- Integer overflow/underflow
- Division by zero
- Negative values in functions expecting positives (like showHex)
- Floating point precision errors
- Range limitations in conversions between numeric types

### Lazy vs. Strict Evaluation Control

Explicitly control evaluation strategy for better performance.

```haskell
import Control.DeepSeq (NFData, force, ($!!))

-- Force full evaluation of a structure when needed
processStrictly :: (NFData a) => [a] -> [a]
processStrictly xs = force (map process xs)

-- Manually force evaluation to specific depth
data Tree a = Leaf a | Node (Tree a) (Tree a)

forceTree :: Tree a -> ()
forceTree (Leaf _) = ()
forceTree (Node l r) = forceTree l `seq` forceTree r `seq` ()

-- Use bang patterns for strict evaluation in function arguments
sumListStrict :: [Int] -> Int
sumListStrict !xs = sum xs  -- Force evaluation of xs

-- Use BangPatterns language extension for more control
{-# LANGUAGE BangPatterns #-}

foldlStrict :: (b -> a -> b) -> b -> [a] -> b
foldlStrict f !acc [] = acc
foldlStrict f !acc (x:xs) = foldlStrict f (f acc x) xs
```

## Build and Packaging Best Practices

### Cabal Configuration

Properly configure your Cabal file for reliable builds and distribution.

```haskell
-- Example cabal file structure with key sections
name:                my-project
version:             0.1.0
synopsis:            Short description of your project
description:         Longer, multi-line description
                     of your project's purpose and features.
license:             MIT
license-file:        LICENSE
author:              Your Name
maintainer:          your.email@example.com
category:            Development
build-type:          Custom  -- Use Custom for custom Setup.hs
cabal-version:       2.0

-- Set up custom build if needed
custom-setup
  setup-depends:     base >= 4.7 && < 5,
                     Cabal >= 2.0.0.2 && < 3.12,
                     directory,
                     filepath,
                     process

-- Documentation files that should be included in source distributions
extra-source-files:  README.md
                     CHANGELOG.md
                     examples/*.hs

-- Files to be installed with the package
data-files:          templates/*.txt
                     data/*.json

-- Create an autogenerated module for accessing data-files
auto-generated-modules: Paths_my_project

-- Library component
library
  -- Modules exposed to users of the library
  exposed-modules:     MyProject
                       MyProject.Core
                       MyProject.Types
  
  -- Internal modules not exposed to users
  other-modules:       MyProject.Internal.Util
                       Paths_my_project  -- Auto-generated module for data files
  
  -- Enable useful warnings
  ghc-options:         -Wall 
                       -Wcompat
                       -Wincomplete-record-updates
                       -Wincomplete-uni-patterns
                       -Wredundant-constraints
  
  -- Dependencies with version constraints
  build-depends:       base >= 4.7 && < 5,
                       aeson >= 1.4 && < 2.2,
                       text >= 1.2 && < 2.1,
                       containers >= 0.6 && < 0.7
```

### Version Range Best Practices

Specify appropriate version ranges for dependencies to avoid compatibility issues.

```
-- For most dependencies, specify both lower and upper bounds
build-depends: base >= 4.14 && < 5,
               text >= 1.2.4 && < 2.1,
               aeson >= 2.0 && < 2.2

-- Version range notation examples:
-- == 1.0.0         -- Exactly version 1.0.0
-- >= 1.0 && < 1.1  -- Greater than or equal to 1.0 and less than 1.1
-- ^>= 1.0.0        -- Compatible with version 1.0.0 (>=1.0.0 && <1.1)
-- ~> 1.0.0         -- Similar to ^>= but with more restriction
```

### Documentation Integration

Integrate documentation into your build process for better user experience.

```haskell
-- In Setup.hs
main = defaultMainWithHooks $ simpleUserHooks
  { postBuild = \args buildFlags pkg lbi -> do
      -- Run standard post-build first
      postBuild simpleUserHooks args buildFlags pkg lbi
      
      -- Generate documentation
      generateDocs pkg lbi
  }

-- Generate embedded documentation from Markdown
generateDocs :: PackageDescription -> LocalBuildInfo -> IO ()
generateDocs pkg lbi = do
  let docsDir = buildDir lbi </> "docs"
  createDirectoryIfMissing True docsDir
  
  -- Process each documentation file
  mapM_ (processDoc docsDir) 
    ["README.md", "TUTORIAL.md", "API.md"]
  
  -- Use Haddock to generate API documentation
  let ghcProg = programPath (haddockProgram (withPrograms lbi))
      pkgDb = packageDBFlags lbi
  runProcess ghcProg ["--haddock", ...] Nothing Nothing Nothing Nothing
```

### Making Use of Flags and Conditionals

Use flags and conditional compilation for flexible builds.

```haskell
-- Define build flags in the cabal file
flag strict
  description: Enable stricter GHC options
  default:     False
  manual:      True

flag optimize
  description: Build with optimization
  default:     True
  manual:      False

-- Use flags in the build configuration
library
  if flag(strict)
    ghc-options: -Wall -Werror
  else
    ghc-options: -Wall
    
  if flag(optimize)
    ghc-options: -O2
  else
    ghc-options: -O0
    
  -- Conditional dependencies
  if os(windows)
    build-depends: Win32
  else
    build-depends: unix

-- For system-specific code
  if os(darwin)
    cpp-options: -DMACOS
    other-modules: System.MacOS.Specific
  elif os(linux)
    cpp-options: -DLINUX
    other-modules: System.Linux.Specific
```

### Custom Setup Scripts

```haskell
-- Setup.hs for custom build steps
import Distribution.Simple
import Distribution.Simple.Setup
import Distribution.Simple.LocalBuildInfo
import Distribution.PackageDescription

main = defaultMainWithHooks simpleUserHooks
  { postBuild = \args flags pkg lbi -> do
      -- Run standard post-build first
      postBuild simpleUserHooks args flags pkg lbi
      -- Then run custom actions
      customAction args flags pkg lbi
  }

-- Custom build/install actions
customAction :: Args -> BuildFlags -> PackageDescription -> LocalBuildInfo -> IO ()
customAction _ _ pkg lbi = do
  -- Access package configuration
  let pkgName = unPackageName $ pkgName $ package pkg
      buildDir = buildDir lbi
  
  -- Execute custom build steps
  -- ...
```

## Version Number Management

```haskell
-- Access version from cabal file
import qualified Paths_<package> as Meta
import Data.Version (showVersion)

-- Display version
version :: String
version = showVersion Meta.version
```

## Best Practices

- Favor pure Haskell implementations over shell commands
- Document system dependencies explicitly
- Load resource files from standardized locations, not hardcoded paths
- Derive version information from the cabal file, not hardcoded
- Use type applications for parametric types
- Normalize paths for cross-platform compatibility
- Add explicit type annotations for complex expressions
- Integrate with standard system conventions (man pages, config directories)
- Use Cabal's installation system rather than custom scripts for deployable artifacts

## Documentation Integration

```haskell
-- In cabal file
data-files:
  doc/*.md,           -- Source files
  templates/*.txt     -- Templates

-- In Setup.hs
import Distribution.PackageDescription
import Distribution.Simple.LocalBuildInfo
import Distribution.Simple.BuildPaths (autogenModulesDir)
import Distribution.Simple.Utils (installOrdinaryFiles)

-- Generate documentation during build
postBuild _ _ pkg lbi = do
  let dataDirName = dataDir lbi
      docSrcDir = dataDirName </> "doc"
      docDestDir = buildDir lbi </> "doc"
  
  -- Generate docs from templates
  generateDocs docSrcDir docDestDir

-- Install documentation to standard locations
copyHook oldHook pkg_descr lbi hooks flags = do
  -- First do the standard copy
  oldHook pkg_descr lbi hooks flags
  
  -- Then copy documentation to proper locations
  let docDir = case os of
        "darwin" -> "/usr/local/share/doc/" ++ pkgName
        "linux"  -> "/usr/share/doc/" ++ pkgName
        _        -> error "Unsupported OS"
  
  installOrdinaryFiles verbosity docDir [(buildDir lbi </> "doc", "*.html")]
```

## Patterns for Human-AI Collaboration

These patterns are particularly effective when working with AI assistants on Haskell projects.

### Leveraging Types for Verification

Use type checking to verify collaboratively written code and catch misunderstandings early:

```haskell
-- Example 1: Use phantom types to enforce usage patterns
data Operation = Read | Write | ReadWrite

-- File access with permission enforcement
newtype File (p :: Operation) = File FilePath

readFile :: File p -> IO String
readFile (File path) = -- Implementation

writeFile :: File 'Write -> String -> IO ()
writeFile (File path) content = -- Implementation

-- Example 2: Use GADTs to restrict operations
data DatabaseAction a where
  Query :: SQL -> DatabaseAction [Row]
  Update :: SQL -> DatabaseAction Int
  Transaction :: [DatabaseAction a] -> DatabaseAction [a]

-- The return type enforces that queries return rows and updates return count
runAction :: DatabaseAction a -> Connection -> IO a

-- Example 3: Use newtypes to prevent confusion
newtype FileReadCap = FileReadCap { allowedDirs :: [FilePath] }
newtype FileWriteCap = FileWriteCap { writeDirs :: [FilePath] }

-- Won't compile if permissions are mixed up
readFile' :: FileReadCap -> FilePath -> IO String
writeFile' :: FileWriteCap -> FilePath -> String -> IO ()
```

By using these patterns, the AI can catch type errors during development rather than relying solely on runtime testing. The compiler becomes an active participant in the human-AI collaboration.

### Explicit Type Annotations

Add type annotations to make intentions clear and guide AI inference, even when GHC can infer types.

```haskell
-- Without annotation (ambiguous intention)
processData input = map process . filter isValid $ input

-- With annotation (clear intention)
processData :: [InputData] -> [OutputData]
processData input = map process . filter isValid $ input

-- Intermediate type annotations for complex pipelines
processData :: [InputData] -> [OutputData]
processData input = 
  let validData = filter isValid input        :: [InputData]
      processedData = map process validData   :: [IntermediateData]
      result = finalize <$> processedData     :: [OutputData]
  in result
```

### Named Function Parameters

Use record syntax for complex parameter sets to make function usage self-documenting.

```haskell
-- Hard to understand parameter meanings
createUser :: String -> Int -> String -> Bool -> IO User
createUser name age email verified = ...

-- Parameters are self-documenting with records
data CreateUserParams = CreateUserParams
  { userName :: String
  , userAge :: Int
  , userEmail :: String
  , isVerified :: Bool
  }

createUser :: CreateUserParams -> IO User
createUser params = ...

-- Usage is clear and order-independent
newUser <- createUser CreateUserParams
  { userName = "John"
  , userAge = 30
  , userEmail = "john@example.com"
  , isVerified = True
  }
```

### Consistent Error Handling Patterns

Choose a consistent error handling approach and stick to it across the codebase.

```haskell
-- Example with ExceptT pattern
type AppM a = ExceptT AppError IO a

-- Clear error hierarchy
data AppError
  = ValidationError String
  | DatabaseError DBError
  | AuthError AuthenticationError
  | NotFoundError Resource
  deriving (Show, Eq)

-- Helper functions for error handling
whenM :: Monad m => m Bool -> m () -> m ()
whenM cond action = do
  result <- cond
  when result action

-- Usage
validateInput :: Input -> AppM ValidatedInput
validateInput input = do
  whenM (pure $ null $ inputName input) $
    throwError $ ValidationError "Name cannot be empty"
  
  whenM (pure $ inputAge input < 18) $
    throwError $ ValidationError "Must be at least 18 years old"
    
  -- Create validated input after all checks pass
  return ValidatedInput
    { validName = inputName input
    , validAge = inputAge input
    }
```

### Build System Patterns

Use simpler build configurations when possible to ease maintenance.

```haskell
-- Prefer Simple build-type over Custom when possible
build-type: Simple

-- Use common extensions across the project
default-extensions: 
  OverloadedStrings
  LambdaCase
  NamedFieldPuns
  RecordWildCards
  DeriveGeneric
  DeriveDataTypeable

-- Only use custom Setup.hs when actually needed
-- For example, to generate and install man pages
```

### Library Selection for Maintainability

Choose libraries that align with the project's complexity needs and maintainer expertise:

```haskell
-- Prefer libraries with clear type signatures and good documentation
-- GOOD: Easy for humans and AI to understand this API
-- xxhash-ffi provides a clear API with good type signatures
import qualified Data.Digest.XXHash.FFI as XXH
import Data.Hashable (hash)

calculateChecksum :: BS.ByteString -> String
calculateChecksum content = show $ hash (XXH.XXH3 content)

-- AVOID: Complex APIs with many type parameters or advanced features 
-- unless they're truly needed
-- Overly complex for simple checksumming needs:
import qualified Crypto.Hash as CH
import qualified Crypto.Hash.Algorithms as CHA

complexChecksum :: BS.ByteString -> String
complexChecksum content = 
  show (CH.hashWith CHA.SHA256 content :: CH.Digest CHA.SHA256)
```

Guidelines for library selection:
- Choose libraries that match the project's complexity and maintainer expertise
- Prefer libraries with clear documentation and simple, well-typed APIs
- Consider the maintenance burden and dependency footprint
- Avoid over-engineered solutions for simple problems
- Document reasoning for library choices to help future maintainers

### Module Organization for Discovery

Structure modules to facilitate code discovery by humans and AI assistants.

```haskell
-- Organize hierarchically with explicit exports
module MyApp
  ( -- * Core types
    AppConfig(..)
  , AppState(..)
    
    -- * Running the application
  , runApp
  , runAppWithConfig
    
    -- * Error handling
  , AppError(..)
  , handleError
  ) where

-- Create index modules
module MyApp.Database
  ( -- * Re-exports from all database modules
    module MyApp.Database.Connection
  , module MyApp.Database.Query
  , module MyApp.Database.Migration
  ) where

import MyApp.Database.Connection
import MyApp.Database.Query
import MyApp.Database.Migration

-- Function purpose is clear from name
validateUserInput :: UserInput -> Either ValidationError ValidatedInput
```

### Type-Level Documentation

Embed information in types to make function behavior self-documenting.

```haskell
-- Types convey information about the function's behavior
authRequired :: HasAuth r => RIO r Resource
adminOnly :: HasAdminAccess r => RIO r Resource

-- Status-tracking in result type
data VerificationStatus = Pending | Verified | Rejected
data Email (s :: VerificationStatus) = Email Text

sendEmail :: Email 'Verified -> Message -> IO ()

-- Directional data flow
data Input
data Processed
data Output

data Pipeline s a where
  Input :: a -> Pipeline 'Input a
  Process :: Pipeline 'Input a -> Pipeline 'Processed a
  Output :: Pipeline 'Processed a -> Pipeline 'Output a

-- Function chains are guaranteed correct order by types
pipeline :: Data -> Pipeline 'Output Result
pipeline = Output . Process . Input
```