{-# LANGUAGE CPP, FlexibleContexts, TypeFamilies, UndecidableInstances, UndecidableSuperClasses #-} -- | Newtypes with instances implemented using generic combinators. -- -- === Warning -- -- This is an internal module: it is not subject to any versioning policy, -- breaking changes can happen at any time. -- -- If something here seems useful, please report it or create a pull request to -- export it from an external module. module Generic.Data.Internal.Generically where import Control.Applicative import Data.Functor.Classes import Data.Semigroup import Data.Ix import GHC.Generics import Text.Read import Generic.Data.Internal.Prelude hiding (gfoldMap, gtraverse, gsequenceA) import Generic.Data.Internal.Enum import Generic.Data.Internal.Error import Generic.Data.Internal.Read import Generic.Data.Internal.Show import Generic.Data.Internal.Traversable (GFoldable, GTraversable, gfoldMap, gtraverse, gsequenceA) -- | Type with instances derived via 'Generic'. -- -- === Examples -- -- ==== __Deriving 'Eq', 'Ord', 'Show', 'Read'__ -- -- >>> :set -XDerivingVia -XDeriveGeneric -- >>> :{ -- data T = C Int Bool -- deriving Generic -- deriving (Eq, Ord, Show, Read) via (Generically T) -- :} -- -- ==== __Deriving 'Semigroup', 'Monoid'__ -- -- The type must have only one constructor. -- -- >>> :{ -- data U = D [Int] (Sum Int) -- deriving Generic -- deriving (Semigroup, Monoid) via (Generically U) -- :} -- -- ==== __Deriving 'Enum', 'Bounded'__ -- -- The type must have only nullary constructors. -- To lift that restriction, see 'FiniteEnumeration'. -- -- >>> :{ -- data V = X | Y | Z -- deriving Generic -- deriving (Eq, Ord, Enum, Bounded) via (Generically V) -- :} newtype Generically a = Generically { unGenerically :: a } instance Generic a => Generic (Generically a) where type Rep (Generically a) = Rep a to = Generically . to from = from . unGenerically instance (Generic a, Eq (Rep a ())) => Eq (Generically a) where (==) = geq instance (Generic a, Ord (Rep a ())) => Ord (Generically a) where compare = gcompare instance (Generic a, GRead0 (Rep a)) => Read (Generically a) where readPrec = greadPrec readListPrec = readListPrecDefault instance (Generic a, GShow0 (Rep a)) => Show (Generically a) where showsPrec = gshowsPrec instance (AssertNoSum Semigroup a, Generic a, Semigroup (Rep a ())) => Semigroup (Generically a) where (<>) = gmappend -- | This uses the 'Semigroup' instance of the wrapped type 'a' to define 'mappend'. -- The purpose of this instance is to derive 'mempty', while remaining consistent -- with possibly custom 'Semigroup' instances. instance (AssertNoSum Semigroup a, Semigroup a, Generic a, Monoid (Rep a ())) => Monoid (Generically a) where mempty = gmempty mappend (Generically x) (Generically y) = Generically (x <> y) instance (Generic a, GEnum StandardEnum (Rep a)) => Enum (Generically a) where toEnum = gtoEnum fromEnum = gfromEnum enumFrom = genumFrom enumFromThen = genumFromThen enumFromTo = genumFromTo enumFromThenTo = genumFromThenTo instance (Generic a, Ord (Rep a ()), GIx (Rep a)) => Ix (Generically a) where range = grange index = gindex inRange = ginRange instance (Generic a, GBounded (Rep a)) => Bounded (Generically a) where minBound = gminBound maxBound = gmaxBound -- | Type with 'Enum' instance derived via 'Generic' with 'FiniteEnum' option. -- This allows deriving 'Enum' for types whose constructors have fields. -- -- Some caution is advised; see details in 'FiniteEnum'. -- -- === __Example__ -- -- >>> :{ -- data Booool = Booool Bool Bool -- deriving Generic -- deriving (Enum, Bounded) via (FiniteEnumeration Booool) -- :} newtype FiniteEnumeration a = FiniteEnumeration { unFiniteEnumeration :: a } instance Generic a => Generic (FiniteEnumeration a) where type Rep (FiniteEnumeration a) = Rep a to = FiniteEnumeration . to from = from . unFiniteEnumeration instance (Generic a, GEnum FiniteEnum (Rep a)) => Enum (FiniteEnumeration a) where toEnum = gtoFiniteEnum fromEnum = gfromFiniteEnum enumFrom = gfiniteEnumFrom enumFromThen = gfiniteEnumFromThen enumFromTo = gfiniteEnumFromTo enumFromThenTo = gfiniteEnumFromThenTo -- | The same instance as 'Generically', for convenience. instance (Generic a, GBounded (Rep a)) => Bounded (FiniteEnumeration a) where minBound = gminBound maxBound = gmaxBound -- | Type with instances derived via 'Generic1'. -- -- === Examples -- -- ==== __Deriving 'Functor', 'Applicative', 'Alternative'__ -- -- 'Applicative' can be derived for types with only one -- constructor, aka. products. -- -- >>> :{ -- data F a = F1 a | F2 (Maybe a) | F3 [Either Bool a] (Int, a) -- deriving Generic1 -- deriving Functor via (Generically1 F) -- :} -- -- >>> :{ -- data G a = G a (Maybe a) [a] (IO a) -- deriving Generic1 -- deriving (Functor, Applicative) via (Generically1 G) -- :} -- -- >>> :{ -- data G' a = G' (Maybe a) [a] -- deriving Generic1 -- deriving (Functor, Applicative, Alternative) via (Generically1 G') -- :} -- -- ==== __Deriving 'Foldable'__ -- -- >>> import Generic.Data.Orphans () -- >>> :{ -- data H a = H1 a | H2 (Maybe a) -- deriving Generic1 -- deriving (Functor, Foldable) via (Generically1 H) -- :} -- -- Note: we can't use @DerivingVia@ for 'Traversable'. -- One may implement 'Traversable' explicitly using 'gtraverse'. -- -- ==== __Deriving 'Eq1', 'Ord1'__ -- -- >>> :{ -- data I a = I [a] (Maybe a) -- deriving Generic1 -- deriving (Eq1, Ord1) via (Generically1 I) -- :} newtype Generically1 f a = Generically1 { unGenerically1 :: f a } instance Generic (f a) => Generic (Generically1 f a) where type Rep (Generically1 f a) = Rep (f a) to = Generically1 . to from = from . unGenerically1 instance Generic1 f => Generic1 (Generically1 f) where type Rep1 (Generically1 f) = Rep1 f to1 = Generically1 . to1 from1 = from1 . unGenerically1 instance (Generic1 f, Eq1 (Rep1 f)) => Eq1 (Generically1 f) where liftEq = gliftEq instance (Generic1 f, Eq1 (Rep1 f), Eq a) => Eq (Generically1 f a) where (==) = eq1 instance (Generic1 f, Ord1 (Rep1 f)) => Ord1 (Generically1 f) where liftCompare = gliftCompare instance (Generic1 f, Ord1 (Rep1 f), Ord a) => Ord (Generically1 f a) where compare = compare1 instance (Generic1 f, GRead1 (Rep1 f)) => Read1 (Generically1 f) where #if MIN_VERSION_base(4,10,0) liftReadPrec = gliftReadPrec liftReadListPrec = liftReadListPrecDefault #else liftReadsPrec rp rl = readPrec_to_S $ gliftReadPrec (readS_to_Prec rp) (readS_to_Prec (const rl)) #endif instance (Generic1 f, GRead1 (Rep1 f), Read a) => Read (Generically1 f a) where #if MIN_VERSION_base(4,10,0) readPrec = readPrec1 readListPrec = readListPrecDefault #else readsPrec = readsPrec1 #endif instance (Generic1 f, GShow1 (Rep1 f)) => Show1 (Generically1 f) where liftShowsPrec = gliftShowsPrec instance (Generic1 f, GShow1 (Rep1 f), Show a) => Show (Generically1 f a) where showsPrec = showsPrec1 instance (Generic1 f, Functor (Rep1 f)) => Functor (Generically1 f) where fmap = gfmap (<$) = gconstmap instance (Generic1 f, Applicative (Rep1 f)) => Applicative (Generically1 f) where pure = gpure (<*>) = gap #if MIN_VERSION_base(4,10,0) liftA2 = gliftA2 #endif instance (Generic1 f, Alternative (Rep1 f)) => Alternative (Generically1 f) where empty = gempty (<|>) = galt instance (Generic1 f, GFoldable (Rep1 f)) => Foldable (Generically1 f) where foldMap = gfoldMap foldr = gfoldr instance (Generic1 f, Functor (Rep1 f), GFoldable (Rep1 f), GTraversable (Rep1 f)) => Traversable (Generically1 f) where traverse = gtraverse sequenceA = gsequenceA -- | Product type with generic instances of 'Semigroup' and 'Monoid'. -- -- This is similar to 'Generic.Data.Generically' in most cases, but -- 'GenericProduct' also works for types @T@ with deriving -- @via 'GenericProduct' U@, where @U@ is a generic product type coercible to, -- but distinct from @T@. In particular, @U@ may not have an instance of -- 'Semigroup', which 'Generic.Data.Generically' requires. -- -- === __Example__ -- -- >>> :set -XDeriveGeneric -XDerivingVia -- >>> data Point a = Point a a deriving Generic -- >>> :{ -- newtype Vector a = Vector (Point a) -- deriving (Semigroup, Monoid) -- via GenericProduct (Point (Sum a)) -- :} -- -- If it were @via 'Generic.Data.Generically' (Point (Sum a))@ instead, then -- @Vector@'s 'mappend' (the 'Monoid' method) would be defined as @Point@'s -- @('<>')@ (the 'Semigroup' method), which might not exist, or might not be -- equivalent to @Vector@'s generic 'Semigroup' instance, which would be -- unlawful. newtype GenericProduct a = GenericProduct { unGenericProduct :: a } instance Generic a => Generic (GenericProduct a) where type Rep (GenericProduct a) = Rep a to = GenericProduct . to from = from . unGenericProduct instance (AssertNoSum Semigroup a, Generic a, Semigroup (Rep a ())) => Semigroup (GenericProduct a) where (<>) = gmappend instance (AssertNoSum Semigroup a, Generic a, Monoid (Rep a ())) => Monoid (GenericProduct a) where mempty = gmempty mappend = gmappend'