Skill: mightybyte/haskell (version 1.0.0)

--- name: haskell description: Expert Haskell development skill. Covers type-driven design, GHC extensions, Cabal/Stack/Nix builds, performance optimization, testing, and the modern Haskell library ecosystem. Activate for any Haskell programming, debugging, or architecture tasks. --- # Haskell Development Guide ## Core Philosophy 1. **Types are the design** — Make illegal states unrepresentable. If the type checker accepts it, we would like it to be correct. 2. **Purity by default** — Side effects are explicit in the type system. `IO` is a feature, not a burden. 3. **Composition over inheritance** — Small, composable functions. Typeclasses for ad-hoc polymorphism. 4. **Laziness as a tool** — Enables elegant abstractions but demands awareness of space leaks. 5. **Correctness first, then performance** — Get it right, then profile, then optimize. 5. **Keep it Simple** — Purity and strong types (i.e. roughly Haskell2010) gives you the majority of Haskell's value. Avoid more advanced language features unless absolutely necessary. ## Project Setup ### Cabal (preferred with Nix) ```bash mkdir my-project && cd my-project cabal init --interactive ``` Minimal `my-project.cabal`: ```cabal cabal-version: 3.0 name: my-project version: 0.1.0.0 build-type: Simple common warnings ghc-options: -Wall -Werror -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wpartial-fields -Wredundant-constraints library import: warnings exposed-modules: MyProject build-depends: base >= 4.17 && < 5 , text , containers , aeson hs-source-dirs: src default-language: Haskell2010 default-extensions: DeriveGeneric DerivingStrategies LambdaCase ScopedTypeVariables executable my-project import: warnings main-is: Main.hs build-depends: base, my-project hs-source-dirs: app default-language: Haskell2010 test-suite tests import: warnings type: exitcode-stdio-1.0 main-is: Main.hs build-depends: base, my-project, hspec, QuickCheck hs-source-dirs: test default-language: Haskell2010 ``` ## Project Structure ``` my-project/ ├── exe/ │ └── Main.hs # Executable entry point (thin — delegates to library) ├── src/ │ ├── MyProject.hs # Public API (re-exports) │ ├── MyProject/ │ │ ├── Types.hs # Core domain types │ │ ├── App.hs # Application monad, config │ │ ├── DB.hs # Database layer │ │ ├── API.hs # HTTP/API layer │ │ └── Internal/ # Not exported — implementation details │ │ └── Utils.hs ├── test/ │ ├── Main.hs │ └── MyProject/ │ ├── TypesSpec.hs │ └── DBSpec.hs ├── my-project.cabal ├── cabal.project # Multi-package config, source-repository-packages ``` ## Essential GHC Extensions ### Always Enable (via `default-extensions` in .cabal) ```haskell DeriveGeneric -- Derives Generic DerivingStrategies -- Explicit: deriving stock, newtype, anyclass, via LambdaCase -- \case { ... } instead of \x -> case x of ... ScopedTypeVariables -- forall a. ... lets you reference 'a' in where clauses ``` ### Use Freely When Needed ```haskell BangPatterns DeriveDataTypeable DeriveFunctor DeriveGeneric -- Generic instances for aeson, etc. DerivingVia -- Derive via newtype coercion DuplicateRecordFields -- Same field name in different records ExistentialQuantification -- Hide type variables FlexibleContexts -- Relax context restrictions FlexibleInstances -- Relax instance head restrictions FunctionalDependencies -- fundeps for MPTC GeneralizedNewtypeDeriving -- Derive through newtypes MultiParamTypeClasses -- Typeclasses with multiple params NumericUnderscores -- More readable number syntax OverloadedRecordDot -- record.field syntax (GHC 9.2+) OverloadedStrings -- String literals as Text/ByteString RankNTypes -- Higher-rank polymorphism (forall inside arrows) RecordWildCards -- Controversial but can be used effectively ``` ### Avoid unless there's a significant clear value ```haskell TemplateHaskell -- Metaprogramming (aeson TH, lens TH). Slows compilation. GADTs -- Generalized algebraic data types TypeFamilies -- Type-level functions DataKinds -- Promote data constructors to types ConstraintKinds -- Alias constraint sets UndecidableInstances -- Sometimes needed for MTL/type families. Understand why. TypeOperators -- type a :+: b. Servant uses heavily. AllowAmbiguousTypes -- Pair with TypeApplications for type-level dispatch. ``` ## Type-Driven Development ### Scope accessors with type name to avoid name clashes ``` data User = User { _user_firstName :: String , _user_email :: String } deriving (Eq,Ord,Show,Read,Generic) ``` ### Make Illegal States Unrepresentable ```haskell -- BAD: stringly-typed data User = User { _user_role :: String, _user_email :: String } -- GOOD: types encode constraints data Role = Admin | Editor | Viewer deriving stock (Show, Eq, Ord) newtype Email = Email { _unEmail :: Text } -- smart constructor validates mkEmail :: Text -> Either EmailError Email mkEmail t | "@" `T.isInfixOf` t = Right (Email t) | otherwise = Left InvalidEmail data User = User { _user_role :: !Role , _user_email :: !Email } ``` ### Phantom Types for State Machines ```haskell data Draft data Published data Article (s :: Type) = Article { articleTitle :: !Text , articleContent :: !Text } publish :: Article Draft -> Article Published publish (Article t c) = Article t c -- Only published articles can be shared share :: Article Published -> IO () share = ... ``` ### Newtypes for Safety ```haskell newtype UserId = UserId { unUserId :: Int64 } deriving newtype (Eq, Ord, Show, FromJSON, ToJSON) newtype ProductId = ProductId { unProductId :: Int64 } deriving newtype (Eq, Ord, Show, FromJSON, ToJSON) -- Now you can't accidentally pass a ProductId where UserId is expected getUser :: UserId -> IO User ``` ## Error Handling ```haskell -- Pure errors: Either parseConfig :: Text -> Either ConfigError Config -- App-level errors: ExceptT or MonadError class Monad m => MonadError e m where throwError :: e -> m a catchError :: m a -> (e -> m a) -> m a -- The ReaderT pattern (simple, composable) newtype App a = App { unApp :: ReaderT AppEnv IO a } deriving newtype (Functor, Applicative, Monad, MonadIO, MonadReader AppEnv) data AppError = NotFound Text | Unauthorized | ValidationError [Text] deriving stock (Show) -- Throw with exceptions in IO, catch at boundaries throwIO :: Exception e => e -> IO a catch :: Exception e => IO a -> (e -> IO a) -> IO a -- RULE: Use Either for expected failures, exceptions for unexpected/IO failures. -- Never use error/undefined in library code. ``` ## Common Patterns ### The ReaderT Pattern ```haskell data AppEnv = AppEnv { appDbPool :: !Pool Connection , appLogger :: !Logger , appConfig :: !Config } newtype App a = App (ReaderT AppEnv IO a) deriving newtype (Functor, Applicative, Monad, MonadIO, MonadReader AppEnv) runApp :: AppEnv -> App a -> IO a runApp env (App m) = runReaderT m env -- Use Has-pattern for granular access: class Has field env where obtain :: env -> field instance Has (Pool Connection) AppEnv where obtain = appDbPool grabPool :: (MonadReader env m, Has (Pool Connection) env) => m (Pool Connection) grabPool = asks obtain ``` ### Optics (lens/optics) ```haskell -- With OverloadedRecordDot (GHC 9.2+), often you don't need lens for simple access. -- Use lens/optics for: nested updates, traversals, prisms for sum types. -- lens: view, set, over view _1 (1, 2) -- 1 set _1 10 (1, 2) -- (10, 2) over _1 (+1) (1, 2) -- (2, 2) -- Compose with (.) view (config . database . host) appEnv over (users . each . name) T.toUpper myData ``` ## Testing ### HSpec - Testing Framework ```haskell -- test/MyProject/TypesSpec.hs module MyProject.TypesSpec (spec) where import Test.Hspec import MyProject.Types spec :: Spec spec = do describe "mkEmail" $ do it "accepts valid emails" $ mkEmail "[REDACTED_SECRET]" `shouldBe` Right (Email "[REDACTED_SECRET]") it "rejects invalid emails" $ mkEmail "invalid" `shouldSatisfy` isLeft ``` ### QuickCheck - Property Testing ```haskell import Test.QuickCheck prop_reverseReverse :: [Int] -> Bool prop_reverseReverse xs = reverse (reverse xs) == xs -- Generate domain types: instance Arbitrary Email where arbitrary = do user <- listOf1 (elements ['a'..'z']) domain <- listOf1 (elements ['a'..'z']) pure $ Email $ T.pack $ user <> "@" <> domain <> ".com" ``` ## Performance Essentials ### Strictness ```haskell -- Strict fields in data types (almost always want this): data Config = Config { configHost :: !Text -- strict (bang pattern in field) , configPort :: !Int } -- BangPatterns in let bindings: {-# LANGUAGE BangPatterns #-} let !result = expensiveComputation -- Use Text, not String. Always. -- Use ByteString for binary data. -- Use Vector for indexed access, [] for sequential processing. -- Use HashMap/HashSet for large unordered collections. -- Use Map/Set for ordered collections or when Ord is available. ``` ### Profiling ```bash # Build with profiling cabal build --enable-profiling # Run with heap profiling ./my-project +RTS -hc -p -RTS # Time profiling report ./my-project +RTS -p -RTS # Generates my-project.prof # Heap profile visualization hp2ps -c my-project.hp ``` ### Common Space Leaks ```haskell -- BAD: lazy accumulator foldl (+) 0 [1..1000000] -- builds giant thunk chain -- GOOD: strict left fold foldl' (+) 0 [1..1000000] -- evaluates as it goes -- BAD: lazy state modify (\s -> s { count = count s + 1 }) -- thunk builds up -- GOOD: strict state modify' (\s -> s { count = count s + 1 }) -- Or: use strict fields + evaluate ``` ## Commands Reference | Task | Command | |------|---------| | Build | `cabal build` | | Run | `cabal run my-project` | | Test | `cabal test --test-show-details=direct` | | REPL | `cabal repl` | | Docs | `cabal haddock` | | Lint | `hlint src/` | | File watch | `ghcid --command="cabal repl"` | | Deps outdated | `cabal outdated` | | Clean | `cabal clean` | | Nix build | `nix build` | | Nix dev shell | `nix develop` | ## Common Gotchas 1. **String is [Char]** — Use `Text` (from `text` package) everywhere. Enable `OverloadedStrings` for Text string literals. 2. **Lazy IO** — `readFile` from Prelude is lazy. Use `Data.Text.IO.readFile` or `ByteString.readFile` instead. 3. **Orphan instances** — Don't define typeclass instances outside the module that defines the type or the class. Use newtypes to wrap. 4. **Cabal hell** — Use Nix or cabal's built-in solver (v2-build). Don't use `cabal-install` v1 commands. 5. **Records** — Field names are global. Prefix field names with a uniform and readable scheme: `_employee_familyName`. 6. **Partial functions** — Never use `head`, `tail`, `fromJust`, `read` in production code. Use pattern matching or safe alternatives. 7. **Unsafe operations** - Never use `accursedUnutterablePerformIO`. Only use `unsafePerformIO` when you can prove its use correct. 8. **Undefined** - `undefined` is a fantastic tool for stubbing things out for incremental development and type checking. It should never exist in production code. 9. **foldl vs foldl'** — Always use `foldl'` (strict). The lazy `foldl` from Prelude is almost never what you want. 10. **Show for serialization** — `Show` is for debugging. Use `aeson` for JSON, `binary`/`cereal` for binary serialization. 11. **MonadFail** — Pattern match failures in `do` blocks require `MonadFail`. Avoid partial patterns in `do`. 12. **Template Haskell ordering** — TH splices create declaration groups. All TH splices must come after the declarations they reference and before declarations that reference the generated code. ## Key Libraries | Library | Purpose | |---------|---------| | `text` | Unicode text (strict & lazy) | | `bytestring` | Binary data | | `aeson` | JSON encoding/decoding | | `postgresql-simple`, `mysql-simple` | Low-level database library | | `lens` / `optics` | Composable getters/setters | | `mtl` | Monad transformer classes | | `containers` | Map, Set, Seq (ordered) | | `unordered-containers` | HashMap, HashSet (fast) | | `vector` | Efficient arrays | | `conduit` / `streaming` | Streaming data processing | | `warp` | Fast HTTP server | | `http-client` | Library for making http requests | | `stm` | Software transactional memory | | `QuickCheck` | Property-based testing | | `hspec` | BDD-style test framework | | `tasty` | Test framework (composable) | | `criterion` | Benchmarking | | `optparse-applicative` | CLI argument parsing | | `katip` | Structured logging | | `fake` | Generating realistic mock data | ## References For detailed information, see: - `references/type-system.md` — ADTs, GADTs, type families, type classes, DataKinds, phantom types - `references/common-patterns.md` — MTL, ReaderT, effect systems, optics, free monads, type-level programming - `references/libraries.md` — Essential library ecosystem with examples - `references/performance.md` — Strictness, profiling, space leaks, concurrency, benchmarking - `references/ghc-extensions.md` — Comprehensive GHC extension guide by category - `references/nix-haskell.md` — Nix-based Haskell development (nixpkgs + haskell.nix) - `references/cabal-guide.md` — Cabal format, multi-package projects, Hackage publishing - `references/best-practices.md` — Code organization, Haddock, CI, style guide

