{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} module Data.Monoid.Generic ( genericMappend , genericMempty , GenericSemigroup(..) , GenericMonoid(..) ) where import Data.Semigroup.Generic import GHC.Generics import GHC.TypeLits -- | A newtype which allows you to using the @DerivingVia@ extension -- to reduce boilerplate. -- -- @ -- data X = X [Int] String -- deriving (Generic, Show) -- deriving Semigroup via GenericSemigroup X -- deriving Monoid via GenericMonoid X -- @ -- -- Note: Do NOT attempt to @derive Semigroup via GenericMonoid@. That will lead -- to infinite recursion. newtype GenericMonoid a = GenericMonoid a deriving Show instance Semigroup a => Semigroup (GenericMonoid a) where GenericMonoid a <> GenericMonoid b = GenericMonoid $ a <> b instance (Semigroup a, Generic a, MemptyProduct (Rep a)) => Monoid (GenericMonoid a) where mempty = GenericMonoid genericMempty -- | A generic @`mempty`@ function which works for product types where each -- contained type is itself a @`Monoid`@. It simply calls @`mempty`@ for -- each field. -- -- If you don't want to use the @deriving via@ mechanism, use this function -- to implement the `Monoid` type class. genericMempty :: (Generic a, MemptyProduct (Rep a)) => a genericMempty = to genericMempty' class MemptyProduct f where genericMempty' :: f k instance MemptyProduct c => MemptyProduct (D1 md c) where genericMempty' = M1 genericMempty' instance MemptyProduct s => MemptyProduct (C1 md s) where genericMempty' = M1 genericMempty' instance (TypeError (Text "You can't use `genericMempty` for sum types")) => MemptyProduct (a :+: b) where genericMempty' = undefined instance (MemptyProduct a, MemptyProduct b) => MemptyProduct (a :*: b) where genericMempty' = genericMempty' :*: genericMempty' instance Monoid t => MemptyProduct (S1 m (Rec0 t)) where genericMempty' = M1 (K1 mempty)