rel8-1.0.0.0: src/Rel8/Schema/HTable.hs
{-# language AllowAmbiguousTypes #-}
{-# language DataKinds #-}
{-# language DefaultSignatures #-}
{-# language FlexibleContexts #-}
{-# language FlexibleInstances #-}
{-# language FunctionalDependencies #-}
{-# language LambdaCase #-}
{-# language RankNTypes #-}
{-# language ScopedTypeVariables #-}
{-# language StandaloneKindSignatures #-}
{-# language TypeApplications #-}
{-# language TypeFamilyDependencies #-}
{-# language TypeOperators #-}
{-# language UndecidableInstances #-}
module Rel8.Schema.HTable
( HTable (HField, HConstrainTable)
, hfield, htabulate, htraverse, hdicts, hspecs
, hmap, htabulateA
)
where
-- base
import Data.Kind ( Constraint, Type )
import Data.Functor.Compose ( Compose( Compose ), getCompose )
import Data.Proxy ( Proxy )
import GHC.Generics
( (:*:)( (:*:) )
, Generic (Rep, from, to)
, K1( K1 )
, M1( M1 )
)
import Prelude
-- rel8
import Rel8.Schema.Dict ( Dict )
import Rel8.Schema.Spec ( Spec, SSpec )
import Rel8.Schema.HTable.Product ( HProduct( HProduct ) )
import qualified Rel8.Schema.Kind as K
-- semigroupoids
import Data.Functor.Apply ( Apply, (<.>) )
-- | A @HTable@ is a functor-indexed/higher-kinded data type that is
-- representable ('htabulate'/'hfield'), constrainable ('hdicts'), and
-- specified ('hspecs').
--
-- This is an internal concept for Rel8, and you should not need to define
-- instances yourself or specify this constraint.
type HTable :: K.HTable -> Constraint
class HTable t where
type HField t = (field :: Spec -> Type) | field -> t
type HConstrainTable t (c :: Spec -> Constraint) :: Constraint
hfield :: t context -> HField t spec -> context spec
htabulate :: (forall spec. HField t spec -> context spec) -> t context
htraverse :: Apply m => (forall spec. f spec -> m (g spec)) -> t f -> m (t g)
hdicts :: HConstrainTable t c => t (Dict c)
hspecs :: t SSpec
type HField t = GHField t
type HConstrainTable t c = HConstrainTable (GHColumns (Rep (t Proxy))) c
default hfield ::
( Generic (t context)
, HField t ~ GHField t
, HField (GHColumns (Rep (t Proxy))) ~ HField (GHColumns (Rep (t context)))
, GHTable context (Rep (t context))
)
=> t context -> HField t spec -> context spec
hfield table (GHField field) = hfield (toGHColumns (from table)) field
default htabulate ::
( Generic (t context)
, HField t ~ GHField t
, HField (GHColumns (Rep (t Proxy))) ~ HField (GHColumns (Rep (t context)))
, GHTable context (Rep (t context))
)
=> (forall spec. HField t spec -> context spec) -> t context
htabulate f = to $ fromGHColumns $ htabulate (f . GHField)
default htraverse
:: forall f g m
. ( Apply m
, Generic (t f), GHTable f (Rep (t f))
, Generic (t g), GHTable g (Rep (t g))
, GHColumns (Rep (t f)) ~ GHColumns (Rep (t g))
)
=> (forall spec. f spec -> m (g spec)) -> t f -> m (t g)
htraverse f = fmap (to . fromGHColumns) . htraverse f . toGHColumns . from
default hdicts
:: forall c
. ( Generic (t (Dict c))
, GHTable (Dict c) (Rep (t (Dict c)))
, GHColumns (Rep (t Proxy)) ~ GHColumns (Rep (t (Dict c)))
, HConstrainTable (GHColumns (Rep (t Proxy))) c
)
=> t (Dict c)
hdicts = to $ fromGHColumns (hdicts @(GHColumns (Rep (t Proxy))) @c)
default hspecs ::
( Generic (t SSpec)
, GHTable SSpec (Rep (t SSpec))
)
=> t SSpec
hspecs = to $ fromGHColumns hspecs
{-# INLINABLE hfield #-}
{-# INLINABLE htabulate #-}
{-# INLINABLE htraverse #-}
{-# INLINABLE hdicts #-}
{-# INLINABLE hspecs #-}
hmap :: HTable t
=> (forall spec. context spec -> context' spec) -> t context -> t context'
hmap f a = htabulate $ \field -> f (hfield a field)
htabulateA :: (HTable t, Apply m)
=> (forall spec. HField t spec -> m (context spec)) -> m (t context)
htabulateA f = htraverse getCompose $ htabulate $ Compose . f
{-# INLINABLE htabulateA #-}
type GHField :: K.HTable -> Spec -> Type
newtype GHField t spec = GHField (HField (GHColumns (Rep (t Proxy))) spec)
type GHTable :: K.HContext -> (Type -> Type) -> Constraint
class HTable (GHColumns rep) => GHTable context rep | rep -> context where
type GHColumns rep :: K.HTable
toGHColumns :: rep x -> GHColumns rep context
fromGHColumns :: GHColumns rep context -> rep x
instance GHTable context rep => GHTable context (M1 i c rep) where
type GHColumns (M1 i c rep) = GHColumns rep
toGHColumns (M1 a) = toGHColumns a
fromGHColumns = M1 . fromGHColumns
instance HTable table => GHTable context (K1 i (table context)) where
type GHColumns (K1 i (table context)) = table
toGHColumns (K1 a) = a
fromGHColumns = K1
instance (GHTable context a, GHTable context b) => GHTable context (a :*: b) where
type GHColumns (a :*: b) = HProduct (GHColumns a) (GHColumns b)
toGHColumns (a :*: b) = HProduct (toGHColumns a) (toGHColumns b)
fromGHColumns (HProduct a b) = fromGHColumns a :*: fromGHColumns b
-- | A HField type for indexing into HProduct.
type HProductField :: K.HTable -> K.HTable -> Spec -> Type
data HProductField x y spec
= HFst (HField x spec)
| HSnd (HField y spec)
instance (HTable x, HTable y) => HTable (HProduct x y) where
type HConstrainTable (HProduct x y) c = (HConstrainTable x c, HConstrainTable y c)
type HField (HProduct x y) = HProductField x y
hfield (HProduct l r) = \case
HFst i -> hfield l i
HSnd i -> hfield r i
htabulate f = HProduct (htabulate (f . HFst)) (htabulate (f . HSnd))
htraverse f (HProduct x y) = HProduct <$> htraverse f x <.> htraverse f y
hdicts = HProduct hdicts hdicts
hspecs = HProduct hspecs hspecs
{-# INLINABLE hfield #-}
{-# INLINABLE htabulate #-}
{-# INLINABLE htraverse #-}
{-# INLINABLE hdicts #-}
{-# INLINABLE hspecs #-}