@@ -43,6 +43,7 @@ import Control.Monad
4343import Control.Monad.Reader
4444import Control.Monad.Writer (WriterT , runWriterT , tell )
4545import Data.Data
46+ import Data.Kind
4647import Data.List
4748import Data.Monoid (Endo (.. ))
4849import Data.Set qualified as Set
@@ -102,13 +103,6 @@ class
102103 -- anything.
103104 data Action state a
104105
105- -- | The type of errors that actions can throw. If this is defined as anything
106- -- other than `Void` `perform` is required to return `Either (Error state) a`
107- -- instead of `a`.
108- type Error state
109-
110- type Error state = Void
111-
112106 -- | Display name for `Action`.
113107 -- This is useful to provide sensible statistics about the distribution of `Action`s run
114108 -- when checking a property.
@@ -158,22 +152,6 @@ class
158152
159153deriving instance (forall a . Show (Action state a )) => Show (Any (Action state ))
160154
161- -- | The result required of `perform` depending on the `Error` type
162- -- of a state model. If there are no errors, `Error state = Void`, and
163- -- so we don't need to specify if the action failed or not.
164- type family PerformResult e a where
165- PerformResult Void a = a
166- PerformResult e a = Either e a
167-
168- class IsPerformResult e a where
169- performResultToEither :: PerformResult e a -> Either e a
170-
171- instance {-# OVERLAPPING #-} IsPerformResult Void a where
172- performResultToEither = Right
173-
174- instance {-# OVERLAPPABLE #-} (PerformResult e a ~ Either e a ) => IsPerformResult e a where
175- performResultToEither = id
176-
177155newtype PostconditionM m a = PostconditionM { runPost :: WriterT (Endo Property , Endo Property ) m a }
178156 deriving (Functor , Applicative , Monad )
179157
@@ -197,7 +175,33 @@ monitorPost m = PostconditionM $ tell (Endo m, mempty)
197175counterexamplePost :: Monad m => String -> PostconditionM m ()
198176counterexamplePost c = PostconditionM $ tell (mempty , Endo $ counterexample c)
199177
178+ -- | The result required of `perform` depending on the `Error` type.
179+ -- If there are no errors, `Error state = Void`, and
180+ -- so we don't need to specify if the action failed or not.
181+ type family PerformResult state (m :: Type -> Type ) a where
182+ PerformResult state m a = EitherIsh (Error state m ) a
183+
184+ type family EitherIsh e a where
185+ EitherIsh Void a = a
186+ EitherIsh e a = Either e a
187+
188+ class IsPerformResult e a where
189+ performResultToEither :: EitherIsh e a -> Either e a
190+
191+ instance {-# OVERLAPPING #-} IsPerformResult Void a where
192+ performResultToEither = Right
193+
194+ instance {-# OVERLAPPABLE #-} (EitherIsh e a ~ Either e a ) => IsPerformResult e a where
195+ performResultToEither = id
196+
200197class (forall a . Show (Action state a ), Monad m ) => RunModel state m where
198+ -- | The type of errors that actions can throw. If this is defined as anything
199+ -- other than `Void` `perform` is required to return `Either (Error state) a`
200+ -- instead of `a`.
201+ type Error state m
202+
203+ type Error state m = Void
204+
201205 -- | Perform an `Action` in some `state` in the `Monad` `m`. This
202206 -- is the function that's used to exercise the actual stateful
203207 -- implementation, usually through various side-effects as permitted
@@ -208,7 +212,7 @@ class (forall a. Show (Action state a), Monad m) => RunModel state m where
208212 --
209213 -- The `Lookup` parameter provides an /environment/ to lookup `Var
210214 -- a` instances from previous steps.
211- perform :: Typeable a => state -> Action state a -> LookUp -> m (PerformResult ( Error state ) a )
215+ perform :: Typeable a => state -> Action state a -> LookUp -> m (PerformResult state m a )
212216
213217 -- | Postcondition on the `a` value produced at some step.
214218 -- The result is `assert`ed and will make the property fail should it be `False`. This is useful
@@ -220,18 +224,18 @@ class (forall a. Show (Action state a), Monad m) => RunModel state m where
220224 -- The result is `assert`ed and will make the property fail should it be `False`. This is useful
221225 -- to check the implementation produces e.g. the expected errors or to check that the SUT hasn't
222226 -- been updated during the execution of the negative action.
223- postconditionOnFailure :: (state , state ) -> Action state a -> LookUp -> Either (Error state ) a -> PostconditionM m Bool
227+ postconditionOnFailure :: (state , state ) -> Action state a -> LookUp -> Either (Error state m ) a -> PostconditionM m Bool
224228 postconditionOnFailure _ _ _ _ = pure True
225229
226230 -- | Allows the user to attach additional information to the `Property` at each step of the process.
227231 -- This function is given the full transition that's been executed, including the start and ending
228232 -- `state`, the `Action`, the current environment to `Lookup` and the value produced by `perform`
229233 -- while executing this step.
230- monitoring :: (state , state ) -> Action state a -> LookUp -> Either (Error state ) a -> Property -> Property
234+ monitoring :: (state , state ) -> Action state a -> LookUp -> Either (Error state m ) a -> Property -> Property
231235 monitoring _ _ _ _ prop = prop
232236
233237 -- | Allows the user to attach additional information to the `Property` if a positive action fails.
234- monitoringFailure :: state -> Action state a -> LookUp -> Error state -> Property -> Property
238+ monitoringFailure :: state -> Action state a -> LookUp -> Error state m -> Property -> Property
235239 monitoringFailure _ _ _ _ prop = prop
236240
237241type LookUp = forall a . Typeable a = > Var a -> a
@@ -488,7 +492,7 @@ runActions
488492 :: forall state m e
489493 . ( StateModel state
490494 , RunModel state m
491- , e ~ Error state
495+ , e ~ Error state m
492496 , forall a . IsPerformResult e a
493497 )
494498 => Actions state
@@ -506,7 +510,7 @@ runSteps
506510 :: forall state m e
507511 . ( StateModel state
508512 , RunModel state m
509- , e ~ Error state
513+ , e ~ Error state m
510514 , forall a . IsPerformResult e a
511515 )
512516 => Annotated state
0 commit comments