path: references/type-system.md
content: # Advanced Type System

## Algebraic Data Types

### Sum Types (Tagged Unions)
```haskell
data Result a b = Success a | Failure b
  deriving stock (Show, Eq, Functor, Foldable, Traversable)
  deriving anyclass (ToJSON, FromJSON)

-- Pattern matching
processResult :: Result User Error -> IO ()
processResult = \case
  Success user -> putStrLn $ "Welcome " <> userName user
  Failure err -> putStrLn $ "Error: " <> show err
```

### Product Types (Records)
```haskell
data User = User
  { userId :: UserId
  , userName :: UserName  
  , userEmail :: Email
  , userCreatedAt :: UTCTime
  } deriving stock (Show, Eq, Generic)
    deriving anyclass (ToJSON, FromJSON)

-- Record update syntax
updateEmail :: Email -> User -> User
updateEmail newEmail user = user { userEmail = newEmail }

-- Lens-compatible fields (with TemplateHaskell)
makeLenses ''User  -- Generates: userId :: Lens' User UserId
```

### Phantom Types for Type Safety
```haskell
data Validated a = Validated a deriving (Show, Eq)
data Unvalidated a = Unvalidated a deriving (Show, Eq)

type ValidatedEmail = Validated Email
type UnvalidatedEmail = Unvalidated Email

validate :: UnvalidatedEmail -> Either ValidationError ValidatedEmail
validate (Unvalidated email) 
  | isValidEmail email = Right (Validated email)
  | otherwise = Left InvalidEmailFormat

-- Forces validation before use
sendEmail :: ValidatedEmail -> Subject -> Body -> IO ()
```

## Generalized Algebraic Data Types (GADTs)

### Type-Safe Expression Trees
```haskell
{-# LANGUAGE GADTs, DataKinds #-}

data Expr a where
  IntLit :: Int -> Expr Int
  BoolLit :: Bool -> Expr Bool
  Add :: Expr Int -> Expr Int -> Expr Int
  Eq :: Eq a => Expr a -> Expr a -> Expr Bool
  If :: Expr Bool -> Expr a -> Expr a -> Expr a

-- Type-safe evaluation
eval :: Expr a -> a
eval (IntLit n) = n
eval (BoolLit b) = b  
eval (Add x y) = eval x + eval y
eval (Eq x y) = eval x == eval y
eval (If cond true false) = if eval cond then eval true else eval false

-- Usage - types enforced at compile time
example :: Expr Int
example = If (Eq (IntLit 5) (Add (IntLit 2) (IntLit 3))) 
             (IntLit 42)
             (IntLit 0)
```

### State Machine Types
```haskell
data State = Locked | Unlocked deriving (Show, Eq)

data Door :: State -> * where
  LockedDoor :: Door 'Locked
  UnlockedDoor :: Door 'Unlocked

unlock :: Code -> Door 'Locked -> Either InvalidCode (Door 'Unlocked)
lock :: Door 'Unlocked -> Door 'Locked  
open :: Door 'Unlocked -> IO (Door 'Unlocked)
-- Cannot open locked door - compile error!
```

## Type Families

### Associated Type Families
```haskell
class Container c where
  type Element c :: *
  empty :: c
  insert :: Element c -> c -> c
  member :: Element c -> c -> Bool

instance Container [a] where
  type Element [a] = a
  empty = []
  insert = (:)
  member = elem

instance Ord a => Container (Set a) where  
  type Element (Set a) = a
  empty = Set.empty
  insert = Set.insert
  member = Set.member
```

## Type Classes

### Multi-Parameter Type Classes
```haskell
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}

class Convertible a b | a -> b where
  convert :: a -> b

instance Convertible String Text where
  convert = Text.pack
  
instance Convertible Text String where  
  convert = Text.unpack

-- Functional dependency: a determines b
-- Only one instance per source type allowed
```

### Higher-Kinded Type Classes
```haskell
class Functor f => Applicative f where
  pure :: a -> f a
  (<*>) :: f (a -> b) -> f a -> f b

class Applicative m => Monad m where
  (>>=) :: m a -> (a -> m b) -> m b

-- Monad transformers
class MonadTrans t where
  lift :: Monad m => m a -> t m a

instance MonadTrans (ReaderT r) where
  lift m = ReaderT (\_ -> m)
```

### Type Class Hierarchies
```haskell
class Semigroup a where
  (<>) :: a -> a -> a

class Semigroup a => Monoid a where
  mempty :: a
  
class Functor f => Foldable f where
  foldMap :: Monoid m => (a -> m) -> f a -> m
  
class (Functor t, Foldable t) => Traversable t where  
  traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
```

## Existential Types

### Hiding Type Information
```haskell
{-# LANGUAGE ExistentialQuantification, RankNTypes #-}

data SomeException = forall e. Exception e => SomeException e

instance Show SomeException where
  show (SomeException e) = show e

-- Type-erased containers
data SomeStorable = forall a. Storable a => SomeStorable a

data Widget = forall w. WidgetClass w => Widget w

render :: Widget -> IO ()
render (Widget w) = renderWidget w  -- Uses type class method
```

### Heterogeneous Lists
```haskell
data HList :: [*] -> * where
  HNil :: HList '[]
  HCons :: a -> HList as -> HList (a ': as)

-- Type-safe heterogeneous operations  
hhead :: HList (a ': as) -> a
hhead (HCons x _) = x

htail :: HList (a ': as) -> HList as
htail (HCons _ xs) = xs
```



path: references/performance.md
content: # Performance Optimization Guide

## Understanding Laziness

### When Laziness Helps
```haskell
-- Infinite structures work naturally
fibonacci :: [Integer]
fibonacci = 0 : 1 : zipWith (+) fibonacci (tail fibonacci)

-- Take only what you need
first10Fibs :: [Integer]
first10Fibs = take 10 fibonacci

-- Short-circuiting evaluation
any' :: (a -> Bool) -> [a] -> Bool
any' p = foldr (\x acc -> p x || acc) False  -- Stops at first True

-- Efficient stream fusion
processLargeList :: [Int] -> Int
processLargeList = sum . filter even . map (*2)  -- Fuses into single pass
```

### When Laziness Hurts
```haskell
-- Space leak: accumulating thunks
badSum :: [Int] -> Int
badSum = foldl (+) 0  -- Builds up thunk chain

-- Good: strict evaluation
goodSum :: [Int] -> Int  
goodSum = foldl' (+) 0  -- Forces evaluation at each step

-- Lazy fields cause space leaks
data BadCounter = BadCounter 
  { count :: Int      -- Lazy field accumulates thunks
  , name :: String 
  }

-- Better: strict fields
data GoodCounter = GoodCounter
  { count :: !Int     -- Strict field
  , name :: !String   -- Forces evaluation
  }
```

## Strictness Annotations

### Bang Patterns
```haskell
{-# LANGUAGE BangPatterns #-}

-- Force evaluation of parameters
strictMap :: (a -> b) -> [a] -> [b]
strictMap _ [] = []
strictMap f (!x:xs) = f x : strictMap f xs  -- x evaluated immediately

-- Strict local bindings
processData :: [Int] -> Int
processData xs = 
  let !len = length xs      -- Evaluated immediately
      !avg = sum xs `div` len
  in avg * 2

-- Strict pattern matching in case
parseNumber :: String -> Maybe Int
parseNumber s = case readMaybe s of
  !result@(Just _) -> result  -- Force evaluation of successful parse
  Nothing -> Nothing
```

### UNPACK Pragma
```haskell
-- Unpack small strict fields directly into constructor
data Point = Point 
  { pointX :: {-# UNPACK #-} !Double  -- No pointer indirection
  , pointY :: {-# UNPACK #-} !Double
  } deriving (Show, Eq)

-- Automatic unpacking for simple types  
data RGB = RGB 
  { red :: {-# UNPACK #-} !Word8
  , green :: {-# UNPACK #-} !Word8  
  , blue :: {-# UNPACK #-} !Word8
  }

-- Don't unpack large or complex types
data User = User
  { userId :: {-# UNPACK #-} !Int     -- Good: small, simple
  , userName :: !Text                 -- Don't unpack: Text is complex
  , userTags :: [Tag]                 -- Don't unpack: list is lazy by design
  }
```

### Seq and DeepSeq
```haskell
import Control.DeepSeq

-- seq: evaluate to weak head normal form (WHNF)
forceHead :: [a] -> [a]
forceHead xs = length xs `seq` xs  -- Forces spine evaluation

-- deepseq: fully evaluate structure  
forceAll :: NFData a => [a] -> [a]
forceAll xs = rnf xs `seq` xs  -- Forces complete evaluation

-- Custom NFData instances
instance NFData User where
  rnf User{..} = rnf userId `seq` 
                 rnf userName `seq` 
                 rnf userEmail

-- Parallel strategies with evaluation control
import Control.Parallel.Strategies

parallelMap :: NFData b => (a -> b) -> [a] -> [b]
parallelMap f xs = map f xs `using` parList rdeepseq
```

## Memory Profiling

### Heap Profiling Setup
```bash
# Build with profiling enabled
cabal build --enable-profiling

# Generate heap profile
./myapp +RTS -h -p

# Different heap profiling modes
./myapp +RTS -hc     # Cost center profiling
./myapp +RTS -hm     # Module profiling
./myapp +RTS -hy     # Type profiling
./myapp +RTS -hd     # Closure description profiling

# Generate visual heap profile
hp2ps -c myapp.hp    # Creates myapp.ps
```

### Cost Centers
```haskell
{-# OPTIONS_GHC -fprof-auto #-}  -- Automatic cost centers

-- Manual cost centers for precision
expensiveComputation :: [Int] -> Int
expensiveComputation xs = {-# SCC "expensive_computation" #-}
  sum $ map complexFunction xs
  where
    complexFunction x = {-# SCC "complex_function" #-}
      x * x + fibonacci x

-- Time profiling
main :: IO ()
main = do
  let result = {-# SCC "main_computation" #-} 
               expensiveComputation [1..10000]
  print result
```

### Space Leak Detection
```haskell
-- Common space leak patterns

-- 1. Lazy accumulator
badFoldl :: [Int] -> Int
badFoldl = foldl (+) 0  -- Creates thunk chain

-- Fix: strict accumulator
goodFoldl :: [Int] -> Int  
goodFoldl = foldl' (+) 0

-- 2. Retaining large structures
processFile :: FilePath -> IO Int
processFile file = do
  content <- readFile file  -- Entire file in memory
  -- Process content...
  return (length content)   -- File content retained until return

-- Fix: streaming or strict reading
processFileStrict :: FilePath -> IO Int  
processFileStrict file = do
  content <- readFile file
  let !len = length content  -- Force evaluation
  return len

-- 3. Monadic accumulation
badMonadicSum :: [IO Int] -> IO Int
badMonadicSum [] = return 0
badMonadicSum (x:xs) = do
  val <- x
  rest <- badMonadicSum xs
  return (val + rest)  -- Accumulates thunks in IO

-- Fix: strict evaluation in monad
goodMonadicSum :: [IO Int] -> IO Int
goodMonadicSum = foldM (\acc action -> do
  val <- action
  let !newAcc = acc + val
  return newAcc) 0
```

## High-Performance Data Structures

### Vector for Arrays
```haskell
import qualified Data.Vector as V
import qualified Data.Vector.Unboxed as U
import qualified Data.Vector.Storable as S
import qualified Data.Vector.Mutable as MV

-- Choose the right vector type
-- V.Vector: Boxed vectors for complex types  
-- U.Vector: Unboxed vectors for primitives (faster)
-- S.Vector: Storable vectors for C interop

-- Efficient array processing
processNumbers :: U.Vector Double -> Double
processNumbers numbers = 
  U.sum $ U.map (\x -> x * x + 1) $ U.filter (> 0) numbers

-- In-place mutation for performance
createLookupTable :: Int -> IO (U.Vector Int)
createLookupTable size = do
  mv <- MV.new size
  forM_ [0..size-1] $ \i -> 
    MV.write mv i (expensiveFunction i)
  U.freeze mv

-- Parallel vector operations
import qualified Data.Vector.Parallel as PV

parallelProcessing :: U.Vector Double -> U.Vector Double
parallelProcessing = PV.map expensiveFunction
```

