linear-base-0.8.1: src/Prelude/Linear/GenericUtil.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
-- | @'FixupMetaData' a g@ copies the metadata from the
-- @"GHC.Generics".'GHC.Generics.Generic'@ representation of @a@ to the
-- representation @g@.
--
-- @'FixupMetaData1' f g@ does something similar when @f 'Any'@ is an instance
-- of @Generic@ and @g@ is a @Rep1@. See the individual type documentation
-- for details.
--
-- This is intended to help users instantiate 'Rep' and 'Rep1' for types with
-- nonlinear or multiplicity-polymorphic fields.
--
-- == Suggested use
--
-- You will need to derive a @"GHC.Generics.Generic"@ instance for the
-- type. This is used to obtain the correct metadata.
--
-- Next you need to construct a @Rep@ and/or @Rep1@ for your type, ignoring the
-- metadata.
--
-- Constructing the actual representations can be a bit annoying, but GHC can
-- help.
--
-- === For 'Rep'
--
-- Once you have derived @"GHC.Generics".'GHC.Generics.Generic'@ for your
-- type, define a value like
--
-- @
-- test :: Rep T a
-- test = _
-- @
--
-- Then compile. The stripped representation you need will be in the error
-- message.
--
-- === For 'Rep1'
--
-- Construct a type with the same shape as the one you wish to
-- instantiate, but with only linear fields. Strictness annotations
-- and @UNPACK@ pragmas are irrelevant here.
--
-- Instantiate @"Generics.Linear".'Generic1'@ for the lookalike using
-- 'Generics.Linear.TH.deriveGeneric1' and follow the same procedure
-- as above (but with @'Rep1' T@, of course) to get a metadata-stripped
-- representation.
--
-- === For either
--
-- To avoid confusion, replace at least the package and module names in the
-- representation with 'Any'. Wrap @MP1@ around any nonlinear/representation
-- polymorphic fields, just under the @S1@ type constructor. The first type
-- argument of @MP1@ will indicate the multiplicity.
module Prelude.Linear.GenericUtil
( FixupMetaData,
FixupMetaData1,
RemoveMetaData,
)
where
import Data.Kind (Type)
import qualified GHC.Generics
import GHC.TypeLits
import Generics.Linear
-- | @FixupMetaData a g@ copies the metadata from the
-- @"GHC.Generics".'GHC.Generics.Generic'@ representation of @a@ to the
-- representation @g@. It also checks that the structure of @Rep a@ is the
-- same as @g@, except that @g@ may have @MP1@ applications under some @S1@
-- constructors.
--
-- === Example
--
-- @
-- instance 'Generic' ('Prelude.Linear.Ur' a) where
-- type Rep (Ur a) = FixupMetaData (Ur a)
-- (D1 Any
-- (C1 Any
-- (S1 Any
-- (MP1 \'Many (Rec0 a)))))
-- @
type FixupMetaData (a :: Type) (g :: Type -> Type) =
Fixup (GHC.Generics.Rep a) g
-- | @FixupMetaData1 f g@ copies the metadata from the
-- @"GHC.Generics".'GHC.Generics.Generic'@ representation of @f 'Any'@
-- to the representation @g@. It also checks that the overall structure of
-- @Rep (f 'Any')@ is the same as @g@, but does not check that their fields
-- match.
--
-- === Example
--
-- @
-- instance 'Generic1' 'Prelude.Linear.Ur' where
-- type Rep1 Ur = FixupMetaData1 Ur
-- (D1 Any
-- (C1 Any
-- (S1 Any
-- (MP1 \'Many Par1))))
-- @
type FixupMetaData1 (f :: k -> Type) (g :: k -> Type) =
Fixup1 (GHC.Generics.Rep (f Any)) g
type family Fixup (f :: Type -> Type) (g :: Type -> Type) :: Type -> Type where
Fixup (D1 c f) (D1 _c g) = D1 c (Fixup f g)
Fixup (C1 c f) (C1 _c g) = C1 c (Fixup f g)
Fixup (S1 c f) (S1 _c (MP1 m f)) = S1 c (MP1 m f)
Fixup (S1 c f) (S1 _c f) = S1 c f
Fixup (f :*: g) (f' :*: g') = Fixup f f' :*: Fixup g g'
Fixup (f :+: g) (f' :+: g') = Fixup f f' :+: Fixup g g'
Fixup V1 V1 = V1
Fixup _ _ = TypeError ('Text "FixupMetaData: representations do not match.")
type family Fixup1 (f :: Type -> Type) (g :: k -> Type) :: k -> Type where
Fixup1 (D1 c f) (D1 _c g) = D1 c (Fixup1 f g)
Fixup1 (C1 c f) (C1 _c g) = C1 c (Fixup1 f g)
Fixup1 (f :*: g) (f' :*: g') = Fixup1 f f' :*: Fixup1 g g'
Fixup1 (f :+: g) (f' :+: g') = Fixup1 f f' :+: Fixup1 g g'
Fixup1 (S1 c _f) (S1 _c g) = S1 c g
Fixup1 V1 V1 = V1
Fixup1 _ _ = TypeError ('Text "Fixup1MetaData1: representations do not match.")
type family RemoveMetaData (f :: k -> Type) :: k -> Type where
RemoveMetaData (D1 _c f) = D1 Any (RemoveMetaData f)
RemoveMetaData (C1 _c f) = C1 Any (RemoveMetaData f)
RemoveMetaData (S1 _c f) = S1 Any f
RemoveMetaData (f :*: g) = RemoveMetaData f :*: RemoveMetaData g
RemoveMetaData (f :+: g) = RemoveMetaData f :+: RemoveMetaData g
RemoveMetaData x = x
type family Any :: k