{-# LANGUAGE ApplicativeDo #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeOperators #-} module Test.Hedgehog.Applicative where import Control.Monad.Morph (hoist) import Control.Monad.State.Class (MonadState(..), modify) import qualified Control.Monad.Trans.State.Lazy as Lazy import Data.Foldable (traverse_) import qualified Data.List as List import qualified Data.Map as Map import Hedgehog hiding (Command, Var) import qualified Hedgehog.Range as Range import qualified Hedgehog.Internal.Gen as Gen import qualified Hedgehog.Internal.Tree as Tree newtype Var = Var Int deriving (Eq, Ord, Show) data Command = Add | Remove deriving (Eq, Ord, Show) data a :<- b = a :<- b deriving (Eq, Ord, Show) takeVar :: a :<- b -> a takeVar (var :<- _) = var genVar :: (MonadState Int m, MonadGen m) => m Var genVar = do modify (+1) Var <$> get genCommand :: MonadGen m => m Command genCommand = Gen.element [Add, Remove] genCommands :: (MonadState Int m, MonadGen m) => m [Var :<- Command] genCommands = Gen.list (Range.constant 0 3) $ do var <- genVar cmd <- genCommand pure $ var :<- cmd -- | Uncomment to observe invalid Applicative behaviour -- -- /This actually also works, if you comment out the ApplicativeDo above./ -- xprop_StateT_inside :: Property xprop_StateT_inside = propVars $ hoist (`Lazy.evalStateT` 0) genCommands prop_StateT_outside :: Property prop_StateT_outside = propVars . (`Lazy.evalStateT` 0) $ distributeT genCommands propVars :: Gen [Var :<- Command] -> Property propVars gen = property $ do let tree <- forAllWith (Tree.render . fmap show . Tree.prune 3) $ Gen.toTree gen let noDuplicates xs = let sorted = List.sort xs unique = Map.elems (Map.fromList (fmap (\x -> (takeVar x, x)) xs)) varsEq ys zs = fmap takeVar ys == fmap takeVar zs in diff sorted varsEq unique traverse_ noDuplicates tree tests :: IO Bool tests = checkParallel $$(discover)