Skip to content
Development
Skill

/nw-pbt-haskell

Haskell property-based testing with QuickCheck and Hedgehog frameworks

From plugin
nwave
635183 skills37 agents

Claude Code

$ npx -y skills add nWave-ai/nWave --skill nw-pbt-haskell --agent claude-code

SKILL.md

nw-pbt-haskell.SKILL.md
name: nw-pbt-haskell
agent: nw-functional-software-crafter
description: Haskell property-based testing with QuickCheck and Hedgehog frameworks
user-invocable: false

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)

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

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

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

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)

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)

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

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.

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

-- 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

-- 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 or
Read more
@nwave-ai
Ships withnwave

nWave supplies specialist roles and a host-led delivery workflow for software teams.

635183 skills37 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
635
Star history of nWave-ai/nWave on GitHub, a chart by star-history.com

Other skills on nwave.

nw-ab-agent-template

nw-ab-agent-template

KNOWLEDGE — the canonical agent-spec template (frontmatter + body skeleton). Reference loaded by the create/migrate procedures;…

@nwave-ai@nwave-aiInstall Skill
nw-ab-anti-patterns

nw-ab-anti-patterns

KNOWLEDGE — agent/skill/command anti-pattern catalog with fixes. Reference scanned by validate-spec; no sequence.

@nwave-ai@nwave-aiInstall Skill
nw-ab-create-agent

nw-ab-create-agent

PROCEDURE — create a NEW agent via the 5-phase workflow (ANALYZE→DESIGN→CREATE→VALIDATE→REFINE). Trigger: build a new AI agent.…

@nwave-ai@nwave-aiInstall Skill
nw-ab-examples

nw-ab-examples

KNOWLEDGE — canonical worked examples for agent creation, migration, and command optimization. Reference for the relevant…

@nwave-ai@nwave-aiInstall Skill
nw-ab-house-style

nw-ab-house-style

KNOWLEDGE — caveman-native authoring house style + by-construction guarantees (Reasoning Mandate injection, measured-gain…

@nwave-ai@nwave-aiInstall Skill

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills13d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc2w293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react4d252k
@facebook@facebookInstall Skill

Questions about nw-pbt-haskell.

Which coding agents can use nw-pbt-haskell?
nw-pbt-haskell installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.