--- name: Haskell Ecosystem description: This skill should be used when working with Haskell projects, "cabal.project", "stack.yaml", "ghc", "cabal build/test/run", "stack build/test/run", or Haskell language patterns. Provides comprehensive Haskell ecosystem patterns and best practices. version: 0.1.0 --- Provide comprehensive patterns for Haskell language, GHC toolchain, Cabal/Stack build systems, and type-level programming. Read - Analyze cabal files, stack.yaml, and Haskell source files Edit - Modify Haskell code and build configuration Bash - Run cabal build, stack build, ghci commands mcp__context7__get-library-docs - Fetch latest Haskell documentation Functions have no side effects; same input always produces same output Expressions evaluated only when needed; enables infinite data structures Hindley-Milner type system; explicit signatures recommended for top-level definitions Compose computations with effects; IO, Maybe, Either, State, Reader, Writer Ad-hoc polymorphism; define operations for types (Eq, Ord, Show, Functor, Applicative, Monad) Sum types (Either, Maybe) and product types (tuples, records) data Maybe a = Nothing | Just a data Either a b = Left a | Right b data Person = Person { name :: String, age :: Int } Define behavior for types; similar to interfaces but more powerful Equality comparison (==, /=) Ordering comparison (compare, <, >, <=, >=) String representation (show) Parse from string (read) Mappable containers (fmap, <$>) Apply functions in context (<*>, pure) Sequence computations (>>=, return, do-notation) Reducible structures (foldr, foldl, toList) Traverse with effects (traverse, sequenceA) Associative binary operation (<>) Semigroup with identity (mempty, mappend) class Eq a where (==) :: a -> a -> Bool (/=) :: a -> a -> Bool instance Eq Person where p1 == p2 = name p1 == name p2 && age p1 == age p2 Advanced type system features for compile-time guarantees Generalized Algebraic Data Types - refine types in pattern matching GADTs {-# LANGUAGE GADTs #-} data Expr a where LitInt :: Int -> Expr Int LitBool :: Bool -> Expr Bool Add :: Expr Int -> Expr Int -> Expr Int If :: Expr Bool -> Expr a -> Expr a -> Expr a eval :: Expr a -> a eval (LitInt n) = n eval (LitBool b) = b eval (Add e1 e2) = eval e1 + eval e2 eval (If c t e) = if eval c then eval t else eval e Type-level functions; compute types from types TypeFamilies {-# LANGUAGE TypeFamilies #-} type family Element c where Element [a] = a Element (Set a) = a Element Text = Char class Container c where type Elem c empty :: c insert :: Elem c -> c -> c Promote data types to kinds for type-level programming DataKinds {-# LANGUAGE DataKinds, KindSignatures #-} data Nat = Zero | Succ Nat data Vec (n :: Nat) a where VNil :: Vec 'Zero a VCons :: a -> Vec n a -> Vec ('Succ n) a Type parameters that don't appear in value constructors newtype Tagged tag a = Tagged { unTagged :: a } data Validated data Unvalidated validateEmail :: Tagged Unvalidated String -> Maybe (Tagged Validated String) Type constructors as parameters; enables Functor, Monad abstractions class Functor f where fmap :: (a -> b) -> f a -> f b -- f is a type constructor (* -> *) -- Functor operates on type constructors, not concrete types Compose monadic effects using transformers from mtl/transformers Build custom monad stacks with transformers import Control.Monad.Trans.Reader import Control.Monad.Trans.State import Control.Monad.Trans.Except type App a = ReaderT Config (StateT AppState (ExceptT AppError IO)) a runApp :: Config -> AppState -> App a -> IO (Either AppError (a, AppState)) runApp cfg st app = runExceptT (runStateT (runReaderT app cfg) st) Use mtl type classes for polymorphic effect handling import Control.Monad.Reader import Control.Monad.State import Control.Monad.Except doSomething :: (MonadReader Config m, MonadState AppState m, MonadError AppError m) => m Result doSomething = do cfg <- ask st <- get when (invalid cfg) $ throwError InvalidConfig pure (compute cfg st) Read-only environment; ask, local Mutable state; get, put, modify Error handling; throwError, catchError Accumulate output; tell, listen Short-circuit on Nothing Non-determinism (use list-t or logict for correct semantics) Composable getters, setters, and traversals using lens library Focus on parts of data structures import Control.Lens data Person = Person { _name :: String, _age :: Int } makeLenses ''Person -- name :: Lens' Person String -- age :: Lens' Person Int getName :: Person -> String getName p = p ^. name setName :: String -> Person -> Person setName n p = p & name .~ n modifyAge :: (Int -> Int) -> Person -> Person modifyAge f p = p & age %~ f Different optic types for different access patterns Get and set exactly one value Focus on one branch of a sum type Focus on zero or more values Bidirectional transformation Read-only access Read-only traversal Common lens operators View through lens (view) Set value (set) Modify value (over) Apply function (flip ($)) View through prism (preview) View all through traversal (toListOf) Alternative to lens with better type errors and composition optics Considered more modern; lens has larger ecosystem Optional values; prefer over null findUser :: UserId -> Maybe User findUser uid = lookup uid users -- Safe chaining with Monad getUserEmail :: UserId -> Maybe Email getUserEmail uid = do user <- findUser uid pure (userEmail user) Computations that may fail with error information parseConfig :: Text -> Either ParseError Config parseConfig input = do json <- parseJSON input validateConfig json Error handling in monadic contexts import Control.Monad.Except data AppError = NotFound | InvalidInput String | IOError IOException loadUser :: MonadError AppError m => UserId -> m User loadUser uid = do mUser <- findUser uid case mUser of Nothing -> throwError NotFound Just u -> pure u Zero-cost wrapper for type safety newtype UserId = UserId { unUserId :: Int } deriving (Eq, Ord, Show) newtype Email = Email { unEmail :: Text } deriving (Eq, Show) Validate data at construction time module Email (Email, mkEmail, unEmail) where newtype Email = Email Text mkEmail :: Text -> Maybe Email mkEmail t | isValidEmail t = Just (Email t) | otherwise = Nothing Named fields with accessor functions {-# LANGUAGE RecordWildCards #-} data Config = Config { configHost :: String , configPort :: Int , configTimeout :: Int } -- Using RecordWildCards mkConnection :: Config -> IO Connection mkConnection Config{..} = connect configHost configPort Functions that crash on some inputs (head, tail, fromJust, read) Use safe alternatives (headMay, listToMaybe) or pattern matching Using String ([Char]) for text processing Use Text or ByteString for performance Using lazy IO (readFile, getContents) in production Use strict IO or streaming (conduit, pipes, streaming) Defining type class instances outside the module of the type or class Use newtype wrappers or define instances in appropriate modules . ├── my-project.cabal ├── cabal.project # Multi-package configuration ├── cabal.project.local # Local overrides (gitignored) ├── src/ │ └── MyProject.hs ├── app/ │ └── Main.hs ├── test/ │ └── Spec.hs └── bench/ └── Bench.hs cabal-version: 3.0 name: my-project version: 0.1.0.0 synopsis: Short description license: MIT author: Your Name maintainer: your@email.com common warnings ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wpartial-fields -Wredundant-constraints library import: warnings exposed-modules: MyProject build-depends: base ^>=4.18, text ^>=2.0, containers ^>=0.6 hs-source-dirs: src default-language: GHC2021 executable my-project import: warnings main-is: Main.hs build-depends: base ^>=4.18, my-project hs-source-dirs: app default-language: GHC2021 test-suite my-project-test import: warnings type: exitcode-stdio-1.0 main-is: Spec.hs build-depends: base ^>=4.18, my-project, hspec ^>=2.11, QuickCheck ^>=2.14 hs-source-dirs: test default-language: GHC2021 -- Caret (^>=): major version compatible base ^>=4.18 -- 4.18.x.x -- Range text >=2.0 && <2.2 -- Any version (avoid in published packages) containers default-extensions: OverloadedStrings LambdaCase RecordWildCards DerivingStrategies GeneralizedNewtypeDeriving TypeApplications default-language: GHC2021 -- Recommended for new projects Multi-package project configuration packages: . ./subpackage -- Use local package optional-packages: ../local-dependency -- Optimization optimization: 2 -- Documentation documentation: True -- Test options tests: True -- Allow newer dependencies allow-newer: base Compile the project Build all targets Build and run executable Run test suites Start GHCi with project loaded Generate documentation Update package index Lock dependency versions Generate version bounds Check for outdated dependencies . ├── stack.yaml # Stack project configuration ├── stack.yaml.lock # Locked dependency versions ├── package.yaml # hpack format (generates .cabal) └── ... (same as cabal) resolver: lts-22.0 # Stackage LTS snapshot packages: - . - ./subpackage extra-deps: - some-package-1.0.0 - github: owner/repo commit: abc123 ghc-options: "$locals": -Wall -Werror hpack format; generates .cabal file name: my-project version: 0.1.0.0 dependencies: - base >= 4.18 && < 5 - text - containers ghc-options: - -Wall - -Wcompat default-extensions: - OverloadedStrings - LambdaCase library: source-dirs: src executables: my-project: main: Main.hs source-dirs: app dependencies: - my-project tests: my-project-test: main: Spec.hs source-dirs: test dependencies: - my-project - hspec - QuickCheck Compile the project Run tests Build and run executable Start GHCi with project loaded Generate documentation Clean build artifacts Upgrade Stack itself Which build tool should I use? Stack with LTS resolver Cabal (native format) Cabal with cabal.project Stack (simpler getting started) Cabal with haskell.nix or nixpkgs Glasgow Haskell Compiler GHC 9.12+ (2025-2026) Previous standard Default edition; enables common extensions Current recommended for new code; extends GHC2021 Haskell Language Server - IDE support via LSP Code completion Type information on hover Go to definition Find references Code actions (import, qualify) Diagnostics (errors, warnings, hlint) Code formatting (fourmolu, ormolu, stylish-haskell) hls.yaml or hie.yaml cradle: cabal: - path: "src" component: "lib:my-project" - path: "app" component: "exe:my-project" - path: "test" component: "test:my-project-test" Opinionated formatter; ormolu fork with more options fourmolu -i src/**/*.hs Minimal configuration formatter ormolu -i src/**/*.hs Configurable import organization and formatting stylish-haskell -i src/**/*.hs Suggest idiomatic Haskell improvements hlint src/ .hlint.yaml - ignore: {name: "Use camelCase"} - warn: {name: "Use head"} - error: {name: "Use String"} Static analysis for Haskell stan Detect dead code weeder BDD-style testing framework import Test.Hspec main :: IO () main = hspec $ do describe "Calculator" $ do it "adds two numbers" $ do add 1 2 `shouldBe` 3 it "handles negative numbers" $ do add (-1) 1 `shouldBe` 0 describe "Parser" $ do context "when input is valid" $ do it "parses successfully" $ do parse "valid" `shouldSatisfy` isRight context "when input is invalid" $ do it "returns error" $ do parse "invalid" `shouldSatisfy` isLeft Equality check Predicate check IO action result Exception check List containment Property-based testing; generate random test cases import Test.QuickCheck prop_reverseReverse :: [Int] -> Bool prop_reverseReverse xs = reverse (reverse xs) == xs prop_sortIdempotent :: [Int] -> Bool prop_sortIdempotent xs = sort (sort xs) == sort xs -- With preconditions prop_headLast :: NonEmptyList Int -> Bool prop_headLast (NonEmpty xs) = head xs == head xs -- Custom generators newtype PositiveInt = PositiveInt Int deriving Show instance Arbitrary PositiveInt where arbitrary = PositiveInt . abs <$> arbitrary -- With HSpec describe "reverse" $ do it "is its own inverse" $ property $ \xs -> reverse (reverse xs) == (xs :: [Int]) Modern property-based testing with integrated shrinking import Hedgehog import qualified Hedgehog.Gen as Gen import qualified Hedgehog.Range as Range prop_reverse :: Property prop_reverse = property $ do xs <- forAll $ Gen.list (Range.linear 0 100) Gen.alpha reverse (reverse xs) === xs Use QuickCheck for properties; HSpec for examples Test edge cases: empty lists, zero, negative numbers Use type-driven development; write types first Use doctest for documentation examples Use Context7 MCP for up-to-date Haskell documentation resolve-library-id libraryName="optics haskell" get-library-docs context7CompatibleLibraryID="/websites/hackage_haskell_package_optics-0_4_2_1" topic="lenses" get-library-docs context7CompatibleLibraryID="/websites/hackage_haskell_package_hspec-2_11_12" topic="expectations" get-library-docs context7CompatibleLibraryID="/haskell-servant/servant" topic="server" Understand Haskell code requirements 1. Check cabal file or package.yaml for project configuration 2. Review existing types and type classes 3. Identify monad transformer requirements 4. Check for type-level programming needs Write pure, type-safe Haskell code 1. Design with types first; let types guide implementation 2. Use appropriate abstractions (Functor, Applicative, Monad) 3. Handle errors with Maybe/Either/ExceptT 4. Write property-based tests for core logic Verify Haskell code correctness 1. Run cabal build or stack build 2. Run hlint for suggestions 3. Run cabal test or stack test 4. Check formatting with fourmolu/ormolu Let types guide design; use the type system to prevent errors Avoid partial functions; use safe alternatives Run hlint and fix suggestions before committing Use Text/ByteString instead of String for performance Prefer mtl-style type class constraints over concrete monad stacks Write property-based tests for core logic Document exported functions with Haddock comments Use newtypes for type safety Enable GHC2021 or explicit commonly-used extensions Run hlint before committing; address all suggestions Avoid partial functions (head, tail, fromJust); use safe alternatives Use types to encode invariants; make illegal states unrepresentable Use fourmolu or ormolu for consistent formatting Prefer Text over String for text processing Write HSpec tests for behavior; QuickCheck for properties Use explicit type signatures for top-level definitions Type system design, monad transformer architecture, and effect modeling Haskell implementation with proper abstractions and error handling Run hlint, check formatting, and enforce Haskell idioms HLint suggestion about style Apply suggestion, maintain idiomatic code Type error or missing instance Review types, add instance or adjust design Breaking change in public API Stop, present migration options to user Partial function usage or unsafe code in library Block operation, require safe alternatives Navigate type class hierarchies and module structure Fetch Haskell documentation and library references Debug type errors, missing instances, and performance issues haskell.nix integration and nixpkgs Haskell infrastructure Use types to encode invariants Avoid partial functions in library code Follow Haskell style conventions Using String for text processing Lazy IO in production code Orphan instances