@@ -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 #-}
15+
16+module Rel8.Generic.Construction
17+ ( GGConstructable
18+ , GGConstruct, ggconstruct
19+ , GGDeconstruct, ggdeconstruct
20+ , GGInsert, gginsert
21+ , GGName, ggname
22+ , GGAggregate, ggaggregate
23+ , GGAggregate', ggaggregate'
24+ )
25+where
26+
27+-- base
28+import Data.Bifunctor ( first )
29+import Data.Kind ( Constraint, Type )
30+import Data.List.NonEmpty ( NonEmpty( (:|) ) )
31+import Data.Type.Equality ( (:~:)( Refl ) )
32+import Prelude
33+
34+-- rel8
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
43+ ( GConstructableADT
44+ , GBuildADT, gbuildADT, gunbuildADT
45+ , GConstructADT, gconstructADT, gdeconstructADT
46+ , CorepConstructors, GConstructors, gcindex, gctabulate
47+ , CorepFields, gfindex, gftabulate, gftraverse
48+ )
49+import Rel8.Generic.Construction.Record
50+ ( GConstructable, GConstruct, gconstruct, gdeconstruct
51+ , Corep, gindex, gtabulate, gtraverse
52+ )
53+import Rel8.Generic.Table ( GGColumns )
54+import Rel8.Kind.Algebra
55+ ( SAlgebra( SProduct, SSum )
56+ , KnownAlgebra, algebraSing
57+ )
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 )
70+import Rel8.Table
71+ ( TTable, TColumns, TUnreify
72+ , Table, fromColumns, toColumns, reify, unreify
73+ )
74+import Rel8.Table.Bool ( case_ )
75+import Rel8.Type.Tag ( Tag )
76+
77+-- semigroupoids
78+import Data.Functor.Apply ( Apply, MaybeApply( MaybeApply ), liftF2 )
79+
80+
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
90+ )
91+
92+
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)))
105+ )
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)))
119+ )
120+
121+
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
129+
130+
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
135+ SProduct ->
136+ gtabulate @TUnreify @(Eval (rep (Reify Expr))) @a $
137+ gfromColumns .
138+ hunreify .
139+ gconstruct
140+ @(TTable (Reify Expr))
141+ @TColumns
142+ @TUnreify
143+ @(Col (Reify Expr))
144+ @(Eval (rep (Reify Expr)))
145+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
146+ SSum -> \f ->
147+ gcindex @TUnreify @(Eval (rep (Reify Expr))) @a f $
148+ fmap (gfromColumns . hunreify) $
149+ gconstructADT
150+ @(TTable (Reify Expr))
151+ @TColumns
152+ @TUnreify
153+ @(Col (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
158+ Null -> id
159+ NotNull -> \(Reify (DB a)) -> Reify (DB (nullify a)))
160+ (HType . Reify . DB . litExpr)
161+
162+
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)
169+
170+
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 .
177+ gdeconstruct
178+ @(TTable (Reify Expr))
179+ @TColumns
180+ @TUnreify
181+ @(Col (Reify Expr))
182+ @(Eval (rep (Reify Expr)))
183+ (\(_ :: proxy x) -> unreify @_ @x Refl . fromColumns) .
184+ hreify .
185+ gtoColumns
186+ SSum ->
187+ gctabulate @TUnreify @(Eval (rep (Reify Expr))) @r @(a -> r) $ \constructors as ->
188+ let
189+ (HType (Reify (DB tag)), cases) =
190+ gdeconstructADT
191+ @(TTable (Reify Expr))
192+ @TColumns
193+ @TUnreify
194+ @(Col (Reify Expr))
195+ @(Eval (rep (Reify Expr)))
196+ (\(_ :: proxy x) -> unreify @_ @x Refl . fromColumns)
197+ (\SSpec {nullity} -> case nullity of
198+ Null -> id
199+ NotNull -> \(Reify (DB a)) -> Reify (DB (unsafeUnnullify a)))
200+ constructors $
201+ hreify $
202+ gtoColumns as
203+ in
204+ case cases of
205+ ((_, r) :| (map (first ((tag ==.) . litExpr)) -> cases')) ->
206+ case_ cases' r
207+
208+
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
216+
217+
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
222+ SProduct ->
223+ gtabulate @TUnreify @(Eval (rep (Reify Insert))) @a $
224+ gfromColumns .
225+ hunreify .
226+ gconstruct
227+ @(TTable (Reify Insert))
228+ @TColumns
229+ @TUnreify
230+ @(Col (Reify Insert))
231+ @(Eval (rep (Reify Insert)))
232+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
233+ SSum -> \f ->
234+ gcindex @TUnreify @(Eval (rep (Reify Insert))) @a f $
235+ fmap (gfromColumns . hunreify) $
236+ gconstructADT
237+ @(TTable (Reify Insert))
238+ @TColumns
239+ @TUnreify
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
247+ Null -> id
248+ NotNull -> \case
249+ Reify (RequiredInsert a) -> Reify (RequiredInsert (nullify a))
250+ Reify (OptionalInsert a) -> Reify (OptionalInsert (nullify <$> a)))
251+ (HType . Reify . RequiredInsert . litExpr)
252+
253+
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
258+
259+
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
264+ SProduct ->
265+ gtabulate @TUnreify @(Eval (rep (Reify Name))) @a $
266+ gfromColumns .
267+ hunreify .
268+ gconstruct
269+ @(TTable (Reify Name))
270+ @TColumns
271+ @TUnreify
272+ @(Col (Reify Name))
273+ @(Eval (rep (Reify Name)))
274+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
275+ SSum -> \tag ->
276+ gftabulate @TUnreify @(Eval (rep (Reify Name))) @a $
277+ gfromColumns .
278+ hunreify .
279+ gbuildADT
280+ @(TTable (Reify Name))
281+ @TColumns
282+ @TUnreify
283+ @(Col (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))
288+
289+
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)
298+
299+
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
305+ SProduct -> f $
306+ fmap
307+ ( gfromColumns
308+ . hunreify
309+ . gconstruct
310+ @(TTable (Reify Expr))
311+ @TColumns
312+ @TUnreify
313+ @(Col (Reify Expr))
314+ @(Eval (rep (Reify Expr)))
315+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
316+ ) .
317+ liftF½ .
318+ gtraverse @TUnreify @(Eval (rep (Reify Expr))) @_ @Aggregate (MaybeApply . Left)
319+ where
320+ f =
321+ gindex
322+ @TUnreify
323+ @(Eval (rep (Reify Expr)))
324+ @(GConstruct (Compose Aggregate TUnreify) (Eval (rep (Reify Expr))) (Aggregate a) -> Aggregate a)
325+ agg
326+ exprs .
327+ gtabulate
328+ @(Compose Aggregate TUnreify)
329+ @(Eval (rep (Reify Expr)))
330+ @(Aggregate a)
331+ exprs =
332+ gdeconstruct
333+ @(TTable (Reify Expr))
334+ @TColumns
335+ @TUnreify
336+ @(Col (Reify Expr))
337+ @(Eval (rep (Reify Expr)))
338+ (\(_ :: proxy x) -> unreify @_ @x Refl . fromColumns) $
339+ hreify $
340+ gtoColumns es
341+
342+ SSum -> f exprs $
343+ liftF1½
344+ (\tag' ->
345+ gfromColumns .
346+ hunreify .
347+ gbuildADT
348+ @(TTable (Reify Expr))
349+ @TColumns
350+ @TUnreify
351+ @(Col (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'))))
357+ (groupByExpr tag) .
358+ gftraverse @TUnreify @(Eval (rep (Reify Expr))) @_ @Aggregate (MaybeApply . Left)
359+ where
360+ f e =
361+ gfindex
362+ @TUnreify
363+ @(Eval (rep (Reify Expr)))
364+ @(GBuildADT (Compose Aggregate TUnreify) (Eval (rep (Reify Expr))) (Aggregate a) -> Aggregate a)
365+ agg
366+ e .
367+ gftabulate
368+ @(Compose Aggregate TUnreify)
369+ @(Eval (rep (Reify Expr)))
370+ @(Aggregate a)
371+ (HType (Reify (DB tag)), exprs) =
372+ gunbuildADT
373+ @(TTable (Reify Expr))
374+ @TColumns
375+ @TUnreify
376+ @(Col (Reify Expr))
377+ @(Eval (rep (Reify Expr)))
378+ (\(_ :: proxy x) -> unreify @_ @x Refl . fromColumns)
379+ (\SSpec {nullity} -> case nullity of
380+ Null -> id
381+ NotNull -> \(Reify (DB a)) -> Reify (DB (unsafeUnnullify a))) $
382+ hreify $
383+ gtoColumns es
384+
385+
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)
394+
395+
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
401+ SProduct -> f $
402+ gfromColumns .
403+ hunreify .
404+ gconstruct
405+ @(TTable (Reify Aggregate))
406+ @TColumns
407+ @TUnreify
408+ @(Col (Reify Aggregate))
409+ @(Eval (rep (Reify Aggregate)))
410+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
411+ where
412+ f =
413+ gindex
414+ @TUnreify
415+ @(Eval (rep (Reify Expr)))
416+ @(GConstruct TUnreify (Eval (rep (Reify Aggregate))) agg -> agg)
417+ agg
418+ exprs .
419+ gtabulate
420+ @TUnreify
421+ @(Eval (rep (Reify Aggregate)))
422+ @agg
423+ exprs =
424+ gdeconstruct
425+ @(TTable (Reify Expr))
426+ @TColumns
427+ @TUnreify
428+ @(Col (Reify Expr))
429+ @(Eval (rep (Reify Expr)))
430+ (\(_ :: proxy x) -> unreify @_ @x Refl . fromColumns) $
431+ hreify $
432+ gtoColumns es
433+ SSum -> f exprs $
434+ gfromColumns .
435+ hunreify .
436+ gbuildADT
437+ @(TTable (Reify Aggregate))
438+ @TColumns
439+ @TUnreify
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))))
446+ where
447+ f e =
448+ gfindex
449+ @TUnreify
450+ @(Eval (rep (Reify Expr)))
451+ @(GBuildADT TUnreify (Eval (rep (Reify Aggregate))) agg -> agg)
452+ agg
453+ e .
454+ gftabulate
455+ @TUnreify
456+ @(Eval (rep (Reify Aggregate)))
457+ @agg
458+ (HType (Reify (DB tag)), exprs) =
459+ gunbuildADT
460+ @(TTable (Reify Expr))
461+ @TColumns
462+ @TUnreify
463+ @(Col (Reify Expr))
464+ @(Eval (rep (Reify Expr)))
465+ (\(_ :: proxy x) -> unreify @_ @x Refl . fromColumns)
466+ (\SSpec {nullity} -> case nullity of
467+ Null -> id
468+ NotNull -> \(Reify (DB a)) -> Reify (DB (unsafeUnnullify a))) $
469+ hreify $
470+ gtoColumns es
471+
472+
473+liftF½ :: MaybeApply Aggregate a -> Aggregate a
474+liftF½ (MaybeApply ma) = case ma of
475+ Left a -> a
476+ Right a -> a <$ groupByExpr (litExpr False)
477+
478+
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