{-# LANGUAGE CPP #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.Generics.Product.Subtype
(
Subtype (..)
) where
import Data.Generics.Internal.Families
import Data.Generics.Internal.VL.Lens as VL
import Data.Generics.Internal.Void
import Data.Generics.Product.Internal.Subtype
import GHC.Generics (Generic (Rep, to, from) )
import GHC.TypeLits (Symbol, TypeError, ErrorMessage (..))
import Data.Kind (Type, Constraint)
import Data.Generics.Internal.Profunctor.Lens hiding (set)
import Data.Generics.Internal.Errors
class Subtype sup sub where
super :: VL.Lens sub sub sup sup
super
= VL.lens upcast (uncurry smash . swap)
upcast :: sub -> sup
upcast s = s ^. super @sup
smash :: sup -> sub -> sub
smash = VL.set (super @sup)
{-# MINIMAL super | smash, upcast #-}
instance
( Generic a
, Generic b
, GSmash (Rep a) (Rep b)
, GUpcast (Rep a) (Rep b)
, CustomError a b
) => Subtype b a where
smash p b = to $ gsmash (from p) (from b)
upcast = to . gupcast . from
type family CustomError a b :: Constraint where
CustomError a b =
( ErrorUnless b a (CollectFieldsOrdered (Rep b) \\ CollectFieldsOrdered (Rep a))
, Defined (Rep a)
(NoGeneric a '[ 'Text "arising from a generic lens focusing on " ':<>: QuoteType b
, 'Text "as a supertype of " ':<>: QuoteType a
])
(() :: Constraint)
, Defined (Rep b)
(NoGeneric b '[ 'Text "arising from a generic lens focusing on " ':<>: QuoteType b
, 'Text "as a supertype of " ':<>: QuoteType a
])
(() :: Constraint)
)
instance {-# OVERLAPPING #-} Subtype a a where
super = id
#if __GLASGOW_HASKELL__ < 804
#else
#endif
instance {-# OVERLAPPING #-} Subtype a Void where
super = undefined
#if __GLASGOW_HASKELL__ < 804
#else
#endif
instance {-# OVERLAPPING #-} Subtype Void a where
super = undefined
type family ErrorUnless (sup :: Type) (sub :: Type) (diff :: [Symbol]) :: Constraint where
ErrorUnless _ _ '[]
= ()
ErrorUnless sup sub fs
= TypeError
( 'Text "The type '"
':<>: 'ShowType sub
':<>: 'Text "' is not a subtype of '"
':<>: 'ShowType sup ':<>: 'Text "'."
':$$: 'Text "The following fields are missing from '"
':<>: 'ShowType sub ':<>: 'Text "':"
':$$: ShowSymbols fs
)