### ByteString for Binary Data
```haskell
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import qualified Data.ByteString.Builder as B

-- Efficient string building
buildLargeByteString :: [ByteString] -> LBS.ByteString
buildLargeByteString chunks = B.toLazyByteString $
  foldMap B.byteString chunks

-- Avoid intermediate allocations
processLargeFile :: FilePath -> FilePath -> IO ()
processLargeFile input output = 
  BS.readFile input >>= BS.writeFile output . processData
  where
    processData = BS.map toUpper . BS.filter (/= 32)  -- Remove spaces, uppercase

-- Lazy ByteString for streaming
streamProcess :: FilePath -> IO Int64
streamProcess file = do
  content <- LBS.readFile file
  return $ LBS.length $ LBS.filter (/= 10) content  -- Count non-newlines
```

### Text Performance
```haskell
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Builder as TB
import qualified Data.Text.IO as T

-- Efficient text building
buildText :: [Text] -> Text  
buildText pieces = TL.toStrict $ TB.toLazyText $
  foldMap TB.fromText pieces

-- Streaming text processing
processTextFile :: FilePath -> IO Text
processTextFile file = do
  content <- T.readFile file  
  return $ T.unlines $ 
           filter (not . T.null) $ 
           map T.strip $ 
           T.lines content

-- Text vs String performance
-- String: Linked list of Char - O(n) for length, indexing
-- Text: UTF-16 encoded array - O(1) for length, fast operations
```

## Fusion and Stream Processing

### List Fusion
```haskell
-- GHC automatically fuses these operations into single pass
efficientPipeline :: [Int] -> [Int]
efficientPipeline = take 100 . filter even . map (*2) . filter (>= 0)

-- Build/foldr fusion
-- map f . map g === map (f . g)  -- Fused automatically
-- filter p . filter q === filter (\x -> p x && q x)

-- Prevent fusion when needed
{-# NOINLINE expensiveFunction #-}
expensiveFunction :: Int -> Int
expensiveFunction x = -- complex computation
```

### Conduit for Streaming
```haskell
import Data.Conduit
import qualified Data.Conduit.List as CL

-- Constant memory usage regardless of input size
processLargeDataset :: FilePath -> FilePath -> IO ()
processLargeDataset input output = runConduitRes $
  sourceFile input
  .| CB.lines  
  .| CL.map processLine
  .| CL.filter isValid
  .| CB.unlines
  .| sinkFile output

-- Memory-bounded operations
slidingWindow :: Int -> Conduit a IO [a]
slidingWindow size = do
  window <- lift $ newIORef []
  awaitForever $ \x -> do
    current <- lift $ readIORef window
    let newWindow = take size (x : current)  
    lift $ writeIORef window newWindow
    yield newWindow
```

## Concurrent Performance

### STM for Shared State
```haskell
import Control.Concurrent.STM

-- Lock-free shared counter
data SharedCounter = SharedCounter (TVar Int)

newSharedCounter :: IO SharedCounter  
newSharedCounter = SharedCounter <$> newTVarIO 0

incrementCounter :: SharedCounter -> STM ()
incrementCounter (SharedCounter var) = modifyTVar' var (+1)

-- Composable transactions
transfer :: TVar Int -> TVar Int -> Int -> STM ()
transfer from to amount = do
  fromBalance <- readTVar from
  when (fromBalance < amount) retry  -- Block until sufficient funds
  modifyTVar from (subtract amount)
  modifyTVar to (+ amount)
```

### Parallel Strategies
```haskell
import Control.Parallel.Strategies
import Control.Parallel

-- Parallel map with controlled evaluation
parMapChunk :: NFData b => Int -> (a -> b) -> [a] -> [b]  
parMapChunk chunkSize f xs = 
  concat $ map (map f) chunks `using` parList rdeepseq
  where
    chunks = chunksOf chunkSize xs

-- Speculative parallelism
fibPar :: Int -> Int  
fibPar n
  | n < 2 = n
  | otherwise = par a (pseq b (a + b))
  where
    a = fibPar (n-1)  -- Computed in parallel
    b = fibPar (n-2)  -- Computed sequentially

-- Divide and conquer parallelism  
mergeSort :: Ord a => [a] -> [a]
mergeSort xs
  | length xs <= 1 = xs
  | otherwise = merge left' right' `using` rpar left' `seq` rpar right'
  where
    (leftHalf, rightHalf) = splitAt (length xs `div` 2) xs
    left' = mergeSort leftHalf  
    right' = mergeSort rightHalf
```

### Async for Concurrency
```haskell
import Control.Concurrent.Async

-- Concurrent HTTP requests
fetchUrls :: [URL] -> IO [Response]
fetchUrls urls = mapConcurrently fetch urls

-- Timeout and cancellation
fetchWithTimeout :: Int -> URL -> IO (Maybe Response)
fetchWithTimeout seconds url = do
  result <- race (threadDelay (seconds * 1000000)) (fetch url)
  case result of
    Left _ -> return Nothing   -- Timeout
    Right response -> return (Just response)

-- Resource pooling  
withResourcePool :: Int -> (Resource -> IO a) -> [Input] -> IO [a]
withResourcePool poolSize action inputs = do  
  semaphore <- newQSem poolSize
  mapConcurrently (\input -> 
    bracket_ (waitQSem semaphore) (signalQSem semaphore) 
             (action input)) inputs
```

## Benchmarking with Criterion

### Basic Benchmarking
```haskell
import Criterion.Main

main :: IO ()
main = defaultMain
  [ bgroup "sorting"
    [ bench "sort 1000 ints" $ whnf sort [1000,999..1]
    , bench "sort 10000 ints" $ whnf sort [10000,9999..1]  
    ]
  , bgroup "data structures"
    [ bench "list sum" $ whnf sum [1..10000]
    , bench "vector sum" $ whnf V.sum (V.fromList [1..10000])
    ]
  ]

-- Different evaluation strategies
benchmarks :: [Benchmark]  
benchmarks =
  [ bench "whnf" $ whnf expensiveFunction input     -- Weak head normal form
  , bench "nf" $ nf expensiveFunction input         -- Full evaluation
  , bench "io" $ whnfIO (expensiveIO input)         -- IO actions
  ]
```

### Advanced Benchmarking
```haskell
-- Environment setup for consistent benchmarking
main :: IO ()  
main = defaultMain
  [ env (setupLargeDataset 100000) $ \ ~dataset ->
      bgroup "processing"
        [ bench "map" $ whnf (map expensiveFunction) dataset
        , bench "vector map" $ whnf (V.map expensiveFunction) (V.fromList dataset)
        ]
  ]

-- Benchmark groups with shared setup
setupLargeDataset :: Int -> IO [Int]
setupLargeDataset size = evaluate (force [1..size])

-- Memory usage benchmarking (with weigh package)
import Weigh

memoryBenchmarks :: IO ()
memoryBenchmarks = mainWith $ do
  func "list sum" sum [1..1000]
  func "vector sum" V.sum (V.fromList [1..1000])
```

## GHC Optimization Flags

### Recommended Optimization Flags
```cabal
-- In cabal file
ghc-options: 
  -O2                    -- Full optimization
  -Wall                  -- All warnings
  -Wcompat               -- Compatibility warnings  
  -Wincomplete-record-updates
  -Wincomplete-uni-patterns
  -Wmissing-home-modules
  -Wpartial-fields
  -Wredundant-constraints

-- For executables
if flag(release)
  ghc-options: 
    -threaded           -- Enable SMP support
    -rtsopts            -- Runtime system options
    -with-rtsopts=-N    -- Use all cores
    -O2 
    -funbox-strict-fields
    -fllvm              -- LLVM backend (if available)
```

### Runtime System Options
```bash
# Garbage collection tuning
./myapp +RTS -A32M      # Increase allocation area (default 1M)
./myapp +RTS -H128M     # Suggest heap size  
./myapp +RTS -G1        # Use single generation GC

# Parallel execution
./myapp +RTS -N4        # Use 4 cores
./myapp +RTS -N         # Use all available cores

# Profiling  
./myapp +RTS -p         # Time profiling
./myapp +RTS -h         # Heap profiling
./myapp +RTS -s         # GC statistics
```

## Common Performance Antipatterns

### Avoid These Patterns
```haskell
-- 1. String concatenation with (++)
badConcat :: [String] -> String  
badConcat = foldr (++) ""  -- O(n²) complexity

-- Fix: Use Text or Builder
goodConcat :: [Text] -> Text
goodConcat = T.concat

-- 2. Repeated list indexing  
badIndexing :: [a] -> [a]
badIndexing xs = [xs !! i | i <- [0..length xs - 1]]

-- Fix: Use vector for indexing or avoid indexing
goodIndexing :: [a] -> [a]  
goodIndexing = id  -- Identity - or use Vector for real indexing needs

-- 3. Excessive laziness in strict contexts
badSum :: [Int] -> Int
badSum [] = 0
badSum (x:xs) = x + badSum xs  -- Stack overflow for large lists

-- Fix: Use accumulator with strictness
goodSum :: [Int] -> Int  
goodSum = go 0
  where
    go !acc [] = acc
    go !acc (x:xs) = go (acc + x) xs

-- 4. Monadic sequence without strictness
badSequence :: [IO Int] -> IO [Int]  
badSequence = sequence  -- Can build up large thunk chains

-- Fix: Force evaluation  
goodSequence :: [IO Int] -> IO [Int]
goodSequence [] = return []
goodSequence (action:actions) = do
  !result <- action
  rest <- goodSequence actions  
  return (result:rest)
```

Performance in Haskell requires understanding the evaluation model and choosing appropriate strictness, data structures, and concurrency primitives for your use case.

path: references/cabal-guide.md
content: # Cabal Project Guide

## Cabal File Format

### Basic Package Structure
```cabal
cabal-version: 3.0  -- Use latest cabal format
name: myproject
version: 0.1.0.0
synopsis: A short description
description: 
    A longer description of the project.
    .
    This can span multiple paragraphs and include formatting.

license: MIT
license-file: LICENSE
author: Your Name
maintainer: [REDACTED_SECRET]
homepage: https://github.com/yourusername/myproject
bug-reports: https://github.com/yourusername/myproject/issues
copyright: 2024 Your Name
category: Development
build-type: Simple

-- Additional metadata
extra-source-files: 
    README.md
    CHANGELOG.md
extra-doc-files:
    docs/*.md

source-repository head
  type: git
  location: https://github.com/yourusername/myproject
```

### Library Section
```cabal
library
  -- Expose modules for library users
  exposed-modules:
      MyProject
      MyProject.Core
      MyProject.Utils
  
  -- Internal modules not exposed to users
  other-modules:
      MyProject.Internal
      MyProject.Types
  
  -- Re-export modules from dependencies
  reexported-modules:
      Data.Text as MyProject.Text,
      Control.Monad as MyProject.Monad
  
  -- Source directories
  hs-source-dirs: src
  
  -- Language and extensions
  default-language: Haskell2010
  default-extensions:
      DerivingStrategies  
      LambdaCase
  
  -- GHC options for all modules
  ghc-options: 
      -Wall
      -Wcompat
      -Widentities
      -Wincomplete-record-updates
      -Wincomplete-uni-patterns
      -Wmissing-home-modules
      -Wpartial-fields
      -Wredundant-constraints
  
  -- Dependencies with version bounds
  build-depends:
      base >= 4.7 && < 5,
      text >= 1.2 && < 3,
      aeson >= 2.0 && < 3,
      containers >= 0.6 && < 0.8
```

### Executable Section
```cabal
executable myproject-exe
  -- Main module
  main-is: Main.hs
  
  -- Source directory
  hs-source-dirs: app
  
  -- Language settings (inherit from common)
  default-language: Haskell2010
  default-extensions:
      OverloadedStrings
  
  -- Optimization for executables
  ghc-options:
      -Wall
      -threaded
      -rtsopts
      -with-rtsopts=-N
      -O2
  
  -- Dependencies (including our own library)
  build-depends:
      base,
      myproject,
      optparse-applicative >= 0.16,
      text
```

