{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.Generics.Product.Types
(
HasTypes
, types
) where
import Data.Kind
import GHC.Generics
import Data.Generics.Internal.VL.Traversal
types :: forall a s. HasTypes s a => Traversal' s a
types = types_ @s @a
{-# INLINE types #-}
class HasTypes s a where
types_ :: Traversal' s a
default types_ :: Traversal' s a
types_ _ = pure
{-# INLINE types_ #-}
instance
( HasTypes' (Interesting s a) s a
) => HasTypes s a where
types_ = types' @(Interesting s a)
{-# INLINE types_ #-}
class HasTypes' (t :: Bool) s a where
types' :: Traversal' s a
instance
( GHasTypes (Rep s) a
, Generic s
) => HasTypes' 'True s a where
types' f s = to <$> gtypes_ f (from s)
instance HasTypes' 'False s a where
types' _ = pure
instance {-# OVERLAPPING #-} HasTypes Bool a
instance {-# OVERLAPPING #-} HasTypes Char a
instance {-# OVERLAPPING #-} HasTypes Double a
instance {-# OVERLAPPING #-} HasTypes Float a
instance {-# OVERLAPPING #-} HasTypes Int a
instance {-# OVERLAPPING #-} HasTypes Integer a
instance {-# OVERLAPPING #-} HasTypes Ordering a
class GHasTypes s a where
gtypes_ :: Traversal' (s x) a
instance
( GHasTypes l a
, GHasTypes r a
) => GHasTypes (l :*: r) a where
gtypes_ f (l :*: r) = (:*:) <$> gtypes_ f l <*> gtypes_ f r
{-# INLINE gtypes_ #-}
instance
( GHasTypes l a
, GHasTypes r a
) => GHasTypes (l :+: r) a where
gtypes_ f (L1 l) = L1 <$> gtypes_ f l
gtypes_ f (R1 r) = R1 <$> gtypes_ f r
{-# INLINE gtypes_ #-}
instance (GHasTypes s a) => GHasTypes (M1 m meta s) a where
gtypes_ f (M1 s) = M1 <$> gtypes_ f s
{-# INLINE gtypes_ #-}
instance {-# OVERLAPPING #-} GHasTypes (Rec0 a) a where
gtypes_ f (K1 x) = K1 <$> f x
{-# INLINE gtypes_ #-}
instance HasTypes b a => GHasTypes (Rec0 b) a where
gtypes_ f (K1 x) = K1 <$> types_ @_ @a f x
{-# INLINE gtypes_ #-}
instance GHasTypes U1 a where
gtypes_ _ _ = pure U1
{-# INLINE gtypes_ #-}
instance GHasTypes V1 a where
gtypes_ _ = pure
{-# INLINE gtypes_ #-}
type Interesting f a = Snd (Interesting' (Rep f) a '[f])
type family Interesting' f (a :: Type) (seen :: [Type]) :: ([Type], Bool) where
Interesting' (M1 _ m f) t seen
= Interesting' f t seen
Interesting' (l :*: r) t seen
= InterestingOr (Interesting' l t seen) r t
Interesting' (l :+: r) t seen
= InterestingOr (Interesting' l t seen) r t
Interesting' (Rec0 t) t seen
= '(seen, 'True)
Interesting' (Rec0 Char) _ seen = '(seen ,'False)
Interesting' (Rec0 Double) _ seen = '(seen ,'False)
Interesting' (Rec0 Float) _ seen = '(seen ,'False)
Interesting' (Rec0 Int) _ seen = '(seen ,'False)
Interesting' (Rec0 Integer) _ seen = '(seen ,'False)
Interesting' (Rec0 r) t seen
= InterestingUnless (Elem r seen) (Rep r) t r seen
Interesting' _ _ seen
= '(seen, 'False)
type family InterestingUnless (s :: Bool) f (a :: Type) (r :: Type) (seen :: [Type]) :: ([Type], Bool) where
InterestingUnless 'True _ _ _ seen = '(seen, 'False)
InterestingUnless 'False f a r seen = Interesting' f a (r ': seen)
type family InterestingOr (b :: ([Type], Bool)) r t :: ([Type], Bool) where
InterestingOr '(seen, 'True) _ _ = '(seen, 'True)
InterestingOr '(seen, 'False) r t = Interesting' r t seen
type family Elem a as where
Elem a (a ': _) = 'True
Elem a (_ ': as) = Elem a as
Elem a '[] = 'False
type family Snd a where
Snd '(_, b) = b