rel8-1.0.0.0: src/Rel8/Generic/Table/ADT.hs
{-# language AllowAmbiguousTypes #-}
{-# language DataKinds #-}
{-# language FlexibleContexts #-}
{-# language FlexibleInstances #-}
{-# language LambdaCase #-}
{-# language MultiParamTypeClasses #-}
{-# language PolyKinds #-}
{-# language RankNTypes #-}
{-# language ScopedTypeVariables #-}
{-# language StandaloneKindSignatures #-}
{-# language TypeApplications #-}
{-# language TypeFamilies #-}
{-# language TypeOperators #-}
{-# language UndecidableInstances #-}
module Rel8.Generic.Table.ADT
( GTableADT, GColumnsADT, gfromColumnsADT, gtoColumnsADT, gtableADT
, GTableADT', GColumnsADT'
, GToExprsADT, gfromResultADT, gtoResultADT
)
where
-- base
import Data.Kind ( Constraint, Type )
import Data.Proxy ( Proxy( Proxy ) )
import GHC.Generics
( (:+:)( L1, R1 ), M1( M1 ), U1( U1 )
, C, D
, Meta( MetaCons )
)
import GHC.TypeLits ( KnownSymbol, symbolVal )
import Prelude hiding ( null )
-- rel8
import Rel8.FCF ( Eval, Exp )
import Rel8.Generic.Table.Record
( GTable, GColumns, gtable
, GToExprs, gfromResult, gtoResult
)
import Rel8.Schema.Context.Label ( HLabelable, hlabeler, labeler, unlabeler )
import Rel8.Schema.HTable ( HTable, hmap )
import Rel8.Schema.HTable.Identity ( HIdentity( HType ), HType )
import Rel8.Schema.HTable.Label ( HLabel, hlabel, hunlabel )
import Rel8.Schema.HTable.Nullify ( HNullify, hnulls, hnullify, hunnullify )
import Rel8.Schema.HTable.Product ( HProduct( HProduct ) )
import qualified Rel8.Schema.Kind as K
import Rel8.Schema.Null ( Nullify )
import Rel8.Schema.Spec ( Spec( Spec ), SSpec )
import Rel8.Schema.Result
( Col( R ), Result
, null, nullifier, unnullifier
)
import Rel8.Type.Tag ( Tag( Tag ) )
-- text
import Data.Text ( pack )
type GColumnsADT
:: (Type -> Exp K.HTable)
-> (Type -> Type) -> K.HTable
type family GColumnsADT _Columns rep where
GColumnsADT _Columns (M1 D _ rep) =
GColumnsADT' _Columns (HLabel "tag" (HType Tag)) rep
type GColumnsADT'
:: (Type -> Exp K.HTable)
-> K.HTable -> (Type -> Type) -> K.HTable
type family GColumnsADT' _Columns htable rep where
GColumnsADT' _Columns htable (a :+: b) =
GColumnsADT' _Columns (GColumnsADT' _Columns htable a) b
GColumnsADT' _Columns htable (M1 C ('MetaCons _ _ _) U1) = htable
GColumnsADT' _Columns htable (M1 C ('MetaCons label _ _) rep) =
HProduct htable (HLabel label (HNullify (GColumns _Columns rep)))
type GTableADT
:: (Type -> Exp Constraint)
-> (Type -> Exp K.HTable)
-> K.HContext -> (Type -> Type) -> Constraint
class GTableADT _Table _Columns context rep where
gfromColumnsADT :: ()
=> (forall spec. context spec -> Col Result spec)
-> (forall spec. Col Result spec -> context spec)
-> (forall a. Eval (_Table a) => Eval (_Columns a) context -> a)
-> GColumnsADT _Columns rep context
-> rep x
gtoColumnsADT :: ()
=> (forall spec. context spec -> Col Result spec)
-> (forall spec. Col Result spec -> context spec)
-> (forall a. Eval (_Table a) => a -> Eval (_Columns a) context)
-> rep x
-> GColumnsADT _Columns rep context
gtableADT :: ()
=> (forall a proxy. Eval (_Table a) => proxy a -> Eval (_Columns a) context)
-> (forall a labels. ()
=> SSpec ('Spec labels a)
-> context ('Spec labels a)
-> context ('Spec labels (Nullify a)))
-> GColumnsADT _Columns rep context
instance
( htable ~ HLabel "tag" (HType Tag)
, HTable (GColumnsADT _Columns (M1 D meta rep))
, Eval (_Table (htable context))
, Eval (_Columns (htable context)) ~ htable
, GTableADT' _Table _Columns htable context rep
, GToExprsADT' (Const _Table) _Columns htable rep rep
)
=> GTableADT _Table _Columns context (M1 D meta rep)
where
gfromColumnsADT fromContext toContext fromColumns =
gfromResultADT @(Const _Table) @_Columns @(M1 D meta rep) @(M1 D meta rep)
(const (fromColumns . hmap toContext)) .
hmap fromContext
gtoColumnsADT fromContext toContext toColumns =
hmap toContext .
gtoResultADT @(Const _Table) @_Columns @(M1 D meta rep) @(M1 D meta rep)
(const (hmap fromContext . toColumns))
gtableADT table hnullifier =
gtableADT' @_Table @_Columns @htable @context @rep table hnullifier htable
where
htable = table (Proxy @(htable context))
type GTableADT'
:: (Type -> Exp Constraint)
-> (Type -> Exp K.HTable)
-> K.HTable -> K.HContext -> (Type -> Type) -> Constraint
class GTableADT' _Table _Columns htable context rep where
gtableADT' :: ()
=> (forall a proxy. Eval (_Table a) => proxy a -> Eval (_Columns a) context)
-> (forall a labels. ()
=> SSpec ('Spec labels a)
-> context ('Spec labels a)
-> context ('Spec labels (Nullify a)))
-> htable context
-> GColumnsADT' _Columns htable rep context
instance
( htable' ~ GColumnsADT' _Columns htable a
, GTableADT' _Table _Columns htable context a
, GTableADT' _Table _Columns htable' context b
)
=> GTableADT' _Table _Columns htable context (a :+: b)
where
gtableADT' table hnullifier =
gtableADT' @_Table @_Columns @_ @_ @b table hnullifier .
gtableADT' @_Table @_Columns @_ @_ @a table hnullifier
instance meta ~ 'MetaCons label _fixity _isRecord =>
GTableADT' _Table _Columns htable context (M1 C meta U1)
where
gtableADT' _ _ = id
instance {-# OVERLAPPABLE #-}
( HTable (GColumns _Columns rep)
, GTable _Table _Columns context rep
, HLabelable context
, meta ~ 'MetaCons label _fixity _isRecord
, KnownSymbol label
, GColumnsADT' _Columns htable (M1 C ('MetaCons label _fixity _isRecord) rep) ~
HProduct htable (HLabel label (HNullify (GColumns _Columns rep)))
)
=> GTableADT' _Table _Columns htable context (M1 C meta rep)
where
gtableADT' table hnullifier htable =
HProduct htable $
hlabel hlabeler $
hnullify hnullifier $
gtable @_Table @_Columns @_ @rep table
type GToExprsADT
:: (Type -> Type -> Exp Constraint)
-> (Type -> Exp K.HTable)
-> (Type -> Type) -> (Type -> Type) -> Constraint
class GToExprsADT _ToExprs _Columns exprs rep where
gfromResultADT :: ()
=> (forall expr a proxy.
( Eval (_ToExprs expr a)
, HTable (Eval (_Columns expr))
)
=> proxy expr
-> Eval (_Columns expr) (Col Result)
-> a)
-> GColumnsADT _Columns exprs (Col Result)
-> rep x
gtoResultADT :: ()
=> (forall expr a proxy.
( Eval (_ToExprs expr a)
, HTable (Eval (_Columns expr))
)
=> proxy expr
-> a
-> Eval (_Columns expr) (Col Result))
-> rep x
-> GColumnsADT _Columns exprs (Col Result)
instance
( htable ~ HLabel "tag" (HType Tag)
, GToExprsADT' _ToExprs _Columns htable exprs rep
)
=> GToExprsADT _ToExprs _Columns (M1 D meta exprs) (M1 D meta rep)
where
gfromResultADT fromResult columns =
case gfromResultADT' @_ToExprs @_Columns @_ @exprs fromResult tag columns of
Just rep -> M1 rep
_ -> error "ADT.fromColumns: mismatch between tag and data"
where
tag = (\(HType (R a)) -> a) . hunlabel @_ @"tag" unlabeler
gtoResultADT toResult (M1 rep) =
gtoResultADT' @_ToExprs @_Columns @_ @exprs toResult tag (Just rep)
where
tag = hlabel @_ @"tag" labeler . HType . R
type GToExprsADT'
:: (Type -> Type -> Exp Constraint)
-> (Type -> Exp K.HTable)
-> K.HTable -> (Type -> Type) -> (Type -> Type) -> Constraint
class GToExprsADT' _ToExprs _Columns htable exprs rep where
gfromResultADT' :: ()
=> (forall expr a proxy.
( Eval (_ToExprs expr a)
, HTable (Eval (_Columns expr))
)
=> proxy expr
-> Eval (_Columns expr) (Col Result)
-> a)
-> (htable (Col Result) -> Tag)
-> GColumnsADT' _Columns htable exprs (Col Result)
-> Maybe (rep x)
gtoResultADT' :: ()
=> (forall expr a proxy.
( Eval (_ToExprs expr a)
, HTable (Eval (_Columns expr))
)
=> proxy expr
-> a
-> Eval (_Columns expr) (Col Result))
-> (Tag -> htable (Col Result))
-> Maybe (rep x)
-> GColumnsADT' _Columns htable exprs (Col Result)
extract :: GColumnsADT' _Columns htable exprs context -> htable context
instance
( htable' ~ GColumnsADT' _Columns htable a
, GToExprsADT' _ToExprs _Columns htable a rep1
, GToExprsADT' _ToExprs _Columns htable' b rep2
)
=> GToExprsADT' _ToExprs _Columns htable (a :+: b) (rep1 :+: rep2)
where
gfromResultADT' fromResult f columns =
case ma of
Just a -> Just (L1 a)
Nothing -> R1 <$>
gfromResultADT' @_ToExprs @_Columns @_ @b @rep2
fromResult
(f . extract @_ToExprs @_Columns @_ @a @rep1)
columns
where
ma =
gfromResultADT' @_ToExprs @_Columns @_ @a @rep1
fromResult
f
(extract @_ToExprs @_Columns @_ @b @rep2 columns)
gtoResultADT' toResult tag = \case
Just (L1 a) ->
gtoResultADT' @_ToExprs @_Columns @_ @b @rep2
toResult
(\_ -> gtoResultADT' @_ToExprs @_Columns @_ @a @rep1
toResult
tag
(Just a))
Nothing
Just (R1 b) ->
gtoResultADT' @_ToExprs @_Columns @_ @b @rep2
toResult
(\tag' ->
gtoResultADT' @_ToExprs @_Columns @_ @a @rep1
toResult
(\_ -> tag tag')
Nothing)
(Just b)
Nothing ->
gtoResultADT' @_ToExprs @_Columns @_ @b @rep2
toResult
(\_ -> gtoResultADT' @_ToExprs @_Columns @_ @a @rep1 toResult tag Nothing)
Nothing
extract =
extract @_ToExprs @_Columns @_ @a @rep1 .
extract @_ToExprs @_Columns @_ @b @rep2
instance
( meta ~ 'MetaCons label _fixity _isRecord
, KnownSymbol label
)
=> GToExprsADT' _ToExprs _Columns htable (M1 C meta U1) (M1 C meta U1)
where
gfromResultADT' _ tag columns
| tag columns == tag' = Just (M1 U1)
| otherwise = Nothing
where
tag' = Tag $ pack $ symbolVal (Proxy @label)
gtoResultADT' _ tag _ = tag tag'
where
tag' = Tag $ pack $ symbolVal (Proxy @label)
extract = id
instance {-# OVERLAPPABLE #-}
( HTable (GColumns _Columns exprs)
, GToExprs _ToExprs _Columns exprs rep
, meta ~ 'MetaCons label _fixity _isRecord
, KnownSymbol label
, GColumnsADT' _Columns htable (M1 C meta exprs) ~
HProduct htable (HLabel label (HNullify (GColumns _Columns exprs)))
)
=> GToExprsADT' _ToExprs _Columns htable (M1 C meta exprs) (M1 C meta rep)
where
gfromResultADT' fromResult tag (HProduct a b)
| tag a == tag' =
M1 . gfromResult @_ToExprs @_Columns @exprs fromResult <$>
hunnullify unnullifier (hunlabel unlabeler b)
| otherwise = Nothing
where
tag' = Tag $ pack $ symbolVal (Proxy @label)
gtoResultADT' toResult tag = \case
Nothing -> HProduct (tag tag') (hlabel labeler (hnulls (const null)))
Just (M1 rep) -> HProduct (tag tag') $
hlabel labeler $
hnullify nullifier $
gtoResult @_ToExprs @_Columns @exprs toResult rep
where
tag' = Tag $ pack $ symbolVal (Proxy @label)
extract (HProduct a _) = a
data Const :: (a -> Exp Constraint) -> a -> a -> Exp Constraint
type instance Eval (Const f x a) = (Eval (f a), x ~ a)