### Test Suites
```cabal
test-suite myproject-test
  type: exitcode-stdio-1.0
  main-is: Spec.hs
  hs-source-dirs: test
  
  default-language: Haskell2010
  default-extensions:
      OverloadedStrings
  
  ghc-options:
      -Wall
      -threaded
      -rtsopts
      -with-rtsopts=-N
  
  build-depends:
      base,
      myproject,
      hspec >= 2.7,
      QuickCheck >= 2.14,
      text
  
  -- Discover test modules automatically
  build-tool-depends:
      hspec-discover:hspec-discover

-- Property-based test suite
test-suite myproject-properties
  type: exitcode-stdio-1.0
  main-is: Properties.hs
  hs-source-dirs: test-properties
  
  default-language: Haskell2010
  
  build-depends:
      base,
      myproject,
      QuickCheck,
      quickcheck-instances
```

### Benchmarks
```cabal
benchmark myproject-bench
  type: exitcode-stdio-1.0
  main-is: Main.hs  
  hs-source-dirs: bench
  
  default-language: Haskell2010
  
  ghc-options:
      -Wall
      -O2
      -threaded
      -rtsopts
      -with-rtsopts=-N
  
  build-depends:
      base,
      myproject,
      criterion >= 1.5,
      deepseq
```

## Common Stanzas

### Sharing Configuration
```cabal
cabal-version: 3.0
name: myproject

-- Define common options
common shared-properties
  default-language: Haskell2010
  default-extensions:
      OverloadedStrings
      DerivingStrategies
      LambdaCase
      MultiWayIf
      TypeApplications
  
  ghc-options:
      -Wall
      -Wcompat
      -Widentities
      -Wincomplete-record-updates
      -Wincomplete-uni-patterns
      -Wmissing-home-modules
      -Wpartial-fields
      -Wredundant-constraints
  
  build-depends:
      base >= 4.7 && < 5

common executable-properties
  import: shared-properties
  ghc-options:
      -threaded
      -rtsopts
      -with-rtsopts=-N
      -O2

-- Use in components
library
  import: shared-properties
  exposed-modules: MyProject
  hs-source-dirs: src
  
executable myproject-exe
  import: executable-properties
  main-is: Main.hs
  hs-source-dirs: app
  build-depends: myproject
```

## Multi-Package Projects

### cabal.project Structure
```
myproject/
├── cabal.project          -- Project configuration
├── myproject-core/
│   ├── myproject-core.cabal
│   └── src/
├── myproject-web/  
│   ├── myproject-web.cabal
│   └── src/
├── myproject-cli/
│   ├── myproject-cli.cabal
│   └── src/
└── test-utils/
    ├── test-utils.cabal
    └── src/
```

### cabal.project Configuration
```cabal
-- cabal.project
packages: 
  myproject-core
  myproject-web
  myproject-cli
  test-utils

-- Global flags
tests: True
benchmarks: False
documentation: False

-- Package-specific configuration
package myproject-core
  tests: True
  benchmarks: True
  documentation: True

package myproject-web  
  flags: +development

-- Dependency overrides
constraints: 
  aeson >= 2.0,
  text >= 2.0

-- Allow newer versions
allow-newer: 
  base,
  *:base

-- Source repositories for unreleased packages
source-repository-package
  type: git
  location: https://github.com/example/some-library
  tag: main

-- Local source overrides  
source-repository-package
  type: git
  location: file:///path/to/local/repo
```

### Cross-Package Dependencies
```cabal
-- myproject-web.cabal
library
  build-depends:
      base,
      myproject-core,  -- Local dependency
      servant >= 0.18

-- myproject-cli.cabal  
executable myproject-cli
  build-depends:
      base,
      myproject-core,  -- Shared core
      optparse-applicative

-- test-utils.cabal (shared test utilities)
library
  exposed-modules:
      Test.MyProject.Utils
      Test.MyProject.Generators
  build-depends:
      base,
      myproject-core,
      QuickCheck,
      hspec
```

## Conditional Compilation

### Flags
```cabal
-- Define flags
flag development
  description: Enable development features
  default: False
  manual: True

flag static
  description: Build static executables
  default: False
  manual: True

library
  -- Conditional modules
  if flag(development)
    exposed-modules: MyProject.Debug
    build-depends: debug >= 0.1
  else
    build-depends: 
  
  -- Conditional compilation
  if flag(static)
    ghc-options: -static -optl-static
    cc-options: -static

-- Conditional dependencies based on OS
if os(windows)
  build-depends: Win32 >= 2.3
if os(linux) || os(darwin)  
  build-depends: unix >= 2.7

-- GHC version conditionals
if impl(ghc >= 9.2)
  default-extensions: OverloadedRecordDot
if impl(ghc < 9.0)
  build-depends: semigroups
```

### Build Configuration
```cabal
-- Architecture-specific options
if arch(x86_64)
  cpp-options: -DARCH_X86_64
  cc-options: -march=native

-- Custom preprocessor  
if flag(use-template-haskell)
  build-depends: template-haskell >= 2.16
  other-extensions: TemplateHaskell
```

## Advanced Features

### Custom Setup
```cabal
-- myproject.cabal
build-type: Custom
custom-setup
  setup-depends:
      base >= 4.7,
      Cabal >= 3.0,
      directory
```

```haskell
-- Setup.hs
import Distribution.Simple
import Distribution.Simple.Setup
import Distribution.Simple.LocalBuildInfo  
import System.Directory

main = defaultMainWithHooks simpleUserHooks
  { preBuild = \args flags -> do
      putStrLn "Running custom pre-build step"
      createDirectoryIfMissing True "generated"
      writeFile "generated/BuildInfo.hs" buildInfoModule
      preBuild simpleUserHooks args flags
  }

buildInfoModule :: String
buildInfoModule = unlines
  [ "module BuildInfo where"
  , "buildTime :: String"  
  , "buildTime = " ++ show "2024-01-01"
  ]
```

### Foreign Libraries
```cabal
foreign-library myproject-c
  type: native-shared
  lib-version-info: 1:0:0
  
  if os(Windows)
    type: native-static
    lib-version-info: 1
  
  other-modules: MyProject.Foreign
  build-depends: base, myproject
  hs-source-dirs: cbits
  c-sources: cbits/wrapper.c
  include-dirs: cbits
  install-includes: myproject.h
```

## Dependency Management

### Version Bounds Best Practices
```cabal
build-depends:
  -- Core dependencies - conservative bounds
  base >= 4.14 && < 4.20,
  text >= 1.2 && < 2.2,
  containers >= 0.6 && < 0.8,
  
  -- Stable packages - wider bounds OK
  aeson >= 1.5 && < 3,
  
  -- Unstable packages - tight bounds
  new-experimental-lib >= 0.1.2 && < 0.2,
  
  -- Internal packages - any version
  myproject-core
```

### Dependency Categories
```cabal
library
  -- Public API dependencies (affect downstream)
  build-depends:
      base,
      text,
      aeson
  
  -- Private implementation dependencies
  build-depends:
      containers,
      unordered-containers,
      hashable
  
  -- Development-only dependencies
  build-depends:
      -- None in library!

executable myproject-exe  
  -- Can have looser bounds
  build-depends:
      base,
      myproject,
      optparse-applicative >= 0.15

test-suite tests
  -- Test-specific dependencies
  build-depends:
      base,
      myproject,
      hspec >= 2.7,
      QuickCheck >= 2.13,
      temporary,
      directory
```

## Publishing to Hackage

### Package Preparation
```cabal
cabal-version: 3.0
name: myproject
version: 1.0.0.0

-- Complete metadata required
synopsis: One-line description (< 80 chars)
description:
    Longer description explaining what the package does.
    .
    * Feature 1
    * Feature 2
    .
    Example usage:
    .
    > import MyProject
    > result = doSomething "input"

license: MIT
license-file: LICENSE
author: Your Name <[REDACTED_SECRET]>
maintainer: [REDACTED_SECRET]
copyright: 2024 Your Name  
homepage: https://github.com/yourusername/myproject
bug-reports: https://github.com/yourusername/myproject/issues
category: Development

-- Version bounds for Hackage
tested-with: 
    GHC == 8.10.7,
    GHC == 9.0.2,
    GHC == 9.2.8,
    GHC == 9.4.8,
    GHC == 9.6.3

extra-source-files:
    README.md
    CHANGELOG.md
    examples/*.hs
```

### Release Checklist
```bash
# 1. Update version and changelog
# 2. Test with multiple GHC versions
cabal test --enable-tests

# 3. Generate documentation
cabal haddock --haddock-all

# 4. Check package
cabal check

# 5. Build source distribution
cabal sdist

# 6. Test the sdist
cd dist-newstyle/sdist
tar -xf myproject-1.0.0.0.tar.gz
cd myproject-1.0.0.0
cabal build

# 7. Upload to Hackage
cabal upload myproject-1.0.0.0.tar.gz

# 8. Upload documentation (optional)
cabal upload --documentation myproject-1.0.0.0.tar.gz
```

### Hackage Metadata
```cabal
-- Hackage categories (choose appropriate ones)
category: 
    Data,
    Development,
    Web,
    System,
    Network

-- Package stability
stability: experimental  -- or alpha, provisional, stable

-- Hackage tags (in description)
description:
    Tags: json, web, api, cli
```

## Continuous Integration

### GitHub Actions
```yaml
# .github/workflows/ci.yml
name: CI
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macOS-latest, windows-latest]
        ghc: ['8.10', '9.0', '9.2', '9.4', '9.6']
        
    steps:
    - uses: actions/checkout@v3
    
    - uses: haskell/actions/setup@v2
      with:
        ghc-version: ${{ matrix.ghc }}
        cabal-version: 'latest'
        
    - name: Cache dependencies  
      uses: actions/cache@v3
      with:
        path: ~/.cabal
        key: ${{ runner.os }}-${{ matrix.ghc }}-cabal
        
    - name: Build dependencies
      run: cabal build --dependencies-only --enable-tests
      
    - name: Build
      run: cabal build --enable-tests
      
    - name: Test
      run: cabal test --enable-tests
      
    - name: Documentation
      run: cabal haddock --haddock-all
```

### Nix CI Integration
```yaml
# .github/workflows/nix.yml
name: Nix CI
on: [push, pull_request]

jobs:
  nix-build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - uses: cachix/install-nix-action@v18
    - uses: cachix/cachix-action@v12
      with:
        name: myproject
        authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
        
    - name: Build with Nix
      run: nix build
      
    - name: Run tests
      run: nix run .#test
```

This comprehensive guide covers modern Cabal usage patterns, from simple packages to complex multi-package projects with proper dependency management and CI/CD integration.


path: references/common-patterns.md
content: # Common Patterns and Idioms

## Monad Transformers

### The Transformer Stack
```haskell
-- Building application context
type App = ReaderT AppConfig (ExceptT AppError (StateT AppState IO))

runApp :: AppConfig -> AppState -> App a -> IO (Either AppError (a, AppState))
runApp config initState action = 
  runStateT (runExceptT (runReaderT action config)) initState

-- Automatic lifting with mtl constraints
fetchUser :: (MonadReader AppConfig m, MonadError AppError m, MonadIO m) 
          => UserId -> m User
fetchUser userId = do
  dbConn <- asks configDbConnection
  result <- liftIO $ queryUser dbConn userId
  case result of
    Nothing -> throwError (UserNotFound userId)
    Just user -> pure user
```

### Common Transformer Combinations
```haskell
-- Web application stack
type WebM = ReaderT Request (ExceptT HttpError (WriterT [LogMessage] IO))

-- Parser with backtracking
type Parser = StateT ParseState (ExceptT ParseError (Writer [Warning]))

-- STM-based concurrent computation  
type ConcurrentApp = ReaderT Config (ExceptT AppError STM)
```

## MTL Style vs Transformers

### MTL Style (Preferred)
```haskell
-- Abstract over concrete transformers
class Monad m => MonadUser m where
  getUser :: UserId -> m (Maybe User)
  saveUser :: User -> m ()

instance MonadUser App where
  getUser userId = do
    conn <- asks dbConnection
    liftIO $ Database.queryUser conn userId
    
  saveUser user = do
    conn <- asks dbConnection  
    liftIO $ Database.insertUser conn user

-- Easy to test with pure instances
instance MonadUser (State [User]) where
  getUser userId = gets (find (\u -> userId u == userId))
  saveUser user = modify (user :)
```

### Avoiding Transformer Stack Explosion
```haskell
-- Instead of ReaderT Config (StateT State (ExceptT Error IO))
-- Use a single transformer with combined context
data AppContext = AppContext
  { appConfig :: Config
  , appState :: IORef State  -- Mutable state via IORef
  }

type App = ReaderT AppContext (ExceptT Error IO)

getState :: App State
getState = do
  stateRef <- asks appState
  liftIO $ readIORef stateRef

modifyState :: (State -> State) -> App ()  
modifyState f = do
  stateRef <- asks appState
  liftIO $ modifyIORef stateRef f
```

