@@ -0,0 +1,482 @@
1+{-# language AllowAmbiguousTypes #-}
2+{-# language ConstraintKinds #-}
3+{-# language DataKinds #-}
4+{-# language FlexibleContexts #-}
5+{-# language FlexibleInstances #-}
6+{-# language LambdaCase #-}
7+{-# language MultiParamTypeClasses #-}
8+{-# language NamedFieldPuns #-}
9+{-# language ScopedTypeVariables #-}
10+{-# language StandaloneKindSignatures #-}
11+{-# language TypeApplications #-}
12+{-# language TypeFamilies #-}
13+{-# language UndecidableInstances #-}
14+{-# language ViewPatterns #-}
16+module Rel8.Generic.Construction
18+ , GGConstruct, ggconstruct
19+ , GGDeconstruct, ggdeconstruct
22+ , GGAggregate, ggaggregate
23+ , GGAggregate', ggaggregate'
28+import Data.Bifunctor ( first )
29+import Data.Kind ( Constraint, Type )
30+import Data.List.NonEmpty ( NonEmpty( (:|) ) )
31+import Data.Type.Equality ( (:~:)( Refl ) )
35+import Rel8.Aggregate ( Col( Aggregation ), Aggregate )
36+import Rel8.Expr ( Col( DB ), Expr )
37+import Rel8.Expr.Aggregate ( groupByExpr )
38+import Rel8.Expr.Eq ( (==.) )
39+import Rel8.Expr.Null ( nullify, snull, unsafeUnnullify )
40+import Rel8.Expr.Serialize ( litExpr )
41+import Rel8.FCF ( Compose, Eval, Exp )
42+import Rel8.Generic.Construction.ADT
44+ , GBuildADT, gbuildADT, gunbuildADT
45+ , GConstructADT, gconstructADT, gdeconstructADT
46+ , CorepConstructors, GConstructors, gcindex, gctabulate
47+ , CorepFields, gfindex, gftabulate, gftraverse
49+import Rel8.Generic.Construction.Record
50+ ( GConstructable, GConstruct, gconstruct, gdeconstruct
51+ , Corep, gindex, gtabulate, gtraverse
53+import Rel8.Generic.Table ( GGColumns )
54+import Rel8.Kind.Algebra
55+ ( SAlgebra( SProduct, SSum )
56+ , KnownAlgebra, algebraSing
58+import qualified Rel8.Kind.Algebra as K
59+import Rel8.Kind.Necessity ( SNecessity( SOptional, SRequired ) )
60+import Rel8.Schema.Context.Nullify ( runTag )
61+import Rel8.Schema.HTable ( HTable )
62+import Rel8.Schema.HTable.Type ( HType( HType ) )
63+import Rel8.Schema.Insert ( Col( OptionalInsert, RequiredInsert ), Insert )
64+import qualified Rel8.Schema.Kind as K
65+import Rel8.Schema.Name ( Col( NameCol ), Name( Name ) )
66+import Rel8.Schema.Null ( Nullity( Null, NotNull ) )
67+import Rel8.Schema.Spec ( SSpec( SSpec, necessity, nullity, info ) )
68+import Rel8.Schema.Reify ( Col( Reify ), Reify, hreify, hunreify )
69+import Rel8.Schema.Result ( Result )
71+ ( TTable, TColumns, TUnreify
72+ , Table, fromColumns, toColumns, reify, unreify
74+import Rel8.Table.Bool ( case_ )
75+import Rel8.Type.Tag ( Tag )
78+import Data.Functor.Apply ( Apply, MaybeApply( MaybeApply ), liftF2 )
81+type GGConstructable :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Constraint
82+type GGConstructable algebra rep =
83+ ( KnownAlgebra algebra
84+ , Eval (GGColumns algebra TColumns (Eval (rep (Reify Aggregate)))) ~ Eval (GGColumns algebra TColumns (Eval (rep (Reify Result))))
85+ , Eval (GGColumns algebra TColumns (Eval (rep (Reify Expr)))) ~ Eval (GGColumns algebra TColumns (Eval (rep (Reify Result))))
86+ , Eval (GGColumns algebra TColumns (Eval (rep (Reify Insert)))) ~ Eval (GGColumns algebra TColumns (Eval (rep (Reify Result))))
87+ , Eval (GGColumns algebra TColumns (Eval (rep (Reify Name)))) ~ Eval (GGColumns algebra TColumns (Eval (rep (Reify Result))))
88+ , HTable (Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))))
89+ , GGConstructable' algebra rep
93+type GGConstructable' :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Constraint
94+type family GGConstructable' algebra rep where
95+ GGConstructable' 'K.Product rep =
96+ ( Corep (Compose Aggregate TUnreify) (Eval (rep (Reify Expr)))
97+ , Corep TUnreify (Eval (rep (Reify Aggregate)))
98+ , Corep TUnreify (Eval (rep (Reify Expr)))
99+ , Corep TUnreify (Eval (rep (Reify Insert)))
100+ , Corep TUnreify (Eval (rep (Reify Name)))
101+ , GConstructable (TTable (Reify Aggregate)) TColumns TUnreify (Col (Reify Aggregate)) (Eval (rep (Reify Aggregate)))
102+ , GConstructable (TTable (Reify Expr)) TColumns TUnreify (Col (Reify Expr)) (Eval (rep (Reify Expr)))
103+ , GConstructable (TTable (Reify Insert)) TColumns TUnreify (Col (Reify Insert)) (Eval (rep (Reify Insert)))
104+ , GConstructable (TTable (Reify Name)) TColumns TUnreify (Col (Reify Name)) (Eval (rep (Reify Name)))
106+ GGConstructable' 'K.Sum rep =
107+ ( CorepConstructors TUnreify (Eval (rep (Reify Expr)))
108+ , CorepConstructors TUnreify (Eval (rep (Reify Insert)))
109+ , CorepFields (Compose Aggregate TUnreify) (Eval (rep (Reify Expr)))
110+ , CorepFields TUnreify (Eval (rep (Reify Aggregate)))
111+ , CorepFields TUnreify (Eval (rep (Reify Expr)))
112+ , CorepFields TUnreify (Eval (rep (Reify Name)))
113+ , Functor (GConstructors TUnreify (Eval (rep (Reify Expr))))
114+ , Functor (GConstructors TUnreify (Eval (rep (Reify Insert))))
115+ , GConstructableADT (TTable (Reify Aggregate)) TColumns TUnreify (Col (Reify Aggregate)) (Eval (rep (Reify Aggregate)))
116+ , GConstructableADT (TTable (Reify Expr)) TColumns TUnreify (Col (Reify Expr)) (Eval (rep (Reify Expr)))
117+ , GConstructableADT (TTable (Reify Insert)) TColumns TUnreify (Col (Reify Insert)) (Eval (rep (Reify Insert)))
118+ , GConstructableADT (TTable (Reify Name)) TColumns TUnreify (Col (Reify Name)) (Eval (rep (Reify Name)))
122+type GGConstruct :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Type -> Type
123+type family GGConstruct algebra rep a where
124+ GGConstruct 'K.Product rep a = GConstruct TUnreify (Eval (rep (Reify Expr))) a
125+ GGConstruct 'K.Sum rep a =
126+ -- Ideally this would be as follows, but GHC complains about "illegal polymorphic type"
127+ -- (forall r. GConstructADT TUnreify (Eval (rep (Reify Expr))) r r) -> a
128+ GConstructADT TUnreify (Eval (rep (Reify Expr))) a a -> a
131+ggconstruct :: forall algebra rep a. GGConstructable algebra rep
132+ => (Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))) (Col Expr) -> a)
133+ -> GGConstruct algebra rep a
134+ggconstruct gfromColumns = case algebraSing @algebra of
136+ gtabulate @TUnreify @(Eval (rep (Reify Expr))) @a $
140+ @(TTable (Reify Expr))
144+ @(Eval (rep (Reify Expr)))
145+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
147+ gcindex @TUnreify @(Eval (rep (Reify Expr))) @a f $
148+ fmap (gfromColumns . hunreify) $
150+ @(TTable (Reify Expr))
154+ @(Eval (rep (Reify Expr)))
155+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
156+ (\SSpec {info} -> Reify (DB (snull info)))
157+ (\SSpec {nullity} -> case nullity of
159+ NotNull -> \(Reify (DB a)) -> Reify (DB (nullify a)))
160+ (HType . Reify . DB . litExpr)
163+type GGDeconstruct :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Type -> Type -> Type
164+type family GGDeconstruct algebra rep a r where
165+ GGDeconstruct 'K.Product rep a r =
166+ GConstruct TUnreify (Eval (rep (Reify Expr))) r -> a -> r
167+ GGDeconstruct 'K.Sum rep a r =
168+ GConstructADT TUnreify (Eval (rep (Reify Expr))) r (a -> r)
171+ggdeconstruct :: forall algebra rep a r. (GGConstructable algebra rep, Table Expr r)
172+ => (a -> Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))) (Col Expr))
173+ -> GGDeconstruct algebra rep a r
174+ggdeconstruct gtoColumns = case algebraSing @algebra of
175+ SProduct -> \build ->
176+ gindex @TUnreify @(Eval (rep (Reify Expr))) @r build .
178+ @(TTable (Reify Expr))
182+ @(Eval (rep (Reify Expr)))
183+ (\(_ :: proxy x) -> unreify @_ @x Refl . fromColumns) .
187+ gctabulate @TUnreify @(Eval (rep (Reify Expr))) @r @(a -> r) $ \constructors as ->
189+ (HType (Reify (DB tag)), cases) =
191+ @(TTable (Reify Expr))
195+ @(Eval (rep (Reify Expr)))
196+ (\(_ :: proxy x) -> unreify @_ @x Refl . fromColumns)
197+ (\SSpec {nullity} -> case nullity of
199+ NotNull -> \(Reify (DB a)) -> Reify (DB (unsafeUnnullify a)))
205+ ((_, r) :| (map (first ((tag ==.) . litExpr)) -> cases')) ->
209+type GGInsert :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Type -> Type
210+type family GGInsert algebra rep a where
211+ GGInsert 'K.Product rep a = GConstruct TUnreify (Eval (rep (Reify Insert))) a
212+ GGInsert 'K.Sum rep a =
213+ -- Ideally this would be as follows, but GHC complains about "illegal polymorphic type"
214+ -- (forall r. GConstructADT TUnreify (Eval (rep (Reify Insert))) r r) -> a
215+ GConstructADT TUnreify (Eval (rep (Reify Insert))) a a -> a
218+gginsert :: forall algebra rep a. GGConstructable algebra rep
219+ => (Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))) (Col Insert) -> a)
220+ -> GGInsert algebra rep a
221+gginsert gfromColumns = case algebraSing @algebra of
223+ gtabulate @TUnreify @(Eval (rep (Reify Insert))) @a $
227+ @(TTable (Reify Insert))
230+ @(Col (Reify Insert))
231+ @(Eval (rep (Reify Insert)))
232+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
234+ gcindex @TUnreify @(Eval (rep (Reify Insert))) @a f $
235+ fmap (gfromColumns . hunreify) $
237+ @(TTable (Reify Insert))
240+ @(Col (Reify Insert))
241+ @(Eval (rep (Reify Insert)))
242+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
243+ (\SSpec {necessity, info} -> Reify $ case necessity of
244+ SRequired -> RequiredInsert (snull info)
245+ SOptional -> OptionalInsert (Just (snull info)))
246+ (\SSpec {nullity} -> case nullity of
249+ Reify (RequiredInsert a) -> Reify (RequiredInsert (nullify a))
250+ Reify (OptionalInsert a) -> Reify (OptionalInsert (nullify <$> a)))
251+ (HType . Reify . RequiredInsert . litExpr)
254+type GGName :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Type -> Type
255+type family GGName algebra rep a where
256+ GGName 'K.Product rep a = GConstruct TUnreify (Eval (rep (Reify Name))) a
257+ GGName 'K.Sum rep a = Name Tag -> GBuildADT TUnreify (Eval (rep (Reify Name))) a
260+ggname :: forall algebra rep a. GGConstructable algebra rep
261+ => (Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))) (Col Name) -> a)
262+ -> GGName algebra rep a
263+ggname gfromColumns = case algebraSing @algebra of
265+ gtabulate @TUnreify @(Eval (rep (Reify Name))) @a $
269+ @(TTable (Reify Name))
273+ @(Eval (rep (Reify Name)))
274+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
276+ gftabulate @TUnreify @(Eval (rep (Reify Name))) @a $
280+ @(TTable (Reify Name))
284+ @(Eval (rep (Reify Name)))
285+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
286+ (\_ _ (Reify (NameCol a)) -> Reify (NameCol a))
287+ (HType ((\(Name a) -> Reify (NameCol a)) tag))
290+type GGAggregate :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Type -> Type
291+type family GGAggregate algebra rep r where
292+ GGAggregate 'K.Product rep r =
293+ GConstruct TUnreify (Eval (rep (Reify Expr)))
294+ (GConstruct (Compose Aggregate TUnreify) (Eval (rep (Reify Expr))) r -> r)
295+ GGAggregate 'K.Sum rep r =
296+ GBuildADT TUnreify (Eval (rep (Reify Expr)))
297+ (GBuildADT (Compose Aggregate TUnreify) (Eval (rep (Reify Expr))) r -> r)
300+ggaggregate :: forall algebra rep a. GGConstructable algebra rep
301+ => (Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))) (Col Expr) -> a)
302+ -> (a -> Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))) (Col Expr))
303+ -> GGAggregate algebra rep (Aggregate a) -> a -> Aggregate a
304+ggaggregate gfromColumns gtoColumns agg es = case algebraSing @algebra of
310+ @(TTable (Reify Expr))
314+ @(Eval (rep (Reify Expr)))
315+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
318+ gtraverse @TUnreify @(Eval (rep (Reify Expr))) @_ @Aggregate (MaybeApply . Left)
323+ @(Eval (rep (Reify Expr)))
324+ @(GConstruct (Compose Aggregate TUnreify) (Eval (rep (Reify Expr))) (Aggregate a) -> Aggregate a)
328+ @(Compose Aggregate TUnreify)
329+ @(Eval (rep (Reify Expr)))
333+ @(TTable (Reify Expr))
337+ @(Eval (rep (Reify Expr)))
338+ (\(_ :: proxy x) -> unreify @_ @x Refl . fromColumns) $
348+ @(TTable (Reify Expr))
352+ @(Eval (rep (Reify Expr)))
353+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
354+ (\tag'' SSpec {nullity} (Reify (DB a)) ->
355+ Reify $ DB $ runTag nullity (tag ==. litExpr tag'') a)
356+ (HType (Reify (DB tag'))))
358+ gftraverse @TUnreify @(Eval (rep (Reify Expr))) @_ @Aggregate (MaybeApply . Left)
363+ @(Eval (rep (Reify Expr)))
364+ @(GBuildADT (Compose Aggregate TUnreify) (Eval (rep (Reify Expr))) (Aggregate a) -> Aggregate a)
368+ @(Compose Aggregate TUnreify)
369+ @(Eval (rep (Reify Expr)))
371+ (HType (Reify (DB tag)), exprs) =
373+ @(TTable (Reify Expr))
377+ @(Eval (rep (Reify Expr)))
378+ (\(_ :: proxy x) -> unreify @_ @x Refl . fromColumns)
379+ (\SSpec {nullity} -> case nullity of
381+ NotNull -> \(Reify (DB a)) -> Reify (DB (unsafeUnnullify a))) $
386+type GGAggregate' :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Type -> Type
387+type family GGAggregate' algebra rep r where
388+ GGAggregate' 'K.Product rep r =
389+ GConstruct TUnreify (Eval (rep (Reify Expr)))
390+ (GConstruct TUnreify (Eval (rep (Reify Aggregate))) r -> r)
391+ GGAggregate' 'K.Sum rep r =
392+ GBuildADT TUnreify (Eval (rep (Reify Expr)))
393+ (GBuildADT TUnreify (Eval (rep (Reify Aggregate))) r -> r)
396+ggaggregate' :: forall algebra rep exprs agg. GGConstructable algebra rep
397+ => (Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))) (Col Aggregate) -> agg)
398+ -> (exprs -> Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))) (Col Expr))
399+ -> GGAggregate' algebra rep agg -> exprs -> agg
400+ggaggregate' gfromColumns gtoColumns agg es = case algebraSing @algebra of
405+ @(TTable (Reify Aggregate))
408+ @(Col (Reify Aggregate))
409+ @(Eval (rep (Reify Aggregate)))
410+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
415+ @(Eval (rep (Reify Expr)))
416+ @(GConstruct TUnreify (Eval (rep (Reify Aggregate))) agg -> agg)
421+ @(Eval (rep (Reify Aggregate)))
425+ @(TTable (Reify Expr))
429+ @(Eval (rep (Reify Expr)))
430+ (\(_ :: proxy x) -> unreify @_ @x Refl . fromColumns) $
437+ @(TTable (Reify Aggregate))
440+ @(Col (Reify Aggregate))
441+ @(Eval (rep (Reify Aggregate)))
442+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
443+ (\tag' SSpec {nullity} (Reify (Aggregation a)) ->
444+ Reify $ Aggregation $ runTag nullity (tag ==. litExpr tag') <$> a)
445+ (HType (Reify (Aggregation (groupByExpr tag))))
450+ @(Eval (rep (Reify Expr)))
451+ @(GBuildADT TUnreify (Eval (rep (Reify Aggregate))) agg -> agg)
456+ @(Eval (rep (Reify Aggregate)))
458+ (HType (Reify (DB tag)), exprs) =
460+ @(TTable (Reify Expr))
464+ @(Eval (rep (Reify Expr)))
465+ (\(_ :: proxy x) -> unreify @_ @x Refl . fromColumns)
466+ (\SSpec {nullity} -> case nullity of
468+ NotNull -> \(Reify (DB a)) -> Reify (DB (unsafeUnnullify a))) $
473+liftF½ :: MaybeApply Aggregate a -> Aggregate a
474+liftF½ (MaybeApply ma) = case ma of
476+ Right a -> a <$ groupByExpr (litExpr False)
479+liftF1½ :: Apply v => (a -> b -> c) -> v a -> MaybeApply v b -> v c
480+liftF1½ f a (MaybeApply eb) = case eb of
481+ Left b -> liftF2 f a b
482+ Right b -> fmap (`f` b) a