{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.Generics.Product.Param
( Rec (Rec)
, HasParam (..)
) where
import GHC.TypeLits
import Data.Generics.Internal.Void
import Data.Generics.Internal.Families.Changing
import Data.Generics.Internal.VL.Traversal
import GHC.Generics
import Data.Kind
import Data.Generics.Internal.VL.Iso
import Data.Generics.Internal.GenericN
class HasParam (p :: Nat) s t a b | p t a -> s, p s b -> t, p s -> a, p t -> b where
param :: Applicative g => (a -> g b) -> s -> g t
instance
( GenericN s
, GenericN t
, s ~ Infer t (P n b 'PTag) a
, t ~ Infer s (P n a 'PTag) b
, Error ((ArgCount s) <=? n) n (ArgCount s) s
, a ~ ArgAt s n
, b ~ ArgAt t n
, GHasParam n (RepN s) (RepN t) a b
) => HasParam n s t a b where
param = confusing (\f s -> toN <$> gparam @n f (fromN s))
{-# INLINE param #-}
type family Error (b :: Bool) (expected :: Nat) (actual :: Nat) (s :: Type) :: Constraint where
Error 'False _ _ _
= ()
Error 'True expected actual typ
= TypeError
( 'Text "Expected a type with at least "
':<>: 'ShowType (expected + 1)
':<>: 'Text " parameters, but "
':$$: 'ShowType typ
':<>: 'Text " only has "
':<>: 'ShowType actual
)
instance {-# OVERLAPPING #-} HasParam p (Void1 a) (Void1 b) a b where
param = undefined
class GHasParam (p :: Nat) s t a b where
gparam :: forall g (x :: Type). Applicative g => (a -> g b) -> s x -> g (t x)
instance (GHasParam p l l' a b, GHasParam p r r' a b) => GHasParam p (l :*: r) (l' :*: r') a b where
gparam f (l :*: r) = (:*:) <$> gparam @p f l <*> gparam @p f r
instance (GHasParam p l l' a b, GHasParam p r r' a b) => GHasParam p (l :+: r) (l' :+: r') a b where
gparam f (L1 l) = L1 <$> gparam @p f l
gparam f (R1 r) = R1 <$> gparam @p f r
instance GHasParam p U1 U1 a b where
gparam _ _ = pure U1
instance GHasParam p s t a b => GHasParam p (M1 m meta s) (M1 m meta t) a b where
gparam f (M1 x) = M1 <$> gparam @p f x
instance GHasParam p (Rec (param p) a) (Rec (param p) b) a b where
gparam = recIso
instance {-# OVERLAPPABLE #-}
( GHasParamRec (LookupParam si p) s t a b
) => GHasParam p (Rec si s) (Rec ti t) a b where
gparam f (Rec (K1 x)) = Rec . K1 <$> gparamRec @(LookupParam si p) f x
class GHasParamRec (param :: Maybe Nat) s t a b | param t a b -> s, param s a b -> t where
gparamRec :: forall g. Applicative g => (a -> g b) -> s -> g t
instance GHasParamRec 'Nothing a a c d where
gparamRec _ = pure
instance (HasParam n s t a b) => GHasParamRec ('Just n) s t a b where
gparamRec = param @n