## ReaderT Pattern

### Configuration Management
```haskell
data AppConfig = AppConfig
  { configDatabase :: DatabaseConfig
  , configLogging :: LogConfig
  , configAuth :: AuthConfig
  } deriving stock (Generic)
    deriving anyclass (FromJSON)

-- Environment-specific configs
loadConfig :: Environment -> IO AppConfig
loadConfig env = do
  configFile <- getConfigPath env
  eitherDecodeFileStrict configFile >>= either throwIO pure

-- Dependency injection via Reader
class HasDatabase env where
  databaseConfig :: env -> DatabaseConfig

class HasLogging env where  
  loggingConfig :: env -> LogConfig

instance HasDatabase AppConfig where
  databaseConfig = configDatabase

instance HasLogging AppConfig where
  loggingConfig = configLogging

-- Functions depend only on what they need
connectDb :: (MonadReader env m, HasDatabase env, MonadIO m) => m Connection
connectDb = do
  dbConfig <- asks databaseConfig
  liftIO $ connect dbConfig
```

## Effect Systems

### Free Monads for Testability
```haskell
{-# LANGUAGE DeriveFunctor, TemplateHaskell #-}

data UserAction a
  = GetUser UserId (User -> a)
  | SaveUser User a  
  | DeleteUser UserId a
  deriving Functor

type UserProgram = Free UserAction

-- Smart constructors
getUser :: UserId -> UserProgram User
getUser userId = liftF $ GetUser userId id

saveUser :: User -> UserProgram ()
saveUser user = liftF $ SaveUser user ()

-- Program composition
updateUserEmail :: UserId -> Email -> UserProgram ()
updateUserEmail userId newEmail = do
  user <- getUser userId
  let updatedUser = user { userEmail = newEmail }
  saveUser updatedUser

-- Different interpreters for different contexts
interpretIO :: UserProgram a -> ReaderT DatabaseConfig IO a
interpretIO = iterM $ \case
  GetUser userId next -> do
    conn <- ask
    user <- liftIO $ queryUser conn userId
    next user
  SaveUser user next -> do
    conn <- ask  
    liftIO $ insertUser conn user
    next
  DeleteUser userId next -> do
    conn <- ask
    liftIO $ deleteUser conn userId
    next

-- Pure interpreter for testing
interpretPure :: UserProgram a -> State [User] a
interpretPure = iterM $ \case
  GetUser userId next -> do
    users <- get
    let user = find ((== userId) . userId) users
    next user
  SaveUser user next -> do
    modify (user :)
    next
```

### Extensible Effects with Polysemy
```haskell
{-# LANGUAGE TemplateHaskell, GADTs, FlexibleContexts #-}
import Polysemy

data UserEffect m a where
  GetUser :: UserId -> UserEffect m (Maybe User)  
  SaveUser :: User -> UserEffect m ()

makeSem ''UserEffect  -- Generates smart constructors

-- Business logic independent of interpretation
updateUserProgram :: Member UserEffect r => UserId -> Email -> Sem r ()
updateUserProgram userId newEmail = do
  maybeUser <- getUser userId
  case maybeUser of
    Nothing -> pure ()  -- or throw error
    Just user -> saveUser user { userEmail = newEmail }

-- Database interpreter
runUserDb :: Member (Embed IO) r => DatabaseConfig -> Sem (UserEffect : r) a -> Sem r a
runUserDb config = interpret $ \case
  GetUser userId -> embed $ queryUserDb config userId
  SaveUser user -> embed $ saveUserDb config user

-- Pure interpreter  
runUserPure :: Sem (UserEffect : r) a -> Sem r a
runUserPure = interpret $ \case
  GetUser userId -> pure Nothing  -- simplified
  SaveUser _ -> pure ()
```

## Optics and Lenses

### Basic Lens Usage
```haskell
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens

data Address = Address
  { _street :: Text
  , _city :: Text  
  , _country :: Text
  } deriving (Show, Eq)

data Person = Person  
  { _name :: Text
  , _age :: Int
  , _address :: Address
  } deriving (Show, Eq)

makeLenses ''Address
makeLenses ''Person

-- Deep updates without nested record syntax
updateCity :: Text -> Person -> Person  
updateCity newCity = address . city .~ newCity

-- Multiple updates
movePerson :: Text -> Text -> Person -> Person
movePerson newStreet newCity = 
  (address . street .~ newStreet) . (address . city .~ newCity)

-- Traversals over structures
allCities :: [Person] -> [Text]
allCities people = people ^.. traverse . address . city
```

### Prisms for Sum Types
```haskell
data Result a = Success a | Failure Text deriving (Show, Eq)

makePrisms ''Result  -- Generates _Success and _Failure prisms

-- Pattern matching with prisms
processResult :: Result Int -> Text
processResult result = case result of
  r | Just value <- r ^? _Success -> "Got: " <> show value  
  r | Just err <- r ^? _Failure -> "Error: " <> err
  _ -> "Unknown"  -- Won't happen but exhaustiveness checker

-- Traversal over successes only
incrementSuccesses :: [Result Int] -> [Result Int]
incrementSuccesses = map (_Success %~ (+ 1))
```

### Custom Optics
```haskell
-- Iso for bidirectional conversion
userNameIso :: Iso' User Text
userNameIso = iso getUserName (\name -> User name)

-- Lens for computed properties
ageBracket :: Lens' Person Text  
ageBracket = lens getAgeBracket setAgeBracket
  where
    getAgeBracket person
      | person ^. age < 18 = "Minor"
      | person ^. age < 65 = "Adult"  
      | otherwise = "Senior"
    setAgeBracket person bracket = person  -- Read-only in practice
```

## Newtype Patterns

### Domain-Driven Design with Newtypes
```haskell
newtype UserId = UserId Int
  deriving newtype (Show, Eq, Ord, Hashable, ToJSON, FromJSON)
  deriving stock (Generic)

newtype EmailAddress = EmailAddress Text
  deriving newtype (Show, Eq, IsString)
  deriving stock (Generic)

-- Smart constructors for validation
mkEmailAddress :: Text -> Either ValidationError EmailAddress
mkEmailAddress email
  | "@" `Text.isInfixOf` email = Right (EmailAddress email)
  | otherwise = Left InvalidEmail

-- Prevents mixing up parameters
sendEmail :: EmailAddress -> Subject -> Body -> IO ()
-- Can't accidentally pass UserId where EmailAddress expected
```

### Phantom Types for State
```haskell
data ValidationState = Validated | Unvalidated

newtype Form (s :: ValidationState) = Form (Map Text Text)
  deriving newtype (Show, Eq)

type ValidatedForm = Form 'Validated
type UnvalidatedForm = Form 'Unvalidated

validate :: UnvalidatedForm -> Either [ValidationError] ValidatedForm
validate (Form fields) = do
  validateRequired fields
  validateFormats fields  
  pure (Form fields)

-- Only validated forms can be processed  
processForm :: ValidatedForm -> IO ()
processForm (Form fields) = do
  -- Process with confidence that validation occurred
  pure ()
```

## Type-Level Programming Patterns

### Singleton Pattern for Runtime Type Info
```haskell
{-# LANGUAGE GADTs, DataKinds, KindSignatures #-}

data Format = JSON | XML | YAML

data SFormat (f :: Format) where
  SJSON :: SFormat 'JSON
  SXML :: SFormat 'XML
  SYAML :: SFormat 'YAML

class SingI (f :: Format) where
  sing :: SFormat f

instance SingI 'JSON where sing = SJSON
instance SingI 'XML where sing = SXML  
instance SingI 'YAML where sing = SYAML

-- Format-specific encoding
encode :: SingI f => SFormat f -> Value -> ByteString
encode SJSON = Aeson.encode
encode SXML = xmlEncode  
encode SYAML = Yaml.encode

-- Type determines runtime behavior
encodeAs :: forall f. SingI f => Value -> ByteString  
encodeAs = encode (sing @f)
```

### HList for Heterogeneous Collections
```haskell
data HList :: [*] -> * where
  HNil :: HList '[]
  HCons :: a -> HList as -> HList (a ': as)

type family All (c :: * -> Constraint) (xs :: [*]) :: Constraint where
  All c '[] = ()
  All c (x ': xs) = (c x, All c xs)

-- Operations constrained to work on all elements
hmap :: (forall a. c a => a -> b) -> HList xs -> [b]
hmap _ HNil = []
hmap f (HCons x xs) = f x : hmap f xs

showAll :: All Show xs => HList xs -> [String]
showAll = hmap show

example :: HList '[Int, Bool, Text]
example = HCons 42 (HCons True (HCons "hello" HNil))

-- Type-safe element access
class HIndex (n :: Nat) (xs :: [*]) where
  type Index n xs :: *
  hindex :: HList xs -> Index n xs
```

## Error Handling Patterns

### Validation Applicative
```haskell
data ValidationError = EmailInvalid | NameTooShort | AgeTooLow
  deriving (Show, Eq)

-- Accumulate all errors, not just first
data Validation e a = Failure [e] | Success a
  deriving (Show, Eq, Functor)

instance Semigroup e => Applicative (Validation e) where
  pure = Success
  Success f <*> Success a = Success (f a)
  Success _ <*> Failure es = Failure es
  Failure es <*> Success _ = Failure es  
  Failure es1 <*> Failure es2 = Failure (es1 <> es2)

-- Validate all fields
validateUser :: Text -> Text -> Int -> Validation ValidationError User
validateUser email name age = User 
  <$> validateEmail email
  <*> validateName name  
  <*> validateAge age
  where
    validateEmail e | "@" `Text.isInfixOf` e = Success e
                   | otherwise = Failure [EmailInvalid]
    
    validateName n | Text.length n >= 2 = Success n
                  | otherwise = Failure [NameTooShort]
    
    validateAge a | a >= 0 = Success a
                 | otherwise = Failure [AgeTooLow]
```

### Exception Hierarchy
```haskell
data AppException 
  = DatabaseException DatabaseError
  | ValidationException ValidationError  
  | NetworkException NetworkError
  deriving stock (Show, Generic)
  deriving anyclass (Exception)

-- Specific handlers for different error types
handleApp :: IO a -> IO (Either AppException a)
handleApp action = do
  result <- try action
  pure $ case result of
    Left (e :: AppException) -> Left e
    Right value -> Right value

-- Selective catching
catchDatabase :: IO a -> (DatabaseError -> IO a) -> IO a
catchDatabase action handler = action `catch` \case
  DatabaseException dbErr -> handler dbErr
  otherErr -> throwIO otherErr
```

## Concurrency Patterns

### STM for Transactional Memory
```haskell
import Control.Concurrent.STM

data Account = Account { balance :: TVar Int }

transfer :: Account -> Account -> Int -> STM ()
transfer from to amount = do
  fromBalance <- readTVar (balance from)
  toBalance <- readTVar (balance to)
  
  when (fromBalance < amount) $ 
    throwSTM InsufficientFunds
    
  writeTVar (balance from) (fromBalance - amount)
  writeTVar (balance to) (toBalance + amount)

-- Atomically execute transaction
safeTransfer :: Account -> Account -> Int -> IO (Either SomeException ())
safeTransfer from to amount = try $ atomically $ transfer from to amount
```

### Async Coordination
```haskell
import Control.Concurrent.Async

-- Parallel processing with error handling
processParallel :: [Input] -> IO [Either Error Result]
processParallel inputs = do
  asyncs <- mapM (async . processOne) inputs
  mapM waitCatch asyncs
  where
    processOne input = do
      result <- process input
      evaluate result  -- Force evaluation

-- Race multiple operations
firstSuccess :: [IO a] -> IO (Maybe a)
firstSuccess actions = do
  asyncs <- mapM async actions
  result <- waitAnySuccess asyncs
  mapM_ cancel asyncs
  pure result
```

These patterns represent the core idioms of idiomatic Haskell development, emphasizing type safety, composability, and explicit error handling.

path: references/ghc-extensions.md
content: # GHC Extensions Reference

## Essential Extensions (Always Enable)

### OverloadedStrings
```haskell
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.Text as T

-- String literals become polymorphic
text :: Text
text = "hello"  -- Automatically Text.pack "hello"

bytestring :: ByteString  
bytestring = "hello"  -- Automatically ByteString.pack "hello"

-- Works with any IsString instance
class IsString a where
  fromString :: String -> a
```

