1{-# language DataKinds #-}
2{-# language DefaultSignatures #-}
3{-# language DisambiguateRecordFields #-}
4{-# language FlexibleContexts #-}
5{-# language FlexibleInstances #-}
6{-# language FunctionalDependencies #-}
7{-# language LambdaCase #-}
8{-# language NamedFieldPuns #-}
9{-# language ScopedTypeVariables #-}
10{-# language StandaloneKindSignatures #-}
11{-# language TypeApplications #-}
12{-# language TypeFamilies #-}
13{-# language TypeOperators #-}
14{-# language UndecidableInstances #-}
17 ( Table (Columns, Context)
18 , toColumns, fromColumns
20 , GTable, GColumns, GContext, fromGColumns, toGColumns
25import Data.Functor ( ($>) )
26import Data.Functor.Identity ( Identity( Identity ) )
27import Data.Kind ( Constraint, Type )
28import Data.List.NonEmpty ( NonEmpty )
30 ( Generic, Rep, from, to
31 , (:*:)( (:*:) ), K1( K1 ), M1( M1 )
35import GHC.TypeLits ( KnownSymbol )
36import Prelude hiding ( null )
39import Rel8.Generic.Record ( Record(..) )
40import Rel8.Schema.Context ( Col(..) )
41import Rel8.Schema.Context.Label ( Labelable, labeler, unlabeler )
42import Rel8.Schema.HTable ( HTable )
43import Rel8.Schema.HTable.Either ( HEitherTable(..) )
44import Rel8.Schema.HTable.Identity ( HIdentity(..) )
45import Rel8.Schema.HTable.Label ( HLabel, hlabel, hunlabel )
46import Rel8.Schema.HTable.List ( HListTable )
47import Rel8.Schema.HTable.Maybe ( HMaybeTable(..) )
48import Rel8.Schema.HTable.NonEmpty ( HNonEmptyTable )
49import Rel8.Schema.HTable.Nullify ( hnulls, hnullify, hunnullify )
50import Rel8.Schema.HTable.Pair ( HPair(..) )
51import Rel8.Schema.HTable.Quartet ( HQuartet(..) )
52import Rel8.Schema.HTable.Quintet ( HQuintet(..) )
53import Rel8.Schema.HTable.These ( HTheseTable(..) )
54import Rel8.Schema.HTable.Trio ( HTrio(..) )
55import Rel8.Schema.HTable.Type ( HType( HType ) )
56import Rel8.Schema.HTable.Vectorize ( hvectorize, hunvectorize )
57import qualified Rel8.Schema.Kind as K
58import Rel8.Schema.Null ( Nullify, Nullity( Null, NotNull ), Sql )
59import Rel8.Schema.Result ( Result )
60import Rel8.Schema.Spec ( Spec( Spec ), SSpec(..), KnownSpec )
61import Rel8.Type ( DBType )
62import Rel8.Type.Tag ( EitherTag( IsLeft, IsRight ), MaybeTag( IsJust ) )
65import Data.These ( These( This, That, These ) )
66import Data.These.Combinators ( justHere, justThere )
69-- | @Table@s are one of the foundational elements of Rel8, and describe data
70-- types that have a finite number of columns. Each of these columns contains
71-- data under a shared context, and contexts describe how to interpret the
72-- metadata about a column to a particular Haskell type. In Rel8, we have
73-- contexts for expressions (the 'Rel8.Expr' context), aggregations (the
74-- 'Rel8.Aggregate' context), insert values (the 'Rel8.Insert' contex), among
77-- In typical usage of Rel8 you don't need to derive instances of 'Table'
78-- yourself, as anything that's an instance of 'Rel8.Rel8able' is always a
80type Table :: K.Context -> Type -> Constraint
81class (HTable (Columns a), context ~ Context a) => Table context a | a -> context where
82 -- | The 'HTable' functor that describes the schema of this table.
83 type Columns a :: K.HTable
85 -- | The common context that all columns use as an interpretation.
86 type Context a :: K.Context
88 toColumns :: a -> Columns a (Col (Context a))
89 fromColumns :: Columns a (Col (Context a)) -> a
91 type Columns a = GColumns (Rep (Record a))
92 type Context a = GContext (Rep (Record a))
95 ( Generic (Record a), GTable (GContext (Rep (Record a))) (Rep (Record a))
96 , Columns a ~ GColumns (Rep (Record a))
97 , Context a ~ GContext (Rep (Record a))
99 => a -> Columns a (Col (Context a))
100 toColumns = toGColumns . from . Record
102 default fromColumns ::
103 ( Generic (Record a), GTable (GContext (Rep (Record a))) (Rep (Record a))
104 , Columns a ~ GColumns (Rep (Record a))
105 , Context a ~ GContext (Rep (Record a))
107 => Columns a (Col (Context a)) -> a
108 fromColumns = unrecord . to . fromGColumns
111type GColumns :: (Type -> Type) -> K.HTable
112type family GColumns rep where
113 GColumns (M1 D _ rep) = GColumns rep
114 GColumns (M1 C _ rep) = GColumns rep
115 GColumns (rep1 :*: rep2) = HPair (GColumns rep1) (GColumns rep2)
116 GColumns (M1 S ('MetaSel ('Just label) _ _ _) (K1 _ a)) =
117 HLabel label (Columns a)
120type GContext :: (Type -> Type) -> K.Context
121type family GContext rep where
122 GContext (M1 _ _ rep) = GContext rep
123 GContext (rep1 :*: _rep2) = GContext rep1
124 GContext (K1 _ a) = Context a
127type GTable :: K.Context -> (Type -> Type) -> Constraint
128class context ~ GContext rep => GTable context rep | rep -> context where
129 fromGColumns :: GColumns rep (Col context) -> rep x
130 toGColumns :: rep x -> GColumns rep (Col context)
133instance GTable context rep => GTable context (M1 D c rep) where
134 fromGColumns = M1 . fromGColumns @context @rep
135 toGColumns (M1 a) = toGColumns @context @rep a
138instance GTable context rep => GTable context (M1 C c rep) where
139 fromGColumns = M1 . fromGColumns @context @rep
140 toGColumns (M1 a) = toGColumns @context @rep a
143instance (GTable context rep1, GTable context rep2) =>
144 GTable context (rep1 :*: rep2)
146 fromGColumns (HPair a b) =
147 fromGColumns @context @rep1 a :*: fromGColumns @context @rep2 b
148 toGColumns (a :*: b) =
149 HPair (toGColumns @context @rep1 a) (toGColumns @context @rep2 b)
156 , GColumns (M1 S meta k1) ~ HLabel label (Columns a)
157 , meta ~ 'MetaSel ('Just label) _su _ss _ds
159 ) => GTable context (M1 S meta k1)
161 fromGColumns = M1 . K1 . fromColumns . hunlabel unlabeler
162 toGColumns (M1 (K1 a)) = hlabel labeler (toColumns a)
165-- | Any 'HTable' is also a 'Table'.
166instance HTable t => Table context (t (Col context)) where
167 type Columns (t (Col context)) = t
168 type Context (t (Col context)) = context
174-- | Any context is trivially a table.
175instance KnownSpec spec => Table context (Col context spec) where
176 type Columns (Col context spec) = HIdentity spec
177 type Context (Col context spec) = context
179 toColumns = HIdentity
180 fromColumns = unHIdentity
183instance Sql DBType a => Table Result (Identity a) where
184 type Columns (Identity a) = HType a
185 type Context (Identity a) = Result
187 toColumns (Identity a) = HType (Result a)
188 fromColumns (HType (Result a)) = Identity a
191instance (Table Result a, Table Result b) => Table Result (Either a b) where
192 type Columns (Either a b) = HEitherTable (Columns a) (Columns b)
193 type Context (Either a b) = Result
196 Left table -> HEitherTable
197 { htag = HIdentity (Result IsLeft)
198 , hleft = hlabel labeler (hnullify nullifier (toColumns table))
199 , hright = hlabel labeler (hnulls null)
201 Right table -> HEitherTable
202 { htag = HIdentity (Result IsRight)
203 , hleft = hlabel labeler (hnulls null)
204 , hright = hlabel labeler (hnullify nullifier (toColumns table))
207 fromColumns HEitherTable {htag, hleft, hright} = case htag of
208 HIdentity (Result tag) -> case tag of
209 IsLeft -> maybe err (Left . fromColumns) $ hunnullify unnullifier (hunlabel unlabeler hleft)
210 IsRight -> maybe err (Right . fromColumns) $ hunnullify unnullifier (hunlabel unlabeler hright)
212 err = error "Either.fromColumns: mismatch between tag and data"
215instance Table Result a => Table Result [a] where
216 type Columns [a] = HListTable (Columns a)
217 type Context [a] = Result
219 toColumns = hvectorize vectorizer . fmap toColumns
220 fromColumns = fmap fromColumns . hunvectorize unvectorizer
223instance Table Result a => Table Result (Maybe a) where
224 type Columns (Maybe a) = HMaybeTable (Columns a)
225 type Context (Maybe a) = Result
228 Nothing -> HMaybeTable
229 { htag = HIdentity (Result Nothing)
230 , hjust = hlabel labeler (hnulls null)
232 Just table -> HMaybeTable
233 { htag = HIdentity (Result (Just IsJust))
234 , hjust = hlabel labeler (hnullify nullifier (toColumns table))
237 fromColumns HMaybeTable {htag, hjust} = case htag of
238 HIdentity (Result tag) -> tag $>
239 case hunnullify unnullifier (hunlabel unlabeler hjust) of
240 Nothing -> error "Maybe.fromColumns: mismatch between tag and data"
241 Just just -> fromColumns just
244instance Table Result a => Table Result (NonEmpty a) where
245 type Columns (NonEmpty a) = HNonEmptyTable (Columns a)
246 type Context (NonEmpty a) = Result
248 toColumns = hvectorize vectorizer . fmap toColumns
249 fromColumns = fmap fromColumns . hunvectorize unvectorizer
252instance (Table Result a, Table Result b) => Table Result (These a b) where
253 type Columns (These a b) = HTheseTable (Columns a) (Columns b)
254 type Context (These a b) = Result
256 toColumns tables = HTheseTable
257 { hhereTag = relabel hhereTag
258 , hhere = hlabel labeler (hunlabel unlabeler (toColumns hhere))
259 , hthereTag = relabel hthereTag
260 , hthere = hlabel labeler (hunlabel unlabeler (toColumns hthere))
266 } = toColumns (justHere tables)
270 } = toColumns (justThere tables)
272 fromColumns HTheseTable {hhereTag, hhere, hthereTag, hthere} =
273 case (fromColumns mhere, fromColumns mthere) of
274 (Just a, Nothing) -> This (fromColumns a)
275 (Nothing, Just b) -> That (fromColumns b)
276 (Just a, Just b) -> These (fromColumns a) (fromColumns b)
277 _ -> error "These.fromColumns: mismatch between tags and data"
280 { htag = relabel hhereTag
281 , hjust = hlabel labeler (hunlabel unlabeler hhere)
284 { htag = relabel hthereTag
285 , hjust = hlabel labeler (hunlabel unlabeler hthere)
290 ( Table context a, Table context b
295 type Columns (a, b) =
297 (HLabel "fst" (Columns a))
298 (HLabel "snd" (Columns b))
299 type Context (a, b) = Context a
301 toColumns (a, b) = HPair
302 { hfst = hlabel labeler $ toColumns a
303 , hsnd = hlabel labeler $ toColumns b
305 fromColumns (HPair a b) =
306 ( fromColumns $ hunlabel unlabeler a
307 , fromColumns $ hunlabel unlabeler b
312 ( Table context a, Table context b, Table context c
314 ) => Table context (a, b, c)
316 type Columns (a, b, c) =
318 (HLabel "fst" (Columns a))
319 (HLabel "snd" (Columns b))
320 (HLabel "trd" (Columns c))
321 type Context (a, b, c) = Context a
323 toColumns (a, b, c) = HTrio
324 { hfst = hlabel labeler $ toColumns a
325 , hsnd = hlabel labeler $ toColumns b
326 , htrd = hlabel labeler $ toColumns c
328 fromColumns (HTrio a b c) =
329 ( fromColumns $ hunlabel unlabeler a
330 , fromColumns $ hunlabel unlabeler b
331 , fromColumns $ hunlabel unlabeler c
336 ( Table context a, Table context b, Table context c, Table context d
338 ) => Table context (a, b, c, d)
340 type Columns (a, b, c, d) =
342 (HLabel "fst" (Columns a))
343 (HLabel "snd" (Columns b))
344 (HLabel "trd" (Columns c))
345 (HLabel "frt" (Columns d))
346 type Context (a, b, c, d) = Context a
348 toColumns (a, b, c, d) = HQuartet
349 { hfst = hlabel labeler $ toColumns a
350 , hsnd = hlabel labeler $ toColumns b
351 , htrd = hlabel labeler $ toColumns c
352 , hfrt = hlabel labeler $ toColumns d
354 fromColumns (HQuartet a b c d) =
355 ( fromColumns $ hunlabel unlabeler a
356 , fromColumns $ hunlabel unlabeler b
357 , fromColumns $ hunlabel unlabeler c
358 , fromColumns $ hunlabel unlabeler d
363 ( Table context a, Table context b, Table context c, Table context d
366 ) => Table context (a, b, c, d, e)
368 type Columns (a, b, c, d, e) =
370 (HLabel "fst" (Columns a))
371 (HLabel "snd" (Columns b))
372 (HLabel "trd" (Columns c))
373 (HLabel "frt" (Columns d))
374 (HLabel "fft" (Columns e))
375 type Context (a, b, c, d, e) = Context a
377 toColumns (a, b, c, d, e) = HQuintet
378 { hfst = hlabel labeler $ toColumns a
379 , hsnd = hlabel labeler $ toColumns b
380 , htrd = hlabel labeler $ toColumns c
381 , hfrt = hlabel labeler $ toColumns d
382 , hfft = hlabel labeler $ toColumns e
384 fromColumns (HQuintet a b c d e) =
385 ( fromColumns $ hunlabel unlabeler a
386 , fromColumns $ hunlabel unlabeler b
387 , fromColumns $ hunlabel unlabeler c
388 , fromColumns $ hunlabel unlabeler d
389 , fromColumns $ hunlabel unlabeler e
393type Congruent :: Type -> Type -> Constraint
394class Columns a ~ Columns b => Congruent a b
395instance Columns a ~ Columns b => Congruent a b
398null :: Col Result ('Spec labels necessity (Maybe a))
403 => SSpec ('Spec labels necessity a)
404 -> Col Result ('Spec labels necessity a)
405 -> Col Result ('Spec labels necessity (Nullify a))
406nullifier SSpec {nullity} (Result a) = Result $ case nullity of
412 => SSpec ('Spec labels necessity a)
413 -> Col Result ('Spec labels necessity (Nullify a))
414 -> Maybe (Col Result ('Spec labels necessity a))
415unnullifier SSpec {nullity} (Result a) =
417 Null -> pure $ Result a
418 NotNull -> Result <$> a
421vectorizer :: Functor f
422 => SSpec ('Spec labels necessity a)
423 -> f (Col Result ('Spec labels necessity a))
424 -> Col Result ('Spec labels necessity (f a))
425vectorizer _ = Result . fmap (\(Result a) -> a)
428unvectorizer :: Functor f
429 => SSpec ('Spec labels necessity a)
430 -> Col Result ('Spec labels necessity (f a))
431 -> f (Col Result ('Spec labels necessity a))
432unvectorizer _ (Result results) = Result <$> results
436 => HIdentity ('Spec labels necessity a) (Col Result)
437 -> HIdentity ('Spec relabels necessity a) (Col Result)
438relabel (HIdentity (Result a)) = HIdentity (Result a)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list