✓ Verified
💻 Development
✓ Enhanced Data
Haskell
Expert Haskell development skill.
- Rating
- 4.6 (154 reviews)
- Downloads
- 31,966 downloads
- Version
- 1.0.0
Overview
Expert Haskell development skill.
Complete Documentation
View Source →
Haskell Development Guide
Core Philosophy
- Types are the design — Make illegal states unrepresentable. If the type checker accepts it, we would like it to be correct.
- Purity by default — Side effects are explicit in the type system.
IOis a feature, not a burden. - Composition over inheritance — Small, composable functions. Typeclasses for ad-hoc polymorphism.
- Laziness as a tool — Enables elegant abstractions but demands awareness of space leaks.
- Correctness first, then performance — Get it right, then profile, then optimize.
- 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
text
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
text
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 "[email protected]" `shouldBe` Right (Email "[email protected]")
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
- String is [Char] — Use
Text(fromtextpackage) everywhere. EnableOverloadedStringsfor Text string literals. - Lazy IO —
readFilefrom Prelude is lazy. UseData.Text.IO.readFileorByteString.readFileinstead. - Orphan instances — Don't define typeclass instances outside the module that defines the type or the class. Use newtypes to wrap.
- Cabal hell — Use Nix or cabal's built-in solver (v2-build). Don't use
cabal-installv1 commands. - Records — Field names are global. Prefix field names with a uniform and readable scheme:
_employee_familyName. - Partial functions — Never use
head,tail,fromJust,readin production code. Use pattern matching or safe alternatives. - Unsafe operations - Never use
accursedUnutterablePerformIO. Only useunsafePerformIOwhen you can prove its use correct. - Undefined -
undefinedis a fantastic tool for stubbing things out for incremental development and type checking. It should never exist in production code. - foldl vs foldl' — Always use
foldl'(strict). The lazyfoldlfrom Prelude is almost never what you want. - Show for serialization —
Showis for debugging. Useaesonfor JSON,binary/cerealfor binary serialization. - MonadFail — Pattern match failures in
doblocks requireMonadFail. Avoid partial patterns indo. - 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 typesreferences/common-patterns.md— MTL, ReaderT, effect systems, optics, free monads, type-level programmingreferences/libraries.md— Essential library ecosystem with examplesreferences/performance.md— Strictness, profiling, space leaks, concurrency, benchmarkingreferences/ghc-extensions.md— Comprehensive GHC extension guide by categoryreferences/nix-haskell.md— Nix-based Haskell development (nixpkgs + haskell.nix)references/cabal-guide.md— Cabal format, multi-package projects, Hackage publishingreferences/best-practices.md— Code organization, Haddock, CI, style guide
Installation
Terminal bash
openclaw install haskell
Copied!
💻Code Examples
├── cabal.project # Multi-package config, source-repository-packages
-cabalproject--multi-package-config-source-repository-packages.txt
## Essential GHC Extensions
### Always Enable (via `default-extensions` in .cabal)AllowAmbiguousTypes -- Pair with TypeApplications for type-level dispatch.
allowambiguoustypes----pair-with-typeapplications-for-type-level-dispatch.txt
## Type-Driven Development
### Scope accessors with type name to avoid name clashes-- Never use error/undefined in library code.
---never-use-errorundefined-in-library-code.txt
## Common Patterns
### The ReaderT Patternover (users . each . name) T.toUpper myData
over-users--each--name-ttoupper-mydata.txt
## Testing
### HSpec - Testing Frameworkpure $ Email $ T.pack $ user <> "@" <> domain <> ".com"
-pure--email--tpack--user----domain--com.txt
## Performance Essentials
### Strictnessexample.txt
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: Haskell2010example.txt
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-packagesexample.txt
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 clausesexample.txt
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 effectivelyexample.txt
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.Tags
#coding_agents-and-ides
Quick Info
Category Development
Model Claude 3.5
Complexity One-Click
Author mightybyte
Last Updated 3/10/2026
🚀
Optimized for
Claude 3.5
Ready to Install?
Get started with this skill in seconds
openclaw install haskell
Related Skills
✓ Verified
💻 Development
4claw
4claw — a moderated imageboard for AI agents.
🧠 Claude-Ready
)}
★ 4.4 (118)
↓ 4,990
v1.0.0
✓ Verified
💻 Development
Aap Passport
Agent Attestation Protocol - The Reverse Turing Test.
🧠 Claude-Ready
)}
★ 4.3 (89)
↓ 4,621
v1.0.0
✓ Verified
💻 Development
Acestep Lyrics Transcription
Transcribe audio to timestamped lyrics using OpenAI Whisper or ElevenLabs Scribe API.
⚡ GPT-Optimized
)}
★ 3.8 (274)
↓ 17,648
v1.0.0
✓ Verified
💻 Development
Adaptive Suite
A continuously adaptive skill suite that empowers Clawdbot.
🧠 Claude-Ready
)}
★ 4.7 (88)
↓ 1,625
v1.0.0