### DerivingStrategies
```haskell
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}

newtype UserId = UserId Int
  deriving stock (Show, Eq, Ord)      -- Use built-in deriving
  deriving newtype (Hashable, ToJSON) -- Unwrap newtype
  deriving anyclass (FromJSON)        -- Use default implementation

-- Always be explicit about deriving strategy
data User = User Text Int
  deriving stock (Show, Eq, Generic)
  deriving anyclass (ToJSON, FromJSON)
```

### LambdaCase
```haskell
{-# LANGUAGE LambdaCase #-}

-- Concise pattern matching in lambda
parseResult :: Text -> Either Error Value
parseResult input = case parse input of
  \case  -- No need to name the parameter
    Left err -> Left (ParseError err)
    Right val -> Right val

-- Useful with higher-order functions
processResults :: [Either Error Value] -> [Value]
processResults = mapMaybe $ \case
  Left _ -> Nothing
  Right val -> Just val
```

### MultiWayIf  
```haskell
{-# LANGUAGE MultiWayIf #-}

gradeToLetter :: Int -> Char
gradeToLetter grade = if
  | grade >= 90 -> 'A'
  | grade >= 80 -> 'B'  
  | grade >= 70 -> 'C'
  | grade >= 60 -> 'D'
  | otherwise -> 'F'
```

## Type System Extensions

### RankNTypes (Higher-Rank Polymorphism)
```haskell
{-# LANGUAGE RankNTypes #-}

-- Functions that take polymorphic functions as arguments
applyToEach :: (forall a. Show a => a -> String) -> (Int, Bool) -> (String, String)
applyToEach f (x, y) = (f x, f y)

-- Continuation-passing style
withResource :: (forall a. Resource -> IO a) -> IO a
withResource action = bracket acquireResource releaseResource action

-- Church encoding  
type ChurchInt = forall a. (a -> a) -> a -> a

churchZero :: ChurchInt
churchZero = \f x -> x

churchOne :: ChurchInt  
churchOne = \f x -> f x
```

### ExistentialQuantification
```haskell
{-# LANGUAGE ExistentialQuantification #-}

-- Hide specific type information
data SomeException = forall e. Exception e => SomeException e

-- Type-erased collections
data Widget = forall w. Renderable w => Widget w

renderWidget :: Widget -> IO ()
renderWidget (Widget w) = render w

-- Existential with constraints
data SomeStorable = forall a. Storable a => SomeStorable a

sizeOfSomeStorable :: SomeStorable -> Int  
sizeOfSomeStorable (SomeStorable x) = sizeOf x
```

## Constraint Extensions

### FlexibleContexts & FlexibleInstances
```haskell
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}

-- Allow complex constraints
complexFunction :: (MonadReader Config m, MonadError AppError m, MonadIO m) 
                => UserId -> m User
complexFunction userId = do
  config <- ask
  -- ... implementation

-- Allow overlapping instances (be careful!)  
instance Show (a -> b) where
  show _ = "<function>"

instance {-# OVERLAPPING #-} Show (Int -> Int) where
  show _ = "<int function>"
```

## Pattern Extensions

### PatternSynonyms
```haskell
{-# LANGUAGE PatternSynonyms #-}

-- Bidirectional pattern synonyms
pattern Empty :: [a]
pattern Empty = []

pattern NonEmpty :: a -> [a] -> [a]  
pattern NonEmpty x xs = x:xs

{-# COMPLETE Empty, NonEmpty #-}  -- Tell GHC patterns are exhaustive

safeLast :: [a] -> Maybe a
safeLast Empty = Nothing
safeLast (NonEmpty x Empty) = Just x  
safeLast (NonEmpty _ xs) = safeLast xs

-- Unidirectional patterns for complex matching
pattern AuthenticatedUser :: Text -> Request -> User
pattern AuthenticatedUser username request <- 
  (lookupUser request -> Just (User username _ _))
```

## Deriving Extensions

### DeriveGeneric
```haskell
{-# LANGUAGE DeriveGeneric #-}
import GHC.Generics

data User = User 
  { name :: Text
  , age :: Int
  } deriving (Generic)

-- Automatic JSON instances
instance ToJSON User
instance FromJSON User

-- Generic programming
genericShow :: (Generic a, GShow (Rep a)) => a -> String
genericShow = gshow . from
```

### DeriveFunctor, DeriveFoldable, DeriveTraversable
```haskell
{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}

data Tree a = Leaf a | Branch (Tree a) (Tree a)
  deriving (Functor, Foldable, Traversable)

-- Automatic instances generated
mapTree :: (a -> b) -> Tree a -> Tree b
mapTree = fmap  -- Uses derived Functor

sumTree :: Num a => Tree a -> a  
sumTree = sum   -- Uses derived Foldable
```

### DeriveAnyClass
```haskell
{-# LANGUAGE DeriveAnyClass #-}

class Serializable a where
  serialize :: a -> ByteString
  deserialize :: ByteString -> Either String a
  
-- Use default implementations
data Point = Point Int Int
  deriving (Generic, Serializable)  -- Uses DeriveAnyClass
```

## Template Haskell Extensions

### TemplateHaskell
```haskell
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens

data User = User
  { _userName :: Text
  , _userAge :: Int
  , _userEmail :: Text  
  } deriving (Show, Eq)

makeLenses ''User  -- Generates lenses at compile time

-- Usage  
updateUserAge :: Int -> User -> User
updateUserAge newAge = userAge .~ newAge
```

## Syntax Extensions

### RecordWildCards
```haskell
{-# LANGUAGE RecordWildCards #-}

data User = User 
  { userName :: Text
  , userAge :: Int
  , userEmail :: Text
  }

-- Automatic pattern matching and binding
processUser :: User -> Text
processUser User{..} = userName <> " (" <> show userAge <> ")"

-- In construction  
createUser :: Text -> Int -> Text -> User
createUser userName userAge userEmail = User{..}
```

### BangPatterns
```haskell
{-# LANGUAGE BangPatterns #-}

-- Force evaluation in pattern matching
strictMap :: (a -> b) -> [a] -> [b]
strictMap _ [] = []
strictMap f (!x:xs) = f x : strictMap f xs

-- Strict let bindings
processData :: [Int] -> Int  
processData xs = 
  let !len = length xs    -- Evaluated immediately
      !sum' = sum xs
  in sum' `div` len
```

## Extension Warnings

### Safe vs Unsafe Extensions
```haskell
-- Generally safe extensions
{-# LANGUAGE OverloadedStrings #-}    -- Always safe
{-# LANGUAGE LambdaCase #-}           -- Pure syntax
{-# LANGUAGE DerivingStrategies #-}   -- Explicit is better

-- Use with caution
{-# LANGUAGE UndecidableInstances #-}  -- Can cause infinite loops
{-# LANGUAGE IncoherentInstances #-}   -- Breaks type system guarantees  
{-# LANGUAGE OverlappingInstances #-}  -- Use OVERLAPPING pragma instead
```

These extensions unlock Haskell's advanced type system features while maintaining the language's strong guarantees about correctness and performance.


path: references/libraries.md
content: # Essential Library Ecosystem

## Foundational Data Structures

### Vector - Efficient Arrays
```haskell
import qualified Data.Vector as V
import qualified Data.Vector.Unboxed as U
import qualified Data.Vector.Storable as S

-- Boxed vectors for complex types
userVector :: V.Vector User
userVector = V.fromList [alice, bob, charlie]

-- Unboxed vectors for primitives (memory efficient)
scores :: U.Vector Int  
scores = U.fromList [85, 92, 78, 96]

-- High-performance operations
averageScore :: U.Vector Int -> Double
averageScore scores = 
  fromIntegral (U.sum scores) / fromIntegral (U.length scores)

-- Parallel processing
parallelSum :: U.Vector Int -> Int
parallelSum vec = runEval $ do
  let (left, right) = U.splitAt (U.length vec `div` 2) vec
  leftSum <- rpar (U.sum left)  
  rightSum <- rseq (U.sum right)
  pure (leftSum + rightSum)
```

### Containers - Pure Data Structures
```haskell
import qualified Data.Map.Strict as Map
import qualified Data.Set as Set
import qualified Data.IntMap.Strict as IntMap
import qualified Data.Sequence as Seq

-- Maps for key-value lookups
userMap :: Map UserId User
userMap = Map.fromList [(UserId 1, alice), (UserId 2, bob)]

lookupUser :: UserId -> Map UserId User -> Maybe User
lookupUser = Map.lookup

-- Sets for membership testing
activeUsers :: Set UserId
activeUsers = Set.fromList [UserId 1, UserId 3, UserId 7]

isActive :: UserId -> Bool
isActive userId = userId `Set.member` activeUsers

-- IntMap for integer keys (more efficient)
scoreMap :: IntMap Int
scoreMap = IntMap.fromList [(1, 100), (2, 85), (3, 92)]

-- Sequence for efficient append/prepend
messageQueue :: Seq Message  
messageQueue = Seq.empty
  |> message1
  |> message2
  |> message3
```

### Unordered-Containers - Hash-Based Structures
```haskell
import qualified Data.HashMap.Strict as HM
import qualified Data.HashSet as HS
import Data.Hashable

-- Faster insertion/lookup for non-ordered operations
userCache :: HashMap Text User
userCache = HM.fromList [("alice", alice), ("bob", bob)]

-- Custom Hashable instances
instance Hashable UserId where
  hashWithSalt salt (UserId n) = hashWithSalt salt n

-- HashSet for fast membership
bannedUsers :: HashSet UserId  
bannedUsers = HS.fromList [UserId 5, UserId 12]
```

## Text Processing

### Text - Efficient Unicode Text
```haskell
import qualified Data.Text as T
import qualified Data.Text.IO as T
import qualified Data.Text.Encoding as T

-- Always prefer Text over String
processText :: Text -> Text
processText = T.toUpper . T.strip . T.filter (/= ' ')

-- File I/O
readConfig :: FilePath -> IO Text
readConfig = T.readFile

-- Encoding/decoding
encodeUtf8 :: Text -> ByteString
encodeUtf8 = T.encodeUtf8

decodeUtf8Safe :: ByteString -> Either UnicodeException Text
decodeUtf8Safe = T.decodeUtf8'
```

### ByteString - Efficient Binary Data
```haskell
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import qualified Data.ByteString.Builder as B

-- Strict vs lazy
strictRead :: FilePath -> IO ByteString
strictRead = BS.readFile  -- Entire file in memory

lazyRead :: FilePath -> IO LBS.ByteString  
lazyRead = LBS.readFile   -- Streaming

-- Efficient building
buildResponse :: [ByteString] -> LBS.ByteString
buildResponse chunks = B.toLazyByteString $ 
  foldMap B.byteString chunks
```

## JSON and Serialization

### Aeson - JSON Processing
```haskell
{-# LANGUAGE DeriveGeneric, DeriveAnyClass #-}
import Data.Aeson
import GHC.Generics

data User = User 
  { name :: Text
  , age :: Int  
  , email :: Maybe Text
  } deriving stock (Generic, Show)
    deriving anyclass (ToJSON, FromJSON)

-- Custom instances for control
instance ToJSON User where
  toJSON User{..} = object 
    [ "name" .= name
    , "age" .= age
    , "email" .= email  
    ]

instance FromJSON User where
  parseJSON = withObject "User" $ \o -> User
    <$> o .: "name"
    <*> o .: "age"  
    <*> o .:? "email"

-- Parsing with error handling
parseUser :: ByteString -> Either String User
parseUser = eitherDecodeStrict

-- Optics integration
userEmail :: Lens' User (Maybe Text)  
userEmail = key "email" . _Just . _String
```

### Binary - Efficient Binary Serialization
```haskell
import Data.Binary

instance Binary User where
  put User{..} = do
    put name
    put age
    put email
    
  get = User <$> get <*> get <*> get

-- Smaller, faster than JSON for internal communication
serialize :: User -> ByteString
serialize = encode

deserialize :: ByteString -> User  
deserialize = decode
```

## Web Development

### Warp - HTTP Server
```haskell
import Network.Wai
import Network.Wai.Handler.Warp

-- High-performance HTTP server
app :: Application
app request respond = do
  putStrLn $ "Request: " <> show (pathInfo request)
  respond $ responseLBS status200 [] "Hello World"

main :: IO ()  
main = do
  putStrLn "Starting server on port 8080"
  run 8080 app
  
-- With TLS
-- runTLS (tlsSettings "cert.pem" "key.pem") (setPort 443 defaultSettings) app
```

