{-# language DataKinds #-}
{-# language FlexibleContexts #-}
{-# language ScopedTypeVariables #-}
{-# language TypeFamilies #-}
{-# language TypeOperators #-}
{-# language UndecidableInstances #-}
{- |
Generic implementation of a generator. Example usage:
@
data Foo = Foo
{ _fooX :: X
, _fooY :: Y
} deriving (Generic)
genFoo :: Gen Foo
genFoo = hgen
@
The generated generator is equivalent to
@Foo \<$\> hgen \<*\> hgen@.
-}
module Hedgehog.Generic
( HGen(..)
, hgen
) where
import Control.Applicative (liftA2)
import Data.Proxy (Proxy(..))
import GHC.Generics
import GHC.TypeLits
import Hedgehog
import qualified Hedgehog.Gen as Gen
-- | A class used to generate generators for types implementing 'Generic'.
class HGen a where
hgen' :: Gen (a x)
instance HGen U1 where
hgen' = pure U1
instance (Generic c, HGen (Rep c)) => HGen (K1 i c) where
hgen' = K1 <$> hgen
instance HGen f => HGen (M1 i c f) where
hgen' = M1 <$> hgen'
instance (HGen a, HGen b) => HGen (a :*: b) where
hgen' = liftA2 (:*:) hgen' hgen'
instance
forall a b. (KnownNat (SumLen a), KnownNat (SumLen b), HGen a, HGen b)
=> HGen (a :+: b) where
hgen' = Gen.frequency
[ (lfreq, L1 <$> hgen')
, (rfreq, R1 <$> hgen')
]
where
lfreq = fromIntegral $ natVal (Proxy :: Proxy (SumLen a))
rfreq = fromIntegral $ natVal (Proxy :: Proxy (SumLen b))
type family SumLen a :: Nat where
SumLen (a :+: b) = SumLen a + SumLen b
SumLen _ = 1
-- | If your type implements 'Generic', you can get a generator for
-- your type 'for free' using this function.
hgen :: (Generic a, HGen (Rep a)) => Gen a
hgen = to <$> hgen'