Skip to content
Back to skills

Lang Haskell Library Dev

ASecurity

Haskell-specific library development patterns. Use when creating Haskell libraries, designing type-safe public APIs, organizing Cabal/Stack projects, writing property-based tests with QuickCheck, generating Haddock documentation, or publishing to Hackage. Extends meta-library-dev with Haskell tooling and idioms.

  • 8 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added February 8, 2026
documentationgobashtestinggitapidocumentation

Works with

  • api

Security analysis

A93/100
  • highPerforms destructive filesystem operations

Pro shows the line behind each finding and how to fix it

Scanned February 12, 2026

npx -y skills add aRustyDev/ai --skill lang-haskell-library-dev --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Lang Haskell Library Dev?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Lang Haskell Library Dev
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/arustydev-lang-haskell-library-dev/badge)](https://www.skillsdirectory.com/skills/arustydev-lang-haskell-library-dev)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: lang-haskell-library-dev
description: Haskell-specific library development patterns. Use when creating Haskell libraries, designing type-safe public APIs, organizing Cabal/Stack projects, writing property-based tests with QuickCheck, generating Haddock documentation, or publishing to Hackage. Extends meta-library-dev with Haskell tooling and idioms.
---

# Haskell Library Development

Haskell-specific patterns for library development. This skill extends `meta-library-dev` with Haskell tooling, type-driven design principles, and ecosystem practices.

## This Skill Extends

- `meta-library-dev` - Foundational library patterns (API design, versioning, testing strategies)
- `lang-haskell-dev` - Core Haskell language patterns and idioms

For general library concepts like semantic versioning and testing pyramids, see `meta-library-dev`. For Haskell fundamentals like type classes and monads, see `lang-haskell-dev`.

## This Skill Adds

- **Build tooling**: Cabal, Stack, package.yaml configuration
- **Type-driven API design**: Leveraging Haskell's type system for library interfaces
- **Module organization**: Best practices for exposing clean public APIs
- **Property-based testing**: QuickCheck patterns for library validation
- **Haddock documentation**: API documentation standards
- **Hackage publishing**: Package distribution and maintenance

## This Skill Does NOT Cover

- General library patterns - see `meta-library-dev`
- Core Haskell language features - see `lang-haskell-dev`
- Advanced type system features (GADTs, Type Families) - see `lang-haskell-advanced-dev`
- Web frameworks (Servant, Yesod) - see framework-specific skills
- Application development - see `lang-haskell-app-dev`

---

## Quick Reference

| Task | Command/Pattern |
|------|-----------------|
| New library (Cabal) | `cabal init --lib <name>` |
| New library (Stack) | `stack new <name> simple-library` |
| Build | `cabal build` / `stack build` |
| Test | `cabal test` / `stack test` |
| Documentation | `cabal haddock` / `stack haddock` |
| REPL with library | `cabal repl` / `stack ghci` |
| Publish (dry run) | `cabal sdist` / `stack sdist` |
| Upload to Hackage | `cabal upload <tarball>` |
| Check package | `cabal check` |
| Format code | `fourmolu` or `ormolu` |

---

## Project Structure

### Standard Library Layout

```
my-library/
├── my-library.cabal          # Cabal package description
├── stack.yaml                # Stack configuration (optional)
├── package.yaml              # hpack configuration (Stack alternative)
├── LICENSE
├── README.md
├── CHANGELOG.md
├── src/
│   ├── MyLibrary.hs          # Main module (re-exports public API)
│   ├── MyLibrary/
│   │   ├── Types.hs          # Public types
│   │   ├── Core.hs           # Core functionality
│   │   └── Internal.hs       # Internal implementation
│   └── MyLibrary/Internal/   # Private modules
│       └── Helpers.hs
├── test/
│   ├── Spec.hs               # Test entry point
│   ├── MyLibrary/
│   │   ├── TypesSpec.hs      # Unit tests
│   │   └── CoreSpec.hs
│   └── Properties.hs         # QuickCheck properties
├── benchmark/
│   └── Main.hs               # Criterion benchmarks
└── examples/
    └── Basic.hs              # Usage examples

```

### Module Visibility

```haskell
-- src/MyLibrary.hs (main entry point)
module MyLibrary
  ( -- * Core Types
    Config(..)
  , Document
  , ParseResult

    -- * Construction
  , defaultConfig
  , mkDocument

    -- * Operations
  , parse
  , render
  , validate

    -- * Error Types
  , ParseError(..)
  , ValidationError(..)

    -- * Re-export specific modules for advanced use
  , module MyLibrary.Types
  ) where

import MyLibrary.Types
import MyLibrary.Core
import qualified MyLibrary.Internal as Internal

-- Re-export selected functions
parse = Internal.parseImpl
render = Internal.renderImpl
```

---

## Cabal Configuration

### Required Fields (.cabal file)

```cabal
cabal-version:      3.0
name:               my-library
version:            0.1.0.0
synopsis:           A brief one-line description
description:
    A longer description of what this library does.
    .
    Multiple paragraphs are separated by a single dot line.

homepage:           https://github.com/username/my-library
bug-reports:        https://github.com/username/my-library/issues
license:            BSD-3-Clause
license-file:       LICENSE
author:             Your Name
maintainer:         you@example.com
copyright:          2025 Your Name
category:           Data
build-type:         Simple
tested-with:        GHC == 9.4.8
                  , GHC == 9.6.3
                  , GHC == 9.8.1

extra-doc-files:
    CHANGELOG.md
    README.md

extra-source-files:
    examples/*.hs
```

### Library Stanza

```cabal
library
  exposed-modules:
      MyLibrary
      MyLibrary.Types
      MyLibrary.Core

  other-modules:
      MyLibrary.Internal
      MyLibrary.Internal.Helpers

  -- Modules included but not exposed
  reexported-modules:
      Data.Text as MyLibrary.Text

  hs-source-dirs:      src
  default-language:    Haskell2010
  default-extensions:
      OverloadedStrings
      DeriveGeneric
      LambdaCase

  ghc-options:
      -Wall
      -Wcompat
      -Widentities
      -Wincomplete-record-updates
      -Wincomplete-uni-patterns
      -Wmissing-home-modules
      -Wpartial-fields
      -Wredundant-constraints

  build-depends:
      base              >= 4.14 && < 5
    , text              >= 1.2  && < 2.2
    , containers        >= 0.6  && < 0.8
    , bytestring        >= 0.10 && < 0.13
```

### Test Suite Stanza

```cabal
test-suite my-library-test
  type:                exitcode-stdio-1.0
  hs-source-dirs:      test
  main-is:             Spec.hs
  other-modules:
      MyLibrary.TypesSpec
      MyLibrary.CoreSpec
      Properties

  default-language:    Haskell2010
  ghc-options:         -Wall -threaded -rtsopts -with-rtsopts=-N

  build-depends:
      base
    , my-library
    , hspec             >= 2.7  && < 3
    , QuickCheck        >= 2.14 && < 3
    , hspec-discover    >= 2.7  && < 3

  build-tool-depends:
      hspec-discover:hspec-discover
```

### Benchmark Stanza

```cabal
benchmark my-library-bench
  type:                exitcode-stdio-1.0
  hs-source-dirs:      benchmark
  main-is:             Main.hs
  default-language:    Haskell2010
  ghc-options:         -Wall -O2 -threaded -rtsopts -with-rtsopts=-N

  build-depends:
      base
    , my-library
    , criterion         >= 1.5 && < 2
```

---

## Stack Configuration

### stack.yaml

```yaml
resolver: lts-22.7  # GHC 9.6.3

packages:
  - .

extra-deps: []

# Recommended flags
flags: {}

# Build options
ghc-options:
  "$locals": -Wall -Werror=incomplete-patterns

# Testing options
test:
  arguments:
    additional-args:
      - --color
      - --format=progress
```

### package.yaml (hpack alternative to .cabal)

```yaml
name: my-library
version: 0.1.0.0
synopsis: A brief one-line description
description: |
  A longer description of what this library does.

  Multiple paragraphs are supported.

github: username/my-library
license: BSD-3-Clause
author: Your Name
maintainer: you@example.com
copyright: 2025 Your Name
category: Data

extra-source-files:
  - README.md
  - CHANGELOG.md

dependencies:
  - base >= 4.14 && < 5
  - text >= 1.2 && < 2.2
  - containers >= 0.6 && < 0.8

ghc-options:
  - -Wall
  - -Wcompat
  - -Widentities
  - -Wincomplete-record-updates

default-extensions:
  - OverloadedStrings
  - DeriveGeneric
  - LambdaCase

library:
  source-dirs: src
  exposed-modules:
    - MyLibrary
    - MyLibrary.Types
    - MyLibrary.Core

tests:
  my-library-test:
    main: Spec.hs
    source-dirs: test
    ghc-options:
      - -threaded
      - -rtsopts
      - -with-rtsopts=-N
    dependencies:
      - my-library
      - hspec
      - QuickCheck
    build-tools:
      - hspec-discover

benchmarks:
  my-library-bench:
    main: Main.hs
    source-dirs: benchmark
    ghc-options:
      - -O2
      - -threaded
      - -rtsopts
    dependencies:
      - my-library
      - criterion
```

---

## Type-Driven API Design

### Smart Constructors

```haskell
-- Hide constructor, export smart constructor
module MyLibrary.Types
  ( Email       -- Type exported without constructor
  , mkEmail     -- Smart constructor
  , emailText   -- Accessor
  ) where

import Data.Text (Text)
import qualified Data.Text as T

-- Opaque type
newtype Email = Email Text
  deriving (Show, Eq)

-- Smart constructor with validation
mkEmail :: Text -> Either String Email
mkEmail input
  | T.null input = Left "Email cannot be empty"
  | '@' `T.elem` input = Right (Email input)
  | otherwise = Left "Invalid email format"

-- Safe accessor
emailText :: Email -> Text
emailText (Email txt) = txt
```

### Phantom Types for Type Safety

```haskell
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}

-- State machine encoded in types
data State = Draft | Published

newtype Document (s :: State) = Document Text

-- Only drafts can be edited
editDocument :: Text -> Document 'Draft -> Document 'Draft
editDocument newText _ = Document newText

-- Only drafts can be published
publishDocument :: Document 'Draft -> Document 'Published
publishDocument (Document txt) = Document txt

-- Published documents can be rendered
renderDocument :: Document 'Published -> Html
renderDocument (Document txt) = toHtml txt

-- Type-safe workflow
workflow :: Html
workflow =
  renderDocument $
    publishDocument $
      editDocument "Updated content" initialDraft
```

### Builder Pattern with Phantom Types

```haskell
data Incomplete
data Complete

data ConfigBuilder (s :: Type) = ConfigBuilder
  { _timeout :: Maybe Int
  , _retries :: Maybe Int
  , _endpoint :: Maybe String
  }

-- Start with incomplete config
emptyConfig :: ConfigBuilder Incomplete
emptyConfig = ConfigBuilder Nothing Nothing Nothing

-- Builder functions return incomplete
setTimeout :: Int -> ConfigBuilder s -> ConfigBuilder Incomplete
setTimeout t cfg = cfg { _timeout = Just t }

setRetries :: Int -> ConfigBuilder s -> ConfigBuilder Incomplete
setRetries r cfg = cfg { _retries = Just r }

-- Only setEndpoint returns Complete
setEndpoint :: String -> ConfigBuilder s -> ConfigBuilder Complete
setEndpoint e cfg = cfg { _endpoint = Just e }

-- Can only build from Complete
build :: ConfigBuilder Complete -> Config
build (ConfigBuilder (Just t) (Just r) (Just e)) =
  Config t r e
build _ = error "Impossible: Complete builder guarantees all fields set"

-- Usage: Type system enforces all fields are set
config = build $ setEndpoint "https://api.example.com"
              $ setTimeout 30
              $ setRetries 3 emptyConfig
```

### Leveraging Type Classes for Polymorphism

```haskell
-- Generic serialization interface
class Serialize a where
  serialize :: a -> ByteString
  deserialize :: ByteString -> Either String a

-- Provide instances for your types
instance Serialize Document where
  serialize = encodeDocument
  deserialize = decodeDocument

-- Functions polymorphic over Serialize
saveToFile :: Serialize a => FilePath -> a -> IO ()
saveToFile path value = BS.writeFile path (serialize value)

loadFromFile :: Serialize a => FilePath -> IO (Either String a)
loadFromFile path = deserialize <$> BS.readFile path
```

---

## Module Organization Patterns

### Internal Modules

```haskell
-- MyLibrary/Internal.hs
-- This module is in other-modules, not exposed-modules
module MyLibrary.Internal where

-- Internal functions used across the library
-- but not part of public API
internalHelper :: Text -> Result
internalHelper = ...
```

### Hierarchical Re-exports

```haskell
-- MyLibrary.hs (main entry point)
module MyLibrary
  ( -- * Re-export everything from Types
    module MyLibrary.Types
    -- * Re-export selected functions from Core
  , parse
  , validate
  , render
  ) where

import MyLibrary.Types
import MyLibrary.Core (parse, validate, render)
```

### Qualified Re-exports

```haskell
-- For users who want namespaced access
import qualified MyLibrary as ML
import qualified MyLibrary.Advanced as ML.Advanced

document = ML.parse input
result = ML.Advanced.complexOperation document
```

---

## Property-Based Testing with QuickCheck

### Arbitrary Instances

```haskell
{-# LANGUAGE DeriveGeneric #-}

import Test.QuickCheck
import Test.QuickCheck.Arbitrary.Generic

data Document = Document
  { docTitle :: Text
  , docContent :: Text
  , docTags :: [Text]
  } deriving (Show, Eq, Generic)

-- Automatic Arbitrary instance
instance Arbitrary Document where
  arbitrary = genericArbitrary

-- Or custom generators
instance Arbitrary Document where
  arbitrary = Document
    <$> genTitle
    <*> genContent
    <*> listOf genTag
    where
      genTitle = T.pack <$> listOf1 (choose ('a', 'z'))
      genContent = T.pack <$> listOf (choose ('a', 'z'))
      genTag = elements ["haskell", "library", "testing"]

-- Constrained generators
genValidEmail :: Gen Email
genValidEmail = do
  user <- listOf1 (choose ('a', 'z'))
  domain <- elements ["example.com", "test.org"]
  return $ Email (T.pack $ user ++ "@" ++ domain)
```

### Properties

```haskell
-- test/Properties.hs
import Test.Hspec
import Test.QuickCheck
import MyLibrary

spec :: Spec
spec = describe "Document properties" $ do

  describe "parse . render = id" $
    it "roundtrips successfully" $
      property $ \doc ->
        parse (render doc) === Right doc

  describe "parse validates input" $
    it "rejects empty titles" $
      property $ \content tags ->
        let doc = Document "" content tags
        in parse (render doc) `shouldSatisfy` isLeft

  describe "tag operations are idempotent" $
    it "adding same tag twice = adding once" $
      property $ \doc tag ->
        addTag tag (addTag tag doc) === addTag tag doc

  describe "parsing is total (never throws)" $
    it "handles arbitrary input" $
      property $ \(input :: Text) ->
        case parse input of
          Left _ -> True
          Right _ -> True
```

### Conditional Properties

```haskell
prop_sortedListHead :: [Int] -> Property
prop_sortedListHead xs =
  not (null xs) ==>  -- Precondition
  head (sort xs) === minimum xs

-- Better: Use forAll with constrained generator
prop_sortedListHead' :: Property
prop_sortedListHead' =
  forAll (listOf1 arbitrary) $ \xs ->
    head (sort xs) === minimum xs
```

### Invariants

```haskell
-- Check invariants hold after operations
prop_balancedAfterInsert :: Key -> Value -> Tree -> Bool
prop_balancedAfterInsert k v tree =
  isBalanced (insert k v tree)

prop_sizeAfterInsert :: Key -> Value -> Tree -> Property
prop_sizeAfterInsert k v tree =
  k `notMember` tree ==>
  size (insert k v tree) === size tree + 1
```

---

## Haddock Documentation

### Module Documentation

```haskell
{-|
Module      : MyLibrary
Description : Brief description of module purpose
Copyright   : (c) Your Name, 2025
License     : BSD-3-Clause
Maintainer  : you@example.com
Stability   : experimental
Portability : POSIX

Longer description of what this module provides.

= Usage

Basic usage example:

>>> import MyLibrary
>>> let doc = mkDocument "Hello"
>>> render doc
"<document>Hello</document>"

= Advanced Usage

More complex patterns and use cases.
-}
module MyLibrary where
```

### Function Documentation

```haskell
-- | Parse a document from text.
--
-- This function validates the input and returns either
-- a parse error or a valid document.
--
-- ==== Examples
--
-- Basic usage:
--
-- >>> parse "title: Hello\ncontent: World"
-- Right (Document {docTitle = "Hello", docContent = "World"})
--
-- Invalid input:
--
-- >>> parse ""
-- Left "Empty input"
--
-- ==== Notes
--
-- * Input must be UTF-8 encoded
-- * Title is required
-- * Content may be empty
--
parse :: Text -> Either ParseError Document
parse = ...

-- | Render a document to text.
--
-- The output format is compatible with 'parse':
--
-- prop> parse (render doc) == Right doc
--
render :: Document -> Text
render = ...
```

### Documenting Types

```haskell
-- | Configuration for the parser.
--
-- Use 'defaultConfig' or the builder pattern to construct.
data Config = Config
  { configTimeout :: Int
    -- ^ Timeout in seconds (must be positive)
  , configStrict :: Bool
    -- ^ Enable strict parsing mode
  , configEncoding :: Encoding
    -- ^ Character encoding to use
  } deriving (Show, Eq)

-- | Default configuration.
--
-- Equivalent to:
--
-- @
-- Config
--   { configTimeout = 30
--   , configStrict = False
--   , configEncoding = UTF8
--   }
-- @
defaultConfig :: Config
defaultConfig = Config 30 False UTF8
```

### Sections and Organization

```haskell
module MyLibrary
  ( -- * Types
    Document(..)
  , ParseError(..)

    -- * Construction
  , mkDocument
  , defaultDocument

    -- * Operations
    -- ** Parsing
  , parse
  , parseStrict
    -- ** Rendering
  , render
  , renderPretty

    -- * Utilities
  , validate
  , normalize
  ) where
```

### Code Examples in Documentation

```haskell
-- | Batch process multiple documents.
--
-- >>> let docs = [mkDocument "A", mkDocument "B"]
-- >>> mapM_ (print . render) docs
-- "<document>A</document>"
-- "<document>B</document>"
--
-- __Warning:__ This loads all documents into memory.
-- For large batches, use 'streamProcess' instead.
processBatch :: [Document] -> IO ()
processBatch = ...
```

---

## Publishing to Hackage

### Pre-publish Checklist

- [ ] Version bumped in .cabal (follow PVP)
- [ ] CHANGELOG.md updated
- [ ] All tests pass: `cabal test --test-show-details=direct`
- [ ] Documentation builds: `cabal haddock`
- [ ] Package builds: `cabal build all`
- [ ] No warnings: `cabal build -Wall -Werror`
- [ ] `cabal check` passes with no errors
- [ ] README.md is current and accurate
- [ ] LICENSE file included
- [ ] Tested with multiple GHC versions
- [ ] Hackage account credentials configured

### Package Versioning Policy (PVP)

Haskell uses PVP (A.B.C.D):

| Change Type | Version Update | Example |
|-------------|----------------|---------|
| Breaking API change | A.B → (A+1).0 | 1.2.0.0 → 2.0.0.0 |
| New functionality (compatible) | A.B.C → A.(B+1).0 | 1.2.3.0 → 1.3.0.0 |
| Bug fix (no API change) | A.B.C.D → A.B.C.(D+1) | 1.2.3.0 → 1.2.3.1 |

```cabal
-- Before: version 0.1.0.0
-- After adding compatible function: version 0.2.0.0
-- After breaking change: version 1.0.0.0
```

### Version Bounds

```cabal
build-depends:
    base              >= 4.14 && < 5
    -- Allow major version 4.14 through 4.x, but not 5.x

  , text              >= 1.2  && < 1.3
    -- Allow 1.2.x versions only

  , containers        >= 0.6  && < 0.8
    -- Allow 0.6.x and 0.7.x
```

### Building Distribution Tarball

```bash
# Check package is ready
cabal check

# Build source distribution
cabal sdist

# Check the tarball builds
cabal upload --publish <dist-newstyle/sdist/my-library-0.1.0.0.tar.gz> --dry-run

# Extract and test the tarball
cd /tmp
tar xzf my-library-0.1.0.0.tar.gz
cd my-library-0.1.0.0
cabal build
cabal test
```

### Uploading to Hackage

```bash
# First-time setup: Create ~/.cabal/config with Hackage credentials
# Or use environment variable
export HACKAGE_USERNAME=yourusername
export HACKAGE_PASSWORD=yourpassword

# Upload as candidate (test on Hackage but not published)
cabal upload path/to/my-library-0.1.0.0.tar.gz

# Publish from candidate
cabal upload --publish path/to/my-library-0.1.0.0.tar.gz

# Check package page
open https://hackage.haskell.org/package/my-library
```

### Documentation Upload

```bash
# Build documentation
cabal haddock --haddock-for-hackage

# Upload to Hackage
cabal upload -d <dist-newstyle/.../my-library-0.1.0.0-docs.tar.gz>
```

### Deprecating Packages

```cabal
-- In .cabal file
x-deprecated: Please use better-library instead
```

---

## Testing Patterns

### Unit Tests with HSpec

```haskell
-- test/MyLibrary/CoreSpec.hs
module MyLibrary.CoreSpec (spec) where

import Test.Hspec
import MyLibrary

spec :: Spec
spec = describe "Core functionality" $ do

  describe "parse" $ do
    it "parses valid input" $ do
      parse "title: Hello" `shouldBe` Right expectedDoc

    it "rejects empty input" $ do
      parse "" `shouldSatisfy` isLeft

    it "handles unicode" $ do
      parse "title: こんにちは" `shouldSatisfy` isRight

  describe "render" $ do
    it "produces valid output" $ do
      let doc = mkDocument "Test"
      render doc `shouldContain` "Test"

    context "with empty document" $ do
      it "returns empty string" $ do
        render emptyDocument `shouldBe` ""
```

### Test Organization

```haskell
-- test/Spec.hs
{-# OPTIONS_GHC -F -pgmF hspec-discover #-}

-- hspec-discover automatically finds all *Spec.hs files
```

### Testing with Temporary Files

```haskell
import System.IO.Temp (withSystemTempFile)

spec = describe "File operations" $
  it "saves and loads documents" $
    withSystemTempFile "test.doc" $ \path handle -> do
      hClose handle
      saveDocument path doc
      loaded <- loadDocument path
      loaded `shouldBe` Right doc
```

---

## Common Dependencies

### Core Libraries

```cabal
build-depends:
    base              >= 4.14 && < 5
  , text              >= 1.2  && < 2.2
  , bytestring        >= 0.10 && < 0.13
  , containers        >= 0.6  && < 0.8
```

### Parsing

```cabal
  , attoparsec        >= 0.13 && < 0.15
  , megaparsec        >= 9.0  && < 10
  , parser-combinators >= 1.2 && < 2
```

### Error Handling

```cabal
  , exceptions        >= 0.10 && < 0.11
  , transformers      >= 0.5  && < 0.7
```

### Testing

```cabal
  , hspec             >= 2.7  && < 3
  , QuickCheck        >= 2.14 && < 3
  , hspec-discover    >= 2.7  && < 3
```

### JSON

```cabal
  , aeson             >= 2.0  && < 2.3
```

---

## Anti-Patterns

### 1. Exposing Too Much

```haskell
-- Bad: Exposes internal implementation
module MyLibrary (module X) where
import MyLibrary.Internal as X

-- Good: Selective exports
module MyLibrary
  ( Document
  , parse
  , render
  ) where
```

### 2. Partial Functions in Public API

```haskell
-- Bad: Can throw exception
head' :: [a] -> a
head' (x:_) = x

-- Good: Total function
headMaybe :: [a] -> Maybe a
headMaybe (x:_) = Just x
headMaybe [] = Nothing
```

### 3. Over-Constrained Version Bounds

```haskell
-- Bad: Too restrictive
build-depends: text == 1.2.4.0

-- Good: PVP-compatible range
build-depends: text >= 1.2 && < 1.3
```

### 4. Missing Upper Bounds

```haskell
-- Bad: No upper bound (will break on major updates)
build-depends: aeson >= 2.0

-- Good: PVP upper bound
build-depends: aeson >= 2.0 && < 2.3
```

---

## Troubleshooting

### Cabal Hell (Dependency Conflicts)

**Problem:** Cannot satisfy dependency requirements

```
Resolving dependencies...
Error: Could not resolve dependencies
```

**Fix:** Use cabal's new-style builds (default in modern Cabal):
```bash
# Clear cache and rebuild
cabal clean
rm -rf dist-newstyle
cabal build

# Or use Stack which uses curated snapshots
stack build
```

### Haddock Fails to Build

**Problem:** Haddock parse errors

**Fix:** Check for:
- Unbalanced delimiters in documentation comments
- Invalid Haddock syntax (`>>>` for examples, `@` for code)
- Missing closing brackets in sections

```bash
# Build with verbose output
cabal haddock --haddock-options="--verbose"
```

### Package Check Errors

**Problem:** `cabal check` reports warnings/errors

**Fix:** Address each error:
- Add missing fields (synopsis, description, license)
- Fix version bounds
- Include all necessary files in extra-source-files

### Tests Pass Locally but Fail in CI

**Problem:** Different GHC versions or dependencies

**Fix:**
- Test locally with multiple GHC versions using Stack
- Use matrix builds in CI
- Pin resolver versions in stack.yaml

```yaml
# .github/workflows/ci.yml
strategy:
  matrix:
    ghc: ['9.2.8', '9.4.8', '9.6.3']
```

---

## References

- `meta-library-dev` - Foundational library patterns
- `lang-haskell-dev` - Core Haskell fundamentals
- [Haskell Package Versioning Policy](https://pvp.haskell.org/)
- [Cabal User Guide](https://cabal.readthedocs.io/)
- [Stack User Guide](https://docs.haskellstack.org/)
- [Hackage](https://hackage.haskell.org/)
- [Haddock Documentation](https://haskell-haddock.readthedocs.io/)
- [QuickCheck Manual](https://hackage.haskell.org/package/QuickCheck)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…