nw-pbt-haskell
Haskell property-based testing with QuickCheck and Hedgehog frameworks
Best use case
nw-pbt-haskell is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Haskell property-based testing with QuickCheck and Hedgehog frameworks
Teams using nw-pbt-haskell should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/nw-pbt-haskell/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How nw-pbt-haskell Compares
| Feature / Agent | nw-pbt-haskell | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Haskell property-based testing with QuickCheck and Hedgehog frameworks
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# PBT Haskell -- QuickCheck + Hedgehog
## Framework Selection
| Framework | Shrinking | Stateful | Choose When |
|-----------|-----------|----------|-------------|
| QuickCheck | Type-based (manual) | Limited (open-source) | Default for most projects. Universal ecosystem support. |
| Hedgehog | Integrated (automatic) | Yes (parallel too) | Need automatic shrinking composition or parallel stateful testing |
QuickCheck is the original PBT framework (2000). Hedgehog is the modern alternative with better shrinking.
## Quick Start (QuickCheck)
```haskell
import Test.QuickCheck
prop_reverse_involutory :: [Int] -> Bool
prop_reverse_involutory xs = reverse (reverse xs) == xs
-- Run: quickCheck prop_reverse_involutory
-- Or in test suite: testProperty "reverse" prop_reverse_involutory
```
## Generator Cheat Sheet (QuickCheck)
### Via Arbitrary Type Class
```haskell
arbitrary :: Gen Int -- any Int
arbitrary :: Gen String -- any String
arbitrary :: Gen [Int] -- any list of Ints
arbitrary :: Gen Bool
-- Custom Arbitrary
data Color = Red | Green | Blue deriving (Show, Eq)
instance Arbitrary Color where
arbitrary = elements [Red, Green, Blue]
shrink Red = []
shrink _ = [Red]
```
### Gen Combinators
```haskell
choose (0, 100) -- bounded int
elements [1, 2, 3] -- pick from list
oneof [gen1, gen2] -- union
frequency [(80, gen1), (20, gen2)] -- weighted
listOf arbitrary -- list
listOf1 arbitrary -- non-empty list
vectorOf 5 arbitrary -- fixed-length list
-- Map
fmap (* 2) arbitrary -- even integers
-- Bind (dependent generation)
do xs <- listOf1 arbitrary
x <- elements xs
return (xs, x)
-- Sized
sized $ \n -> resize (n `div` 2) arbitrary
```
### Shrinking
```haskell
instance Arbitrary MyType where
arbitrary = ...
shrink (MyType a b) = [MyType a' b | a' <- shrink a]
++ [MyType a b' | b' <- shrink b]
```
## Stateful Testing (QuickCheck)
Not supported in open-source QuickCheck. Use Hedgehog for stateful testing, or commercial Quviq QuickCheck (Erlang).
## Quick Start (Hedgehog)
```haskell
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
-- Run: check prop_reverse
```
## Generator Cheat Sheet (Hedgehog)
```haskell
Gen.int (Range.linear 0 100)
Gen.int (Range.constant 0 100) -- no size scaling
Gen.double (Range.linearFrac 0 1)
Gen.string (Range.linear 0 50) Gen.alpha
Gen.bool
Gen.element [Red, Green, Blue]
-- Collections
Gen.list (Range.linear 0 50) Gen.alpha
Gen.nonEmpty (Range.linear 1 50) Gen.alpha
Gen.set (Range.linear 0 20) Gen.int
-- Map
Gen.int (Range.linear 0 100) <&> (* 2)
-- Filter
Gen.filter (> 0) (Gen.int (Range.linear minBound maxBound))
-- Recursive
Gen.recursive Gen.choice
[ Gen.constant Leaf ]
[ Node <$> genTree <*> Gen.int (Range.linear 0 100) <*> genTree ]
```
### Hedgehog Ranges
```haskell
Range.linear 0 100 -- scales with size parameter
Range.constant 0 100 -- fixed bounds, no scaling
Range.linearFrac 0.0 1.0 -- fractional, scales with size
Range.singleton 42 -- always 42
```
## Stateful Testing (Hedgehog)
Hedgehog supports sequential and parallel state machine testing.
```haskell
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import Hedgehog
import Hedgehog.Internal.State
-- Define state and commands
newtype ModelState (v :: * -> *) = ModelState { items :: Map String Int }
data Put (v :: * -> *) = Put String Int deriving (Show, Eq)
data Get (v :: * -> *) = Get String deriving (Show, Eq)
instance HTraversable Put where
htraverse _ (Put k v) = pure (Put k v)
instance HTraversable Get where
htraverse _ (Get k) = pure (Get k)
cPut :: (MonadGen n, MonadIO m, MonadTest m) => Command n m ModelState
cPut = Command
(\state -> Just $ Put <$> Gen.string (Range.linear 1 10) Gen.alpha
<*> Gen.int (Range.linear 0 100))
(\(Put k v) -> liftIO $ storePut realStore k v)
[ Update $ \(ModelState m) (Put k v) _out -> ModelState (Map.insert k v m)
]
cGet :: (MonadGen n, MonadIO m, MonadTest m) => Command n m ModelState
cGet = Command
(\(ModelState m) -> if Map.null m then Nothing
else Just $ Get <$> Gen.element (Map.keys m))
(\(Get k) -> liftIO $ storeGet realStore k)
[ Ensure $ \(ModelState before) _after (Get k) out ->
out === Map.lookup k before
]
prop_store :: Property
prop_store = property $ do
actions <- forAll $ Gen.sequential (Range.linear 1 50)
(ModelState Map.empty) [cPut, cGet]
executeSequential (ModelState Map.empty) actions
```
For parallel testing, replace `Gen.sequential` with `Gen.parallel` and `executeSequential` with `executeParallel`. Checks linearizability of concurrent operations.
## Test Runner Integration
### QuickCheck
```haskell
-- With HSpec
describe "sort" $ do
it "preserves length" $ property $
\(xs :: [Int]) -> length (sort xs) == length xs
-- With Tasty
testGroup "sort" [ testProperty "length" prop_sortLength ]
-- Cabal/Stack: depends on QuickCheck, hspec-discover or tasty
```
### Hedgehog
```haskell
-- With Tasty
testGroup "sort" [ Hedgehog.testProperty "length" prop_sortLength ]
-- Cabal/Stack: depends on hedgehog, tasty-hedgehog
```
## Unique Features
### QuickCheck
- **The original**: 25+ years, every Haskell test framework integrates with it
- **Arbitrary type class**: One generator per type, automatic derivation via Generic
- **Quviq commercial version**: Most complete PBT implementation ever built (Erlang)
### Hedgehog
- **Integrated shrinking**: Automatic via rose trees, composes through applicative
- **No orphan instances**: Explicit generators avoid type class problem
- **Range combinators**: Fine control over value scaling with size
- **Parallel stateful testing**: Built-in linearizability checkingRelated Skills
nw-fp-haskell
Haskell language-specific patterns, GADTs, type classes, and effect systems
nw-ux-web-patterns
Web UI design patterns for product owners. Load when designing web application interfaces, writing web-specific acceptance criteria, or evaluating responsive designs.
nw-ux-tui-patterns
Terminal UI and CLI design patterns for product owners. Load when designing command-line tools, interactive terminal applications, or writing CLI-specific acceptance criteria.
nw-ux-principles
Core UX principles for product owners. Load when evaluating interface designs, writing acceptance criteria with UX requirements, or reviewing wireframes and mockups.
nw-ux-emotional-design
Emotional design and delight patterns for product owners. Load when designing onboarding flows, empty states, first-run experiences, or evaluating the emotional quality of an interface.
nw-ux-desktop-patterns
Desktop application UI patterns for product owners. Load when designing native or cross-platform desktop applications, writing desktop-specific acceptance criteria, or evaluating panel layouts and keyboard workflows.
nw-user-story-mapping
User story mapping for backlog management and outcome-based prioritization. Load during Phase 2.5 (User Story Mapping) to produce story-map.md and prioritization.md.
nw-tr-review-criteria
Review dimensions and scoring for root cause analysis quality assessment
nw-tlaplus-verification
TLA+ formal verification for design correctness and PBT pipeline integration
nw-test-refactoring-catalog
Detailed refactoring mechanics with step-by-step procedures, and test code smell catalog with detection patterns and before/after examples
nw-test-organization-conventions
Test directory structure patterns by architecture style, language conventions, naming rules, and fixture placement. Decision tree for selecting test organization strategy.
nw-test-design-mandates
Four design mandates for acceptance tests - hexagonal boundary enforcement, business language abstraction, user journey completeness, walking skeleton strategy, and pure function extraction