generic-labels-0.1.0.1: src/Data/Generic/Labels/Internal.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-|
Module: Data.Generic.Labels.Internal
Internal module containing the generics machinery to provide the instances
exported by this library, using 'Data.Generics.Product.Internal.GLens.GLens'.
__Warnings__:
* contains an incoherent instance for 'GAdapt' which is used to
prioritise built-in record field names over explicitly labelled types;
* contains an orphan overlapping instance for @generic-lens@'s 'Data.Generics.Product.Internal.GLens.GLens'
typeclass, which is used to additionally unwrap through labelled types.
-}
module Data.Generic.Labels.Internal
( GAdapt(..) )
where
-- base
import Data.Kind
( Constraint, Type )
import GHC.Generics
import GHC.TypeLits
( Symbol )
-- generic-lens-core
import Data.Generics.Product.Internal.GLens
( Eval, GLens(..), GLens', TyFun )
import Data.Generics.Internal.Profunctor.Lens
( view )
import Data.Generics.Internal.Profunctor.Iso
( Iso, iso, kIso )
-- generic-labels
import Data.Label
( (:=)(..), Label(..) )
import Data.Generic.Labels.Internal.Errors
( AdaptLabelMessage )
--------------------------------------------------------------------------------
-- Generics machinery for 'Adapt'.
-- | Generic version of 'Data.Generic.Labels.Adapt'.
type GAdapt :: ( Type -> Type ) -> ( Type -> Type ) -> ( Type -> Type ) -> Constraint
class GAdapt args opt all where
gAdapt :: args p -> opt p -> all p
instance ( GAdapt args opt all1, GAdapt args opt all2 ) => GAdapt args opt ( all1 :*: all2 ) where
gAdapt args opt = gAdapt args opt :*: gAdapt args opt
instance GAdapt args opt all => GAdapt args opt ( C1 c all ) where
gAdapt args opt = M1 $ gAdapt args opt
instance GAdapt args opt all => GAdapt args opt ( D1 c all ) where
gAdapt args opt = M1 $ gAdapt args opt
-- | This instance is INCOHERENT because we assume that no type variable (say @all0@)
-- will later be instantiated to a labelled type @lbl := all@.
--
-- The end result is that, when we have both a built-in Haskell record field name
-- as well as an explicit label, we prioritise the built-in record field name over the label.
instance {-# INCOHERENT #-}
( GLens' ( HasTotalLabelPSym lbl ) ( args :*: opts ) all )
=> GAdapt args opts ( M1 m meta ( Rec0 ( lbl := all ) ) )
where
gAdapt args opt = M1 . K1 . ( Label @lbl := ) $ view ( glens @( HasTotalLabelPSym lbl ) ) ( args :*: opt )
instance ( GLens' ( HasTotalLabelPSym lbl ) ( args :*: opts ) all )
=> GAdapt args opts ( S1 ( MetaSel ( Just lbl ) p f b ) ( Rec0 all ) )
where
gAdapt args opt = M1 . K1 $ view ( glens @( HasTotalLabelPSym lbl ) ) ( args :*: opt )
--------------------------------------------------------------------------------
-- Generic lens machinery.
type And :: Maybe a -> Maybe a -> Maybe a
type family m1 `And` m2 where
Just a `And` Just a = Just a
_ `And` _ = Nothing
type Or :: Maybe a -> Maybe a -> Maybe a
type family m1 `Or` m2 where
Just a `Or` _ = Just a
_ `Or` b = b
type HasTotalLabelP :: Symbol -> ( Type -> Type ) -> Maybe Type
type family HasTotalLabelP lbl f where
HasTotalLabelP lbl ( S1 ( MetaSel ( Just lbl ) _ _ _ ) ( Rec0 ty ) ) =
Just ty
HasTotalLabelP lbl ( S1 ( MetaSel ( Just lbl' ) _ _ _ ) _ ) =
Nothing
HasTotalLabelP lbl ( S1 _ ( K1 _ ( lbl := ty ) ) ) =
Just ty
HasTotalLabelP lbl ( S1 _ ( K1 _ ( lbl' := _ ) ) ) =
Nothing
HasTotalLabelP lbl ( l :*: r ) =
HasTotalLabelP lbl l `Or` HasTotalLabelP lbl r
HasTotalLabelP lbl ( l :+: r ) =
HasTotalLabelP lbl l `And` HasTotalLabelP lbl r
HasTotalLabelP lbl ( S1 _ _ ) =
Nothing
HasTotalLabelP lbl ( C1 _ f ) =
HasTotalLabelP lbl f
HasTotalLabelP lbl ( D1 _ f ) =
HasTotalLabelP lbl f
HasTotalLabelP lbl ( K1 _ _ ) =
Nothing
HasTotalLabelP lbl U1 =
Nothing
HasTotalLabelP lbl V1 =
Nothing
type HasTotalLabelPSym :: Symbol -> TyFun ( Type -> Type ) ( Maybe Type )
data HasTotalLabelPSym lbl f mbTy
type instance Eval ( HasTotalLabelPSym lbl ) f = HasTotalLabelP lbl f
class LabelIso mbLbl1 mbLbl2 s t a b | mbLbl1 s -> a, mbLbl2 t -> b where
lblIso :: Iso s t a b
instance
( AdaptLabelMessage lbl ( Just a1 ) Nothing b1
, a1 ~ a, b1 ~ b
) => LabelIso ( Just lbl ) ( Just lbl ) ( lbl := a1 ) ( lbl := b1 ) a b where
lblIso = iso ( \ ( _ := a ) -> a ) ( Label @lbl := )
{-# INLINE lblIso #-}
instance LabelIso Nothing Nothing a b a b where
lblIso = id
{-# INLINE lblIso #-}
type GetLabel :: Type -> Maybe Symbol
type family GetLabel ty where
GetLabel ( lbl := _ ) = Just lbl
GetLabel _ = Nothing
instance {-# OVERLAPPABLE #-} LabelIso ( GetLabel a' ) ( GetLabel b' ) a' b' a b
=> GLens pred ( K1 r a' ) ( K1 r b' ) a b where
glens = kIso . lblIso @( GetLabel a' ) @( GetLabel b' )
{-# INLINE glens #-}