polysemy-check-0.1.0.0: src/Polysemy/Check/Arbitrary/AnyEff.hs
module Polysemy.Check.Arbitrary.AnyEff where
import Data.Kind (Type, Constraint)
import GHC.Exts (type (~~))
import Generics.Kind
import Polysemy
import Polysemy.Internal
import {-# SOURCE #-} Polysemy.Check.Arbitrary.Generic
import Test.QuickCheck
------------------------------------------------------------------------------
-- | Helper function for implementing 'GTypesOf'
type family GTypesOf (f :: LoT Effect -> Type) :: [Type] where
GTypesOf (M1 _1 _2 f) = GTypesOf f
GTypesOf (f :+: g) = Append (GTypesOf f) (GTypesOf g)
GTypesOf (('Kon (~~) ':@: Var1 ':@: 'Kon a) :=>: f) = '[a]
GTypesOf (('Kon ((~~) a) ':@: Var1) :=>: f) = '[a]
-- Otherwise, we don't have any constraints on @a@, so we can instantiate it
-- how we please. Just assume ().
GTypesOf _1 = '[()]
------------------------------------------------------------------------------
-- | @'TypesOf' e@ is a list of every type that can be bound via @e@'s actions.
--
-- For example, given:
--
-- @
-- data MyEffect m a where
-- Foo :: MyEffect m Int
-- Blah :: Bool -> MyEffect m String
-- @
--
-- the result of @'TypesOf' MyEffect@ is @'[Int, String]@.
type TypesOf (e :: Effect) = GTypesOf (RepK e)
------------------------------------------------------------------------------
-- | A type family that expands to a 'GArbitraryK' constaint for every type in
-- the first list.
type family ArbitraryForAll (as :: [Type]) (m :: Type -> Type) :: Constraint where
ArbitraryForAll '[] f = ()
ArbitraryForAll (a ': as) f = (Eq a, Show a, GArbitraryK a (RepK (f a)), ArbitraryForAll as f)
------------------------------------------------------------------------------
-- | @'SomeAction' e r@ is some action for effect @e@ in effect row @r@.
data SomeAction e (r :: EffectRow) where
SomeAction
:: (Member e r, Eq a, Show a, CoArbitrary a, Show (e (Sem r) a))
=> e (Sem r) a
-- ^
-> SomeAction e r
-- ^
instance Show (SomeAction e r) where
show (SomeAction ema) = show ema
instance Show (SomeEff r) where
show (SomeEff sse) = show sse
------------------------------------------------------------------------------
-- | @'SomeEff' r@ is some action for some effect in the effect row @r@.
data SomeEff (r :: EffectRow) where
SomeEff
:: (Member e r, Eq a, Show a, CoArbitrary a, Show (e (Sem r) a))
=> e (Sem r) a
-- ^
-> SomeEff r
-- ^
------------------------------------------------------------------------------
-- | @'SomeEff' r@ is some action for some effect in the effect row @r@.
data SomeEffOfType (r :: EffectRow) a where
SomeEffOfType
:: (Member e r, Eq a, Show a, CoArbitrary a, Show (e (Sem r) a))
=> e (Sem r) a
-- ^
-> SomeEffOfType r a
-- ^
------------------------------------------------------------------------------
-- | @'ArbitraryEff' es r@ lets you randomly generate an action in any of
-- the effects @es@.
class ArbitraryEff (es :: EffectRow) (r :: EffectRow) where
genSomeEff :: [Gen (SomeEff r)]
instance ArbitraryEff '[] r where
genSomeEff = []
instance
(ArbitraryEff es r, ArbitraryAction (TypesOf e) e r)
=> ArbitraryEff (e ': es) r
where
genSomeEff = fmap (fmap (\(SomeAction e) -> SomeEff e)) (genSomeAction @(TypesOf e) @e @r)
<> genSomeEff @es @r
------------------------------------------------------------------------------
-- | @'ArbitraryEffOfType' a es r@ lets you randomly generate an action in any of
-- the effects @es@ that produces type @a@.
class ArbitraryEffOfType (a :: Type) (es :: EffectRow) (r :: EffectRow) where
genSomeEffOfType :: [Gen (SomeEffOfType r a)]
instance ArbitraryEffOfType a '[] r where
genSomeEffOfType = []
instance
( Eq a
, Show a
, Show (e (Sem r) a)
, ArbitraryEffOfType a es r
, GenericK (e (Sem r) a)
, GArbitraryK a (RepK (e (Sem r) a))
, CoArbitrary a
, Member e r
)
=> ArbitraryEffOfType a (e ': es) r
where
genSomeEffOfType
= fmap SomeEffOfType (genEff @e @a @(Sem r))
: genSomeEffOfType @a @es @r
------------------------------------------------------------------------------
-- | @'ArbitraryAction' as e r@ lets you randomly generate an action
-- producing any type in @as@ from the effect @e@.
class ArbitraryAction (as :: [Type]) (e :: Effect) (r :: EffectRow) where
genSomeAction :: [Gen (SomeAction e r)]
instance ArbitraryAction '[] e r where
genSomeAction = []
instance
( ArbitraryAction as e r
, Eq a
, Show a
, Member e r
, Show (e (Sem r) a)
, GenericK (e (Sem r) a)
, CoArbitrary a
, GArbitraryK a (RepK (e (Sem r) a))
)
=> ArbitraryAction (a : as) e r
where
genSomeAction = (fmap SomeAction $ genEff @e @a @(Sem r)) : genSomeAction @as @e @r