{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
module Data.Semigroup.Generic
( genericMappend
, GenericSemigroup(..)
) where
import GHC.TypeLits
import Data.Semigroup
import GHC.Generics
newtype GenericSemigroup a = GenericSemigroup a
instance
(Generic a, MappendProduct (Rep a))
=> Semigroup (GenericSemigroup a) where
(GenericSemigroup a) <> (GenericSemigroup b)
= GenericSemigroup $ genericMappend a b
genericMappend :: (Generic a, MappendProduct (Rep a)) => a -> a -> a
genericMappend a b = to $ from a `genericMappend'` from b
class MappendProduct f where
genericMappend' :: f k -> f k -> f k
instance
(TypeError (Text "You can't use `genericMappend` for sum types"))
=> MappendProduct (a :+: b) where
genericMappend' = undefined
instance MappendProduct c => MappendProduct (D1 md c) where
genericMappend' (M1 a) (M1 b) = M1 (genericMappend' a b)
instance MappendProduct s => MappendProduct (C1 mc s) where
genericMappend' (M1 a) (M1 b) = M1 (genericMappend' a b)
instance (MappendProduct a, MappendProduct b) => MappendProduct (a :*: b) where
genericMappend' (a :*: b) (a' :*: b')
= genericMappend' a a' :*: genericMappend' b b'
instance Semigroup t => MappendProduct (S1 m (Rec0 t)) where
genericMappend' (M1 (K1 a)) (M1 (K1 b)) = M1 (K1 (a <> b))