rel8-1.7.0.0: src/Rel8/Generic/Construction.hs
{-# language AllowAmbiguousTypes #-}
{-# language ConstraintKinds #-}
{-# language DataKinds #-}
{-# language FlexibleContexts #-}
{-# language NamedFieldPuns #-}
{-# language ScopedTypeVariables #-}
{-# language StandaloneKindSignatures #-}
{-# language TypeApplications #-}
{-# language TypeFamilies #-}
{-# language TypeOperators #-}
{-# language UndecidableInstances #-}
{-# language ViewPatterns #-}
module Rel8.Generic.Construction
( GGBuildable
, GGBuild, ggbuild
, GGConstructable
, GGConstruct, ggconstruct
, GGDeconstruct, ggdeconstruct, ggdeconstructA
, GGName, ggname
)
where
-- base
import Data.Bifunctor ( first )
import Data.Functor ((<&>))
import Data.Kind ( Constraint, Type )
import Data.List.NonEmpty ( NonEmpty( (:|) ) )
import GHC.TypeLits ( Symbol )
import Prelude
-- rel8
import Rel8.Expr ( Expr )
import Rel8.Expr.Eq ( (==.) )
import Rel8.Expr.Null ( nullify, snull, unsafeUnnullify )
import Rel8.Expr.Serialize ( litExpr )
import Rel8.FCF ( Eval, Exp, Id )
import Rel8.Generic.Construction.ADT
( GConstructorADT, GMakeableADT, gmakeADT
, GConstructableADT
, GBuildADT, gbuildADT
, GConstructADT, gconstructADT, gdeconstructADT
, RepresentableConstructors, GConstructors, gcindex, gctabulate
, RepresentableFields, gftabulate
)
import Rel8.Generic.Construction.Record
( GConstructor
, GConstructable, GConstruct, gconstruct, gdeconstruct
, Representable, gindex, gtabulate
)
import Rel8.Generic.Table ( GGColumns )
import Rel8.Kind.Algebra
( SAlgebra( SProduct, SSum )
, KnownAlgebra, algebraSing
)
import qualified Rel8.Kind.Algebra as K
import Rel8.Schema.HTable ( HTable )
import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) )
import qualified Rel8.Schema.Kind as K
import Rel8.Schema.Name ( Name( Name ) )
import Rel8.Schema.Null ( Nullity( Null, NotNull ) )
import Rel8.Schema.Spec ( Spec( Spec, nullity, info ) )
import Rel8.Table
( TTable, TColumns
, Table, fromColumns, toColumns
)
import Rel8.Table.Bool ( case_ )
import Rel8.Type.Tag ( Tag )
-- semigroupoids
import Data.Functor.Apply (Apply)
import Data.Semigroup.Traversable (sequence1, traverse1)
type GGBuildable :: K.Algebra -> Symbol -> (K.Context -> Exp (Type -> Type)) -> Constraint
type GGBuildable algebra name rep =
( KnownAlgebra algebra
, Eval (GGColumns algebra TColumns (Eval (rep Expr))) ~ Eval (GGColumns algebra TColumns (Eval (rep Expr)))
, Eval (GGColumns algebra TColumns (Eval (rep Name))) ~ Eval (GGColumns algebra TColumns (Eval (rep Expr)))
, HTable (Eval (GGColumns algebra TColumns (Eval (rep Expr))))
, GGBuildable' algebra name rep
)
type GGBuildable' :: K.Algebra -> Symbol -> (K.Context -> Exp (Type -> Type)) -> Constraint
type family GGBuildable' algebra name rep where
GGBuildable' 'K.Product name rep =
( name ~ GConstructor (Eval (rep Expr))
, Representable Id (Eval (rep Expr))
, GConstructable (TTable Expr) TColumns Id Expr (Eval (rep Expr))
)
GGBuildable' 'K.Sum name rep =
( Representable Id (GConstructorADT name (Eval (rep Expr)))
, GMakeableADT (TTable Expr) TColumns Id Expr name (Eval (rep Expr))
)
type GGBuild :: K.Algebra -> Symbol -> (K.Context -> Exp (Type -> Type)) -> Type -> Type
type family GGBuild algebra name rep r where
GGBuild 'K.Product _name rep r =
GConstruct Id (Eval (rep Expr)) r
GGBuild 'K.Sum name rep r =
GConstruct Id (GConstructorADT name (Eval (rep Expr))) r
ggbuild :: forall algebra name rep a. GGBuildable algebra name rep
=> (Eval (GGColumns algebra TColumns (Eval (rep Expr))) Expr -> a)
-> GGBuild algebra name rep a
ggbuild gfromColumns = case algebraSing @algebra of
SProduct ->
gtabulate @Id @(Eval (rep Expr)) @a $
gfromColumns .
gconstruct
@(TTable Expr)
@TColumns
@Id
@Expr
@(Eval (rep Expr))
(const toColumns)
SSum ->
gtabulate @Id @(GConstructorADT name (Eval (rep Expr))) @a $
gfromColumns .
gmakeADT
@(TTable Expr)
@TColumns
@Id
@Expr
@name
@(Eval (rep Expr))
(const toColumns)
(\Spec {info} -> snull info)
(\Spec {nullity} -> case nullity of
Null -> id
NotNull -> nullify)
(HIdentity . litExpr)
type GGConstructable :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Constraint
type GGConstructable algebra rep =
( KnownAlgebra algebra
, Eval (GGColumns algebra TColumns (Eval (rep Expr))) ~ Eval (GGColumns algebra TColumns (Eval (rep Expr)))
, Eval (GGColumns algebra TColumns (Eval (rep Name))) ~ Eval (GGColumns algebra TColumns (Eval (rep Expr)))
, HTable (Eval (GGColumns algebra TColumns (Eval (rep Expr))))
, GGConstructable' algebra rep
)
type GGConstructable' :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Constraint
type family GGConstructable' algebra rep where
GGConstructable' 'K.Product rep =
( Representable Id (Eval (rep Expr))
, Representable Id (Eval (rep Name))
, GConstructable (TTable Expr) TColumns Id Expr (Eval (rep Expr))
, GConstructable (TTable Name) TColumns Id Name (Eval (rep Name))
)
GGConstructable' 'K.Sum rep =
( RepresentableConstructors Id (Eval (rep Expr))
, RepresentableFields Id (Eval (rep Expr))
, RepresentableFields Id (Eval (rep Name))
, Functor (GConstructors Id (Eval (rep Expr)))
, GConstructableADT (TTable Expr) TColumns Id Expr (Eval (rep Expr))
, GConstructableADT (TTable Name) TColumns Id Name (Eval (rep Name))
)
type GGConstruct :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Type -> Type
type family GGConstruct algebra rep r where
GGConstruct 'K.Product rep r = GConstruct Id (Eval (rep Expr)) r -> r
GGConstruct 'K.Sum rep r = GConstructADT Id (Eval (rep Expr)) r r
ggconstruct :: forall algebra rep a. GGConstructable algebra rep
=> (Eval (GGColumns algebra TColumns (Eval (rep Expr))) Expr -> a)
-> GGConstruct algebra rep a -> a
ggconstruct gfromColumns f = case algebraSing @algebra of
SProduct ->
f $
gtabulate @Id @(Eval (rep Expr)) @a $
gfromColumns .
gconstruct
@(TTable Expr)
@TColumns
@Id
@Expr
@(Eval (rep Expr))
(const toColumns)
SSum ->
gcindex @Id @(Eval (rep Expr)) @a f $
fmap gfromColumns $
gconstructADT
@(TTable Expr)
@TColumns
@Id
@Expr
@(Eval (rep Expr))
(const toColumns)
(\Spec {info} -> snull info)
(\Spec {nullity} -> case nullity of
Null -> id
NotNull -> nullify)
(HIdentity . litExpr)
type GGDeconstruct :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Type -> Type -> Type
type family GGDeconstruct algebra rep a r where
GGDeconstruct 'K.Product rep a r =
GConstruct Id (Eval (rep Expr)) r -> a -> r
GGDeconstruct 'K.Sum rep a r =
GConstructADT Id (Eval (rep Expr)) r (a -> r)
ggdeconstruct :: forall algebra rep a r. (GGConstructable algebra rep, Table Expr r)
=> (a -> Eval (GGColumns algebra TColumns (Eval (rep Expr))) Expr)
-> GGDeconstruct algebra rep a r
ggdeconstruct gtoColumns = case algebraSing @algebra of
SProduct -> \build ->
gindex @Id @(Eval (rep Expr)) @r build .
gdeconstruct
@(TTable Expr)
@TColumns
@Id
@Expr
@(Eval (rep Expr))
(const fromColumns) .
gtoColumns
SSum ->
gctabulate @Id @(Eval (rep Expr)) @r @(a -> r) $ \constructors as ->
let
(HIdentity tag, cases) =
gdeconstructADT
@(TTable Expr)
@TColumns
@Id
@Expr
@(Eval (rep Expr))
(const fromColumns)
(\Spec {nullity} -> case nullity of
Null -> id
NotNull -> unsafeUnnullify)
constructors $
gtoColumns as
in
case cases of
((_, r) :| (map (first ((tag ==.) . litExpr)) -> cases')) ->
case_ cases' r
ggdeconstructA :: forall algebra rep a f r. (GGConstructable algebra rep, Apply f, Table Expr r)
=> (a -> Eval (GGColumns algebra TColumns (Eval (rep Expr))) Expr)
-> GGDeconstruct algebra rep a (f r)
ggdeconstructA gtoColumns = case algebraSing @algebra of
SProduct -> \build ->
gindex @Id @(Eval (rep Expr)) @(f r) build .
gdeconstruct
@(TTable Expr)
@TColumns
@Id
@Expr
@(Eval (rep Expr))
(const fromColumns) .
gtoColumns
SSum ->
gctabulate @Id @(Eval (rep Expr)) @(f r) @(a -> f r) $ \constructors as ->
let
(HIdentity tag, cases) =
gdeconstructADT
@(TTable Expr)
@TColumns
@Id
@Expr
@(Eval (rep Expr))
(const fromColumns)
(\Spec {nullity} -> case nullity of
Null -> id
NotNull -> unsafeUnnullify)
constructors $
gtoColumns as
fcases = traverse1 sequence1 cases
in
fcases
<&> \((_, r) :| (map (first ((tag ==.) . litExpr)) -> cases')) ->
case_ cases' r
type GGName :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Type -> Type
type family GGName algebra rep a where
GGName 'K.Product rep a = GConstruct Id (Eval (rep Name)) a
GGName 'K.Sum rep a = Name Tag -> GBuildADT Id (Eval (rep Name)) a
ggname :: forall algebra rep a. GGConstructable algebra rep
=> (Eval (GGColumns algebra TColumns (Eval (rep Expr))) Name -> a)
-> GGName algebra rep a
ggname gfromColumns = case algebraSing @algebra of
SProduct ->
gtabulate @Id @(Eval (rep Name)) @a $
gfromColumns .
gconstruct
@(TTable Name)
@TColumns
@Id
@Name
@(Eval (rep Name))
(const toColumns)
SSum -> \tag ->
gftabulate @Id @(Eval (rep Name)) @a $
gfromColumns .
gbuildADT
@(TTable Name)
@TColumns
@Id
@Name
@(Eval (rep Name))
(const toColumns)
(\_ _ (Name a) -> Name a)
(HIdentity tag)