content: {-# language AllowAmbiguousTypes #-}
  {-# language DataKinds #-}
  {-# language FlexibleContexts #-}
  {-# language FlexibleInstances #-}
  {-# language MultiParamTypeClasses #-}
  {-# language RankNTypes #-}
  {-# language ScopedTypeVariables #-}
  {-# language StandaloneKindSignatures #-}
  {-# language TypeApplications #-}
  {-# language TypeFamilies #-}
  {-# language TypeOperators #-}
  {-# language UndecidableInstances #-}
  
  module Rel8.Generic.Table
    ( GGSerialize, GGColumns, ggfromResult, ggtoResult
    , GAlgebra
    )
  where
  
  -- base
  import Data.Kind ( Constraint, Type )
  import GHC.Generics ( (:+:), (:*:), K1, M1, U1, V1 )
  import Prelude ()
  
  -- rel8
  import Rel8.FCF ( Eval, Exp )
  import Rel8.Generic.Table.ADT
    ( GSerializeADT, GColumnsADT, gtoResultADT, gfromResultADT
    )
  import Rel8.Generic.Table.Record ( GSerialize, GColumns, gtoResult, gfromResult )
  import Rel8.Kind.Algebra
    ( Algebra( Product, Sum )
    , SAlgebra( SProduct, SSum )
    , KnownAlgebra, algebraSing
    )
  import qualified Rel8.Schema.Kind as K
  import Rel8.Schema.Result ( Result )
  
  
  data GGSerialize
    :: Algebra
    -> (Type -> Type -> Exp Constraint)
    -> (Type -> Exp K.HTable)
    -> (Type -> Type)
    -> (Type -> Type)
    -> Exp Constraint
  
  
  type instance Eval (GGSerialize 'Product _Serialize _Columns exprs rep) =
    GSerialize _Serialize _Columns exprs rep
  
  
  type instance Eval (GGSerialize 'Sum _Serialize _Columns exprs rep) =
    GSerializeADT _Serialize _Columns exprs rep
  
  
  data GGColumns
    :: Algebra
    -> (Type -> Exp K.HTable)
    -> (Type -> Type)
    -> Exp K.HTable
  
  
  type instance Eval (GGColumns 'Product _Columns rep) = GColumns _Columns rep
  
  
  type instance Eval (GGColumns 'Sum _Columns rep) = GColumnsADT _Columns rep
  
  
  type GAlgebra :: (Type -> Type) -> Algebra
  type family GAlgebra rep where
    GAlgebra (M1 _ _ rep) = GAlgebra rep
    GAlgebra V1 = 'Sum
    GAlgebra (_ :+: _) = 'Sum
    GAlgebra U1 = 'Sum
    GAlgebra (_ :*: _) = 'Product
    GAlgebra (K1 _ _) = 'Product
  
  
  ggfromResult :: forall algebra _Serialize _Columns exprs rep x.
    ( KnownAlgebra algebra
    , Eval (GGSerialize algebra _Serialize _Columns exprs rep)
    )
    => (forall expr a proxy. Eval (_Serialize expr a)
        => proxy expr -> Eval (_Columns expr) Result -> a)
    -> Eval (GGColumns algebra _Columns exprs) Result
    -> rep x
  ggfromResult f x = case algebraSing @algebra of
    SProduct -> gfromResult @_Serialize @_Columns @exprs @rep f x
    SSum -> gfromResultADT @_Serialize @_Columns @exprs @rep f x
  
  
  ggtoResult :: forall algebra _Serialize _Columns exprs rep x.
    ( KnownAlgebra algebra
    , Eval (GGSerialize algebra _Serialize _Columns exprs rep)
    )
    => (forall expr a proxy. Eval (_Serialize expr a)
        => proxy expr -> a -> Eval (_Columns expr) Result)
    -> rep x
    -> Eval (GGColumns algebra _Columns exprs) Result
  ggtoResult f x = case algebraSing @algebra of
    SProduct -> gtoResult @_Serialize @_Columns @exprs @rep f x
    SSum -> gtoResultADT @_Serialize @_Columns @exprs @rep f x
  