### Servant - Type-Safe Web APIs
```haskell
{-# LANGUAGE DataKinds, TypeOperators #-}
import Servant

type UserAPI = "users" :> Get '[JSON] [User]
          :<|> "users" :> ReqBody '[JSON] User :> Post '[JSON] User
          :<|> "users" :> Capture "id" Int :> Get '[JSON] User

userServer :: Server UserAPI
userServer = listUsers :<|> createUser :<|> getUser
  where
    listUsers = liftIO getAllUsers
    createUser user = liftIO $ saveUser user  
    getUser userId = liftIO $ findUser userId

-- Automatic client generation
userClient :: ClientM [User] 
         :<|> ClientM User 
         :<|> ClientM User
userClient = client (Proxy @UserAPI)

-- Documentation generation  
userDocs :: API
userDocs = docs (Proxy @UserAPI)
```

## Concurrency

### STM - Software Transactional Memory
```haskell
import Control.Concurrent.STM

data Counter = Counter { value :: TVar Int }

newCounter :: IO Counter
newCounter = Counter <$> newTVarIO 0

increment :: Counter -> STM ()
increment counter = modifyTVar' (value counter) (+1)

-- Composable transactions
transfer :: TVar Int -> TVar Int -> Int -> STM ()
transfer fromAccount toAccount amount = do
  fromBalance <- readTVar fromAccount
  when (fromBalance < amount) retry  -- Block until sufficient funds
  modifyTVar fromAccount (subtract amount)
  modifyTVar toAccount (+ amount)

-- Atomic execution
atomicTransfer :: TVar Int -> TVar Int -> Int -> IO ()
atomicTransfer from to amount = atomically $ transfer from to amount
```

### Async - Concurrent Programming
```haskell
import Control.Concurrent.Async

-- Parallel processing
processParallel :: [FilePath] -> IO [ProcessResult]
processParallel files = do
  asyncs <- mapM (async . processFile) files
  mapM wait asyncs

-- Race conditions
fetchWithTimeout :: Int -> IO a -> IO (Maybe a)
fetchWithTimeout timeoutSecs action = do
  result <- race (threadDelay (timeoutSecs * 1000000)) action
  case result of
    Left _ -> pure Nothing    -- Timeout
    Right value -> pure (Just value)

-- Concurrent resource pooling
withResourcePool :: Int -> (Resource -> IO a) -> [Input] -> IO [a]
withResourcePool poolSize action inputs = do
  sem <- newQSem poolSize  -- Limit concurrent operations
  mapConcurrently (\input -> bracket_ (waitQSem sem) (signalQSem sem) 
                                      (action input)) inputs
```

## Database

### PostgreSQL-Simple - PostgreSQL Interface
```haskell
import Database.PostgreSQL.Simple
import Database.PostgreSQL.Simple.FromRow

instance FromRow User where
  fromRow = User <$> field <*> field <*> field

instance ToRow User where  
  toRow User{..} = [toField name, toField age, toField email]

-- Connection management
connectDB :: IO Connection
connectDB = connect defaultConnectInfo 
  { connectDatabase = "myapp"
  , connectUser = "postgres" 
  }

-- Queries with type safety
getUsers :: Connection -> IO [User]
getUsers conn = query_ conn "SELECT name, age, email FROM users"

getUserById :: Connection -> Int -> IO (Maybe User)  
getUserById conn userId = do
  results <- query conn "SELECT name, age, email FROM users WHERE id = ?" 
                        (Only userId)
  case results of
    [user] -> pure (Just user)
    _ -> pure Nothing

-- Prepared statements
insertUser :: Connection -> User -> IO ()
insertUser conn user = do
  _ <- execute conn "INSERT INTO users (name, age, email) VALUES (?, ?, ?)" user
  pure ()
```

## Testing

### Hspec - Behavior-Driven Testing
```haskell
import Test.Hspec
import Test.Hspec.QuickCheck

spec :: Spec
spec = describe "User module" $ do
  describe "validateEmail" $ do
    it "accepts valid emails" $ do
      validateEmail "[REDACTED_SECRET]" `shouldBe` Right (Email "[REDACTED_SECRET]")
      
    it "rejects invalid emails" $ do  
      validateEmail "invalid" `shouldBe` Left InvalidEmailFormat
      
    prop "roundtrip property" $ \email ->
      either (const True) (\e -> renderEmail e == email) (validateEmail email)

  describe "User creation" $ do
    context "with valid data" $ do
      it "creates user successfully" $ do
        let user = createUser "Alice" 25 "[REDACTED_SECRET]"  
        userName user `shouldBe` "Alice"
        userAge user `shouldBe` 25

-- Running tests
main :: IO ()
main = hspec spec
```

### QuickCheck - Property-Based Testing
```haskell
import Test.QuickCheck

-- Custom generators
instance Arbitrary User where
  arbitrary = User 
    <$> genName
    <*> choose (0, 120)  
    <*> arbitrary
    where
      genName = elements ["Alice", "Bob", "Charlie"]

instance Arbitrary Email where
  arbitrary = Email <$> genEmail
    where  
      genEmail = do
        local <- listOf1 (choose ('a', 'z'))
        domain <- listOf1 (choose ('a', 'z'))
        pure $ local <> "@" <> domain <> ".com"

-- Properties
prop_serializeDeserialize :: User -> Property
prop_serializeDeserialize user = 
  deserialize (serialize user) === user

prop_emailValidation :: Text -> Property  
prop_emailValidation email = 
  case validateEmail email of
    Right (Email e) -> e === email
    Left _ -> property True  -- Invalid input is fine

-- Conditional properties
prop_positiveAge :: User -> Property
prop_positiveAge user = userAge user >= 0 ==> 
  isValidUser user === True
```

### Tasty - Testing Framework
```haskell
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck

tests :: TestTree
tests = testGroup "All Tests"
  [ unitTests
  , propertyTests  
  ]

unitTests :: TestTree
unitTests = testGroup "Unit Tests"
  [ testCase "User validation" $ do
      let result = validateUser "Alice" 25 "[REDACTED_SECRET]"
      result @?= Right (User "Alice" 25 "[REDACTED_SECRET]")
  ]

propertyTests :: TestTree  
propertyTests = testGroup "Property Tests"
  [ testProperty "Serialize roundtrip" prop_serializeDeserialize
  , testProperty "Email validation" prop_emailValidation
  ]

main :: IO ()
main = defaultMain tests
```

## Streaming and Performance

### Conduit - Streaming Data Processing
```haskell
import Data.Conduit
import qualified Data.Conduit.List as CL
import qualified Data.Conduit.Binary as CB

-- Efficient file processing
processLogFile :: FilePath -> FilePath -> IO ()
processLogFile inputFile outputFile = runConduitRes $
  sourceFile inputFile
  .| CB.lines
  .| CL.map (processLogLine . decodeUtf8)
  .| CL.filter isImportantLog  
  .| CL.map encodeUtf8
  .| CB.unlines
  .| sinkFile outputFile

-- Memory-efficient aggregation
countWords :: FilePath -> IO Int
countWords file = runConduitRes $
  sourceFile file  
  .| CB.lines
  .| CL.concatMap T.words
  .| CL.length
```

This ecosystem provides the foundation for most Haskell applications, with each library designed for composability and type safety.


path: references/nix-haskell.md
content: # Nix-Based Haskell Development

## Why Nix for Haskell

1. **Reproducible builds** - Same dependencies everywhere, no version conflicts
2. **Isolated environments** - Different projects can use different GHC/package versions  
3. **System integration** - Easy to add C libraries, databases, tools
4. **Binary caches** - Avoid rebuilding everything from source
5. **Cross-compilation** - Build for different architectures

## Modern Approach: haskell-flake

### Basic Project Setup
```nix
# flake.nix
{
  description = "My Haskell project";
  
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
    haskell-flake.url = "github:srid/haskell-flake";
  };
  
  outputs = inputs@{ self, nixpkgs, haskell-flake, ... }:
    haskell-flake.lib.mkFlake { inherit inputs; } {
      systems = [ "x86_64-linux" "aarch64-darwin" ];
      
      haskellProjects.default = {
        basePackages = nixpkgs.haskellPackages;  
        
        packages.myproject.root = ./.;  # Local package
        
        devShell.tools = hp: {
          inherit (hp) 
            haskell-language-server
            hlint  
            ormolu
            haskell-ci;
          inherit (nixpkgs) 
            ghcid
            cabal-install;
        };
      };
    };
}
```

### Multi-Package Projects
```nix
# flake.nix for workspace with multiple packages
{
  outputs = inputs@{ self, nixpkgs, haskell-flake, ... }:
    haskell-flake.lib.mkFlake { inherit inputs; } {
      haskellProjects.default = {
        basePackages = nixpkgs.haskellPackages;
        
        # Local packages
        packages = {
          myproject-core.root = ./core;
          myproject-web.root = ./web;  
          myproject-cli.root = ./cli;
        };
        
        # Package-specific settings
        settings = {
          myproject-core = {
            jailbreak = true;      # Ignore version bounds
            check = false;         # Disable tests for this package
          };
          myproject-web = {
            buildFromSdist = true; # Test packaging
          };
        };
      };
    };
}
```

### Using Different GHC Versions
```nix
{
  outputs = inputs@{ self, nixpkgs, haskell-flake, ... }:
    haskell-flake.lib.mkFlake { inherit inputs; } {
      haskellProjects = {
        # Default project with latest GHC
        default = {
          basePackages = nixpkgs.haskellPackages;
          packages.myproject.root = ./.;
        };
        
        # Legacy project with older GHC  
        ghc96 = {
          basePackages = nixpkgs.haskell.packages.ghc96;
          packages.myproject.root = ./.;
        };
        
        # Bleeding edge with latest GHC
        ghc98 = {
          basePackages = nixpkgs.haskell.packages.ghc98;
          packages.myproject.root = ./.;
        };
      };
    };
}
```

## Traditional Approach: nixpkgs haskellPackages

### Basic shell.nix
```nix
{ pkgs ? import <nixpkgs> {} }:
let
  ghc = pkgs.haskell.packages.ghc9121;  # Choose GHC version
in
ghc.shellFor {
  packages = p: [ (p.callCabal2nix "myproject" ./. {}) ];
  
  buildInputs = with pkgs; [
    ghc.haskell-language-server  
    ghc.hlint
    ghc.ormolu
    ghc.cabal-install
    ghcid
    
    # System tools
    zlib.dev
    pkg-config
  ];
  
  withHoogle = true;  # Local documentation
  
  shellHook = ''
    echo "Haskell development environment"
    echo "GHC version: $(ghc --version)"
    export PS1="[haskell] $PS1"
  '';
}
```

### Overriding Dependencies
```nix
{ pkgs ? import <nixpkgs> {} }:
let
  hpkgs = pkgs.haskell.packages.ghc9121.override {
    overrides = hself: hsuper: {
      # Use newer version of a package
      aeson = hself.callHackage "aeson" "2.2.1.0" {};
      
      # Use git version
      servant = hself.callCabal2nix "servant" (pkgs.fetchFromGitHub {
        owner = "haskell-servant";
        repo = "servant";
        rev = "main";
        sha256 = "...";
      }) {};
      
      # Local dependency
      mylib = hself.callCabal2nix "mylib" ../mylib {};
      
      # Disable tests for problematic packages
      some-package = pkgs.haskell.lib.dontCheck hsuper.some-package;
      
      # Jailbreak version bounds
      old-package = pkgs.haskell.lib.doJailbreak hsuper.old-package;
    };
  };
in
hpkgs.shellFor {
  packages = p: [ (p.callCabal2nix "myproject" ./. {}) ];
  # ... rest of shell configuration
}
```

### Building Executables
```nix
{ pkgs ? import <nixpkgs> {} }:
let
  hpkgs = pkgs.haskell.packages.ghc9121;
  myproject = hpkgs.callCabal2nix "myproject" ./. {};
in
{
  # The package itself
  inherit myproject;
  
  # Development shell
  shell = hpkgs.shellFor {
    packages = p: [ myproject ];
    buildInputs = with pkgs; [
      hpkgs.haskell-language-server
      ghcid
    ];
  };
  
  # Static binary (if possible)
  static = (hpkgs.extend (hself: hsuper: {
    myproject = pkgs.haskell.lib.justStaticExecutables 
                  (hsuper.callCabal2nix "myproject" ./. {});
  })).myproject;
}
```

## Advanced haskell.nix Setup

### Using IOHK's haskell.nix
```nix
{
  description = "Project using haskell.nix";
  
  inputs = {
    nixpkgs.follows = "haskellNix/nixpkgs-unstable";
    haskellNix.url = "github:input-output-hk/haskell.nix";
  };
  
  outputs = { self, nixpkgs, haskellNix }:
    let
      system = "x86_64-linux";
      pkgs = import nixpkgs {
        inherit system;
        inherit (haskellNix) config;
        overlays = [ haskellNix.overlay ];
      };
      
      project = pkgs.haskell-nix.cabalProject' {
        src = ./.;
        compiler-nix-name = "ghc9121";
        
        # Package customization
        modules = [
          {
            packages.myproject = {
              doHaddock = false;  # Disable docs for faster builds
              configureFlags = [ "--disable-optimization" ];
            };
          }
        ];
      };
    in
    {
      packages.${system}.default = project.myproject.components.exes.myproject;
      
      devShells.${system}.default = project.shellFor {
        tools = {
          cabal = {};
          hlint = {};
          haskell-language-server = {};
        };
        
        buildInputs = with pkgs; [
          ghcid
          nixpkgs-fmt
        ];
      };
    };
}
```

## Adding System Dependencies

### C Libraries and Tools
```nix
{ pkgs ? import <nixpkgs> {} }:
let
  hpkgs = pkgs.haskell.packages.ghc9121;
in
hpkgs.shellFor {
  packages = p: [ (p.callCabal2nix "myproject" ./. {}) ];
  
  buildInputs = with pkgs; [
    # Haskell tools
    hpkgs.haskell-language-server
    ghcid
    
    # C libraries (for FFI)
    zlib.dev
    openssl.dev
    postgresql.lib
    
    # Development tools
    pkg-config  # For finding C libraries
    docker
    docker-compose
    
    # Database  
    postgresql
    redis
  ];
  
  # Set up environment for C libraries
  shellHook = ''
    export PKG_CONFIG_PATH="${pkgs.openssl.dev}/lib/pkgconfig:${pkgs.zlib.dev}/lib/pkgconfig:$PKG_CONFIG_PATH"
    export LD_LIBRARY_PATH="${pkgs.lib.makeLibraryPath [pkgs.openssl pkgs.zlib]}"
  '';
}
```

### Database Setup
```nix
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
  buildInputs = with pkgs; [
    # Haskell environment
    (haskell.packages.ghc9121.ghcWithPackages (ps: with ps; [
      postgresql-simple
      persistent-postgresql
    ]))
    
    # Database
    postgresql
    
    # Development tools
    haskell-language-server
  ];
  
  shellHook = ''
    # Set up local PostgreSQL
    export PGDATA="$PWD/.pgdata"
    export PGHOST="$PWD/.pgdata"
    export PGPORT="5432"
    export PGDATABASE="myapp"
    
    if [ ! -d "$PGDATA" ]; then
      echo "Initializing PostgreSQL..."
      initdb --auth=trust
      echo "unix_socket_directories = '$PGDATA'" >> $PGDATA/postgresql.conf
      echo "listen_addresses = ''" >> $PGDATA/postgresql.conf
      echo "port = $PGPORT" >> $PGDATA/postgresql.conf
    fi
    
    pg_ctl start -l $PGDATA/postgres.log
    createdb "$PGDATABASE" 2>/dev/null || true
    
    # Clean up function
    cleanup() {
      pg_ctl stop
    }
    trap cleanup EXIT
  '';
}
```

## Cross-Compilation

### Building for Different Architectures
```nix
{
  description = "Cross-compilation example";
  
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
    haskell-flake.url = "github:srid/haskell-flake";
  };
  
  outputs = inputs@{ self, nixpkgs, haskell-flake, ... }:
    haskell-flake.lib.mkFlake { inherit inputs; } {
      systems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" ];
      
      haskellProjects.default = {
        basePackages = nixpkgs.pkgsCross.aarch64-multiplatform.haskellPackages;
        packages.myproject.root = ./.;
      };
    };
}
```

### Static Binaries
```nix
{
  outputs = inputs@{ self, nixpkgs, ... }:
    let
      system = "x86_64-linux";
      pkgs = nixpkgs.legacyPackages.${system};
      
      # Static build configuration
      hpkgs = pkgs.pkgsStatic.haskell.packages.ghc9121.override {
        overrides = hself: hsuper: {
          # Disable shared libraries
          myproject = pkgs.haskell.lib.disableSharedExecutables 
                        (hsuper.callCabal2nix "myproject" ./. {});
        };
      };
    in
    {
      packages.${system}.default = hpkgs.myproject;
      
      # Docker image with static binary
      packages.${system}.docker = pkgs.dockerTools.buildImage {
        name = "myproject";
        contents = [ hpkgs.myproject ];
        config.Cmd = [ "/bin/myproject" ];
      };
    };
}
```

## IDE Integration

### VS Code with Nix
```json
// .vscode/settings.json
{
  "nix.enableLanguageServer": true,
  "nix.serverPath": "nil",
  
  "haskell.manageHLS": "PATH",
  "haskell.serverEnvironment": {
    "PATH": "${workspaceFolder}/.nix-shell-env/bin:${env:PATH}"
  }
}
```

```nix
# Generate .vscode/settings.json automatically
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
  buildInputs = with pkgs; [
    haskell-language-server
    nil  # Nix language server
  ];
  
  shellHook = ''
    mkdir -p .vscode .nix-shell-env/bin
    
    # Create symlinks for tools
    ln -sf $(which haskell-language-server) .nix-shell-env/bin/
    ln -sf $(which nil) .nix-shell-env/bin/
    
    # Generate VS Code settings
    cat > .vscode/settings.json << EOF
    {
      "nix.enableLanguageServer": true,
      "nix.serverPath": "nil",
      "haskell.manageHLS": "PATH"
    }
    EOF
  '';
}
```

### direnv Integration
```bash
# .envrc
use flake

# Or with impure evaluation for unfree packages
# use flake --impure
```

```nix
# .envrc for shell.nix
use nix
```

## Package Publishing with Nix

### Cachix for Binary Caches
```yaml
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: cachix/install-nix-action@v18
      - uses: cachix/cachix-action@v12
        with:
          name: myproject
          authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
      
      - name: Build
        run: |
          nix build
          nix flake check
```

### Nix Package Repository
```nix
# Generate a package set
{
  description = "My Haskell packages";
  
  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
  
  outputs = { self, nixpkgs }:
    let
      system = "x86_64-linux";
      pkgs = nixpkgs.legacyPackages.${system};
      hpkgs = pkgs.haskell.packages.ghc9121;
    in
    {
      packages.${system} = {
        mypackage1 = hpkgs.callCabal2nix "mypackage1" ./packages/mypackage1 {};
        mypackage2 = hpkgs.callCabal2nix "mypackage2" ./packages/mypackage2 {};
      };
      
      # Overlay for use in other projects  
      overlays.default = final: prev: {
        haskellPackages = prev.haskellPackages.override {
          overrides = hself: hsuper: {
            mypackage1 = self.packages.${system}.mypackage1;
            mypackage2 = self.packages.${system}.mypackage2;
          };
        };
      };
    };
}
```

## Troubleshooting

### Common Issues
```bash
# Missing C library errors
# Add to shell.nix buildInputs: pkgs.zlib.dev pkgs.openssl.dev

# Package conflicts
# Use overrides to pin specific versions

# Build failures with latest nixpkgs
# Pin to specific nixpkgs commit:
# nixpkgs.url = "github:NixOS/nixpkgs/a1b2c3d4..."

# GHC version mismatches
# Ensure all packages use same GHC:
# basePackages = nixpkgs.haskell.packages.ghc9121

# Out of memory during compilation
# Add to flake: settings.myproject = { enableSeparateDataOutput = true; };
```

### Debugging Builds
```bash
# Build with more verbose output
nix build --print-build-logs --show-trace

# Drop into build environment
nix develop .#default

# Check what's in the build
nix-store -q --tree $(nix build --print-out-paths)

# See why packages are being rebuilt
nix why-depends /nix/store/... /nix/store/...
```

This covers the modern approaches to Haskell development with Nix, emphasizing reproducibility and ease of development while avoiding traditional Haskell toolchain issues.


path: README.md
content: # Haskell Skill

The comprehensive [Agent Skill](https://agentskills.io) for professional Haskell development. Built by senior Haskell developers and community leaders with years of production Haskell experience, this skill encodes the patterns, practices, and hard-won knowledge that define modern, industrial-strength Haskell.

## Overview

This skill gives any compatible coding agent deep expertise in Haskell programming, from project setup through production deployment. It reflects the way experienced Haskell engineers actually write code: type-driven, pragmatic, and grounded in real-world tradeoffs.

**Core philosophy:**

1. **Types are the design** -- Make illegal states unrepresentable
2. **Purity by default** -- Side effects are explicit in the type system
3. **Composition over inheritance** -- Small, composable functions and typeclasses
4. **Laziness as a tool** -- Elegant abstractions with awareness of space leaks
5. **Correctness first, then performance** -- Get it right, profile, then optimize
6. **Keep it simple** -- Haskell2010 + purity + strong types deliver the majority of the value

## What's Included

The skill follows a progressive disclosure architecture. The main `SKILL.md` provides immediately actionable guidance (~420 lines), while seven deep-dive reference documents cover advanced topics agents can load on demand.

### SKILL.md (Primary Instructions)

- Project setup and structure (Cabal 3.0)
- Essential GHC extensions (categorized: always-on, use-freely, avoid-unless-needed)
- Type-driven development patterns (newtypes, phantom types, smart constructors)
- Error handling (Either vs exceptions, the ReaderT pattern)
- Testing with HSpec and QuickCheck
- Performance essentials (strictness, profiling, space leak prevention)
- Common gotchas that trip up even intermediate Haskellers
- Key library recommendations across 18 packages
- Commands reference for the full build/test/profile lifecycle

### Reference Documents

| File | Topics |
|------|--------|
| [`references/type-system.md`](references/type-system.md) | ADTs, GADTs, type families, type classes, DataKinds, phantom types |
| [`references/common-patterns.md`](references/common-patterns.md) | MTL, ReaderT, effect systems, optics, free monads, type-level programming |
| [`references/libraries.md`](references/libraries.md) | Essential library ecosystem with usage examples |
| [`references/performance.md`](references/performance.md) | Strictness, profiling, space leaks, concurrency, benchmarking with Criterion |
| [`references/ghc-extensions.md`](references/ghc-extensions.md) | 30+ GHC extensions documented with examples and safety guidance |
| [`references/cabal-guide.md`](references/cabal-guide.md) | Cabal format, multi-package projects, CI/CD, Hackage publishing |
| [`references/nix-haskell.md`](references/nix-haskell.md) | Nix-based Haskell development (nixpkgs, haskell.nix, haskell-flake) |

Total: ~3,600 lines of curated, production-tested guidance.

## Installation

### Claude Code

Add the skill as a dependency in your project's `.claude/settings.json`:

```json
{
  "skills": ["github:mightybyte/haskell-skill"]
}
```

Or install it directly:

```
/skill add github:mightybyte/haskell-skill
```

### OpenClaw

Install from [ClawHub](https://clawhub.ai/):

```bash
npx clawhub@latest install haskell
```

Or via the OpenClaw CLI:

```bash
clawhub install haskell
```

### Cursor

Add to your Cursor rules or `.cursor/skills/` directory. See [Cursor's Agent Skills documentation](https://docs.cursor.com/context/agent-skills) for details.

### Other Compatible Agents

This skill follows the open [Agent Skills specification](https://agentskills.io/specification). Any agent that supports the spec can use it. Clone or download the repository and point your agent's skill configuration to the `SKILL.md` file.

## Compatibility

- **GHC**: 9.2+ (some features reference GHC 9.2+ syntax like `OverloadedRecordDot`)
- **Build system**: Cabal 3.0+ (primary), with Nix and Stack guidance included
- **Language standard**: Haskell2010 as the base, with curated extensions

## Design Principles

This skill is intentionally opinionated. Rather than presenting every possible approach, it captures what works in production:

- **Pragmatic over academic** -- Recommends the simplest abstraction that solves the problem. Discourages reaching for advanced type machinery unless it pays for itself.
- **Battle-tested patterns** -- Every recommendation has been validated in real-world codebases. The ReaderT pattern, strict-by-default data types, Text-not-String -- these aren't theoretical preferences.
- **Progressive complexity** -- Agents start with the core instructions and pull in reference material only when the task demands it. Simple tasks stay simple.
- **Guardrails included** -- Explicit "avoid" lists for extensions, functions, and patterns that cause problems in practice. Twelve common gotchas that prevent the mistakes teams actually make.

## Contributing

Contributions are welcome. If you have improvements grounded in real-world Haskell experience, please open a pull request.

## License

BSD 3-Clause. See [LICENSE](LICENSE) for details.


Security scan verdicts — ClawScan: clean | static analysis:  | VirusTotal: clean | SkillSpector: clean (LOW)