@@ -14,6 +14,7 @@
15module Rel8.Table.Serialize
15module Rel8.Table.Serialize
16 ( Serializable, lit, parse
16 ( Serializable, lit, parse
@@ -28,9 +29,8 @@ import Prelude
28import qualified Hasql.Decoders as Hasql
29import qualified Hasql.Decoders as Hasql
31−import Rel8.Expr ( Expr, Encodes, Col(..) )
32+import Rel8.Expr ( Expr, Col(..) )
32import Rel8.Expr.Serialize ( slitExpr, sparseValue )
33import Rel8.Expr.Serialize ( slitExpr, sparseValue )
33−import Rel8.Opaque ( Opaque )
34import Rel8.Schema.Context ( Col(..) )
34import Rel8.Schema.Context ( Col(..) )
35import Rel8.Schema.Context.Identity
35import Rel8.Schema.Context.Identity
36 ( fromHEitherTable, toHEitherTable
36 ( fromHEitherTable, toHEitherTable
@@ -54,6 +54,7 @@ import Rel8.Table.Either ( EitherTable )
54import Rel8.Table.List ( ListTable )
54import Rel8.Table.List ( ListTable )
55import Rel8.Table.Maybe ( MaybeTable )
55import Rel8.Table.Maybe ( MaybeTable )
56import Rel8.Table.NonEmpty ( NonEmptyTable )
56import Rel8.Table.NonEmpty ( NonEmptyTable )
57+import Rel8.Table.Recontextualize ( Recontextualize )
57import Rel8.Table.These ( TheseTable )
58import Rel8.Table.These ( TheseTable )
58import Rel8.Type ( DBType )
59import Rel8.Type ( DBType )
@@ -64,160 +65,44 @@ import Data.Functor.Apply ( WrappedApplicative(..) )
64import Data.These ( These )
65import Data.These ( These )
67−type IsPlainColumn :: Type -> Bool
68−type family IsPlainColumn a where
69− IsPlainColumn (Either _ _) = 'False
70− IsPlainColumn [_] = 'False
71− IsPlainColumn (Maybe _) = 'False
72− IsPlainColumn (NonEmpty _) = 'False
73− IsPlainColumn (These _ _) = 'False
74− IsPlainColumn (_, _) = 'False
75− IsPlainColumn (_, _, _) = 'False
76− IsPlainColumn (_, _, _, _) = 'False
77− IsPlainColumn (_, _, _, _, _) = 'False
78− IsPlainColumn (_ Identity) = 'False
79− IsPlainColumn (_ (Col Identity _)) = 'False
80− IsPlainColumn (Identity _) = 'False
81− IsPlainColumn _ = 'True
84−type IsTabular :: Type -> Bool
85−type family IsTabular a where
86− IsTabular (Either _ _) = 'True
87− IsTabular [a] = IsListTabular a
88− IsTabular (Maybe a) = IsMaybeTabular a
89− IsTabular (NonEmpty a) = IsListTabular a
90− IsTabular (These _ _) = 'True
91− IsTabular (_, _) = 'True
92− IsTabular (_, _, _) = 'True
93− IsTabular (_, _, _, _) = 'True
94− IsTabular (_, _, _, _, _) = 'True
95− IsTabular (_ Identity) = 'True
96− IsTabular (_ (Col Identity)) = 'True
97− IsTabular (Col Identity _) = 'True
101−type IsTabular' :: Bool -> Type -> Bool
102−type family IsTabular' isTabular exprs where
103− IsTabular' _ (Expr _) = 'False
104− IsTabular' 'False _ = 'False
105− IsTabular' _ _ = 'True
108−type IsMaybeTabular :: Type -> Bool
109−type family IsMaybeTabular a where
110− IsMaybeTabular (Maybe _) = 'True
111− IsMaybeTabular a = IsTabular a
114−type IsListTabular :: Type -> Bool
115−type family IsListTabular a where
116− IsListTabular (Maybe a) = IsMaybeTabular a
117− IsListTabular a = IsTabular a
120−type ToExprs :: Type -> Type -> Constraint
121−class ExprsFor (IsPlainColumn a) (IsTabular' (IsTabular a) exprs) a exprs => ToExprs a exprs
122−instance ExprsFor (IsPlainColumn a) (IsTabular' (IsTabular a) exprs) a exprs => ToExprs a exprs
125fromIdentity' :: forall exprs a. ToExprs a exprs => Columns exprs (Col Identity) -> a
68fromIdentity' :: forall exprs a. ToExprs a exprs => Columns exprs (Col Identity) -> a
126−fromIdentity' = fromIdentity @(IsPlainColumn a) @_ @_ @exprs
69+fromIdentity' = fromIdentity @_ @exprs
129toIdentity' :: forall exprs a. ToExprs a exprs => a -> Columns exprs (Col Identity)
72toIdentity' :: forall exprs a. ToExprs a exprs => a -> Columns exprs (Col Identity)
130−toIdentity' = toIdentity @(IsPlainColumn a) @_ @_ @exprs
73+toIdentity' = toIdentity @_ @exprs
133−type ExprsFor :: Bool -> Bool -> Type -> Type -> Constraint
76+type ToExprs :: Type -> Type -> Constraint
134−class (Table Expr exprs, isTabular ~ IsTabular a) =>
77+class Table Expr exprs => ToExprs a exprs where
135− ExprsFor isPlainColumn isTabular a exprs where
136 fromIdentity :: Columns exprs (Col Identity) -> a
78 fromIdentity :: Columns exprs (Col Identity) -> a
137 toIdentity :: a -> Columns exprs (Col Identity)
79 toIdentity :: a -> Columns exprs (Col Identity)
140−instance
82+instance {-# OVERLAPPABLE #-} (Sql DBType a, x ~ Expr a) => ToExprs a x where
143− , IsTabular a ~ 'False
146− => ExprsFor 'True 'False a x
148 fromIdentity (HType (Result a)) = a
83 fromIdentity (HType (Result a)) = a
149 toIdentity = HType . Result
84 toIdentity = HType . Result
153− ( Sql DBType a
87+instance (Sql DBType a, x ~ [a]) => ToExprs [a] (Expr x) where
154− , IsListTabular a ~ 'False
156− ) => ExprsFor 'False 'False [a] (Expr x)
158 fromIdentity (HType (Result a)) = a
88 fromIdentity (HType (Result a)) = a
159 toIdentity = HType . Result
89 toIdentity = HType . Result
162−instance
92+instance (Sql DBType a, NotNull a, x ~ Maybe a) => ToExprs (Maybe a) (Expr x)
164− , isTabular ~ 'False
166− , IsMaybeTabular a ~ 'False
168− ) => ExprsFor 'False 'False (Maybe a) (Expr x)
170 fromIdentity (HType (Result a)) = a
94 fromIdentity (HType (Result a)) = a
171 toIdentity = HType . Result
95 toIdentity = HType . Result
174−instance
98+instance (Sql DBType a, NotNull a, x ~ NonEmpty a) => ToExprs (NonEmpty a) (Expr x)
176− , IsListTabular a ~ 'False
178− ) => ExprsFor 'False 'False (NonEmpty a) (Expr x)
180 fromIdentity (HType (Result a)) = a
100 fromIdentity (HType (Result a)) = a
181 toIdentity = HType . Result
101 toIdentity = HType . Result
186− , IsListTabular a ~ 'False
187− ) => ExprsFor 'False 'False [a] (ListTable exprs)
189− fromIdentity = fmap (fromIdentity' @exprs) . fromHListTable
190− toIdentity = toHListTable . fmap (toIdentity' @exprs)
195− , IsMaybeTabular a ~ 'False
196− ) => ExprsFor 'False 'False (Maybe a) (MaybeTable exprs)
199− fmap (fromIdentity' @exprs . hunlabel unlabeler) .
203− fmap (hlabel labeler . toIdentity' @exprs)
208− , IsListTabular a ~ 'False
209− ) => ExprsFor 'False 'False (NonEmpty a) (NonEmptyTable exprs)
211− fromIdentity = fmap (fromIdentity' @exprs) . fromHNonEmptyTable
212− toIdentity = toHNonEmptyTable . fmap (toIdentity' @exprs)
219− , x ~ EitherTable exprs1 exprs2
104+instance (ToExprs a exprs1, ToExprs b exprs2, x ~ EitherTable exprs1 exprs2) =>
220− ) => ExprsFor 'False isTabular (Either a b) x
105+ ToExprs (Either a b) x
222 fromIdentity =
107 fromIdentity =
@@ -231,22 +116,12 @@ instance
231 (hlabel labeler . toIdentity' @exprs2)
116 (hlabel labeler . toIdentity' @exprs2)
236− , IsListTabular a ~ 'True
237− , x ~ ListTable exprs
119+instance ToExprs a exprs => ToExprs [a] (ListTable exprs) where
238− ) => ExprsFor 'False 'True [a] x
240 fromIdentity = fmap (fromIdentity' @exprs) . fromHListTable
120 fromIdentity = fmap (fromIdentity' @exprs) . fromHListTable
241 toIdentity = toHListTable . fmap (toIdentity' @exprs)
121 toIdentity = toHListTable . fmap (toIdentity' @exprs)
244−instance
124+instance ToExprs a exprs => ToExprs (Maybe a) (MaybeTable exprs) where
246− , IsMaybeTabular a ~ 'True
247− , x ~ MaybeTable exprs
248− ) => ExprsFor 'False 'True (Maybe a) x
250 fromIdentity =
125 fromIdentity =
251 fmap (fromIdentity' @exprs . hunlabel unlabeler) .
126 fmap (fromIdentity' @exprs . hunlabel unlabeler) .
252 fromHMaybeTable
127 fromHMaybeTable
@@ -255,22 +130,14 @@ instance
255 fmap (hlabel labeler . toIdentity' @exprs)
130 fmap (hlabel labeler . toIdentity' @exprs)
258−instance
133+instance ToExprs a exprs => ToExprs (NonEmpty a) (NonEmptyTable exprs)
260− , IsListTabular a ~ 'True
261− , x ~ NonEmptyTable exprs
262− ) => ExprsFor 'False 'True (NonEmpty a) x
264 fromIdentity = fmap (fromIdentity' @exprs) . fromHNonEmptyTable
135 fromIdentity = fmap (fromIdentity' @exprs) . fromHNonEmptyTable
265 toIdentity = toHNonEmptyTable . fmap (toIdentity' @exprs)
136 toIdentity = toHNonEmptyTable . fmap (toIdentity' @exprs)
272− , x ~ TheseTable exprs1 exprs2
139+instance (ToExprs a exprs1, ToExprs b exprs2, x ~ TheseTable exprs1 exprs2) =>
273− ) => ExprsFor 'False isTabular (These a b) x
140+ ToExprs (These a b) x
275 fromIdentity =
142 fromIdentity =
@@ -284,12 +151,8 @@ instance
284 (hlabel labeler . toIdentity' @exprs2)
151 (hlabel labeler . toIdentity' @exprs2)
288− ( ToExprs a exprs1
154+instance (ToExprs a exprs1, ToExprs b exprs2, x ~ (exprs1, exprs2)) =>
289− , ToExprs b exprs2
155+ ToExprs (a, b) x
291− , x ~ (exprs1, exprs2)
292− ) => ExprsFor 'False isTabular (a, b) x
294 fromIdentity (HPair a b) =
157 fromIdentity (HPair a b) =
295 ( fromIdentity' @exprs1 $ hunlabel unlabeler a
158 ( fromIdentity' @exprs1 $ hunlabel unlabeler a
@@ -305,9 +168,8 @@ instance
305 ( ToExprs a exprs1
168 ( ToExprs a exprs1
306 , ToExprs b exprs2
169 , ToExprs b exprs2
307 , ToExprs c exprs3
170 , ToExprs c exprs3
309 , x ~ (exprs1, exprs2, exprs3)
171 , x ~ (exprs1, exprs2, exprs3)
310− ) => ExprsFor 'False isTabular (a, b, c) x
172+ ) => ToExprs (a, b, c) x
312 fromIdentity (HTrio a b c) =
174 fromIdentity (HTrio a b c) =
313 ( fromIdentity' @exprs1 $ hunlabel unlabeler a
175 ( fromIdentity' @exprs1 $ hunlabel unlabeler a
@@ -326,9 +188,8 @@ instance
326 , ToExprs b exprs2
188 , ToExprs b exprs2
327 , ToExprs c exprs3
189 , ToExprs c exprs3
328 , ToExprs d exprs4
190 , ToExprs d exprs4
330 , x ~ (exprs1, exprs2, exprs3, exprs4)
191 , x ~ (exprs1, exprs2, exprs3, exprs4)
331− ) => ExprsFor 'False isTabular (a, b, c, d) x
192+ ) => ToExprs (a, b, c, d) x
333 fromIdentity (HQuartet a b c d) =
194 fromIdentity (HQuartet a b c d) =
334 ( fromIdentity' @exprs1 $ hunlabel unlabeler a
195 ( fromIdentity' @exprs1 $ hunlabel unlabeler a
@@ -350,9 +211,8 @@ instance
350 , ToExprs c exprs3
211 , ToExprs c exprs3
351 , ToExprs d exprs4
212 , ToExprs d exprs4
352 , ToExprs e exprs5
213 , ToExprs e exprs5
354 , x ~ (exprs1, exprs2, exprs3, exprs4, exprs5)
214 , x ~ (exprs1, exprs2, exprs3, exprs4, exprs5)
355− ) => ExprsFor 'False isTabular (a, b, c, d, e) x
215+ ) => ToExprs (a, b, c, d, e) x
357 fromIdentity (HQuintet a b c d e) =
217 fromIdentity (HQuintet a b c d e) =
358 ( fromIdentity' @exprs1 $ hunlabel unlabeler a
218 ( fromIdentity' @exprs1 $ hunlabel unlabeler a
@@ -370,34 +230,22 @@ instance
376− , result ~ Col Identity
233+instance (HTable t, result ~ Col Identity, x ~ t (Col Expr)) =>
377− , x ~ t (Col Expr)
234+ ToExprs (t result) x
378− ) => ExprsFor 'False isTabular (t result) x
380 fromIdentity = id
236 fromIdentity = id
381 toIdentity = id
237 toIdentity = id
384−instance
240+instance (Recontextualize Identity Expr (t Identity) (t Expr), result ~ Identity, x ~ t Expr) =>
385− ( Encodes (t Identity) (t Expr)
241+ ToExprs (t result) x
389− ) => ExprsFor 'False isTabular (t result) x
391 fromIdentity = fromColumns
243 fromIdentity = fromColumns
392 toIdentity = toColumns
244 toIdentity = toColumns
396− ( KnownSpec spec
247+instance (KnownSpec spec, x ~ Col Expr spec) =>
400− ) => ExprsFor 'False isTabular (Col Identity spec) x
248+ ToExprs (Col Identity spec) x
402 fromIdentity = fromColumns
250 fromIdentity = fromColumns
403 toIdentity = toColumns
251 toIdentity = toColumns
@@ -425,27 +273,75 @@ type family FromExprs a where
425-- | @Serializable@ witnesses the one-to-one correspondence between the type
273-- | @Serializable@ witnesses the one-to-one correspondence between the type
426-- @sql@, which contains SQL expressions, and the type @haskell@, which
274-- @sql@, which contains SQL expressions, and the type @haskell@, which
427-- contains the Haskell decoding of rows containing @sql@ SQL expressions.
275-- contains the Haskell decoding of rows containing @sql@ SQL expressions.
276+type Serializable :: Type -> Type -> Constraint
428class (ToExprs a exprs, a ~ FromExprs exprs) => Serializable exprs a | exprs -> a
277class (ToExprs a exprs, a ~ FromExprs exprs) => Serializable exprs a | exprs -> a
429instance (ToExprs a exprs, a ~ FromExprs exprs) => Serializable exprs a
278instance (ToExprs a exprs, a ~ FromExprs exprs) => Serializable exprs a
430−instance Serializable (Expr Opaque) Opaque
279+instance {-# OVERLAPPING #-} Sql DBType a => Serializable (Expr a) a
433lit :: forall exprs a. Serializable exprs a => a -> exprs
282lit :: forall exprs a. Serializable exprs a => a -> exprs
434−lit = fromColumns . litTable . toIdentity' @exprs
283+lit = fromColumns . litHTable . toIdentity' @exprs
437parse :: forall exprs a. Serializable exprs a => Hasql.Row a
286parse :: forall exprs a. Serializable exprs a => Hasql.Row a
438−parse = fromIdentity' @exprs <$> parseTable
287+parse = fromIdentity' @exprs <$> parseHTable
290+type Encodes :: Type -> Type -> Constraint
291+class Serializable exprs a => Encodes a exprs | a -> exprs, exprs -> a
294+instance KnownSpec spec => Encodes (Col Identity spec) (Col Expr spec)
297+instance Serializable (t Identity) (t Expr) => Encodes (t Expr) (t Identity)
300+instance HTable t => Encodes (t (Col Identity)) (t (Col Expr))
303+instance (Encodes a x, Encodes b y) => Encodes (Either a b) (EitherTable x y)
306+instance Encodes a x => Encodes [a] (ListTable x)
309+instance Encodes a x => Encodes (Maybe a) (MaybeTable x)
312+instance Encodes a x => Encodes (NonEmpty a) (NonEmptyTable x)
315+instance (Encodes a x, Encodes b y) => Encodes (These a b) (TheseTable x y)
318+instance (Encodes a x, Encodes b y) => Encodes (a, b) (x, y)
321+instance (Encodes a x, Encodes b y, Encodes c z) =>
322+ Encodes (a, b, c) (x, y, z)
325+instance (Encodes a w, Encodes b x, Encodes c y, Encodes d z) =>
326+ Encodes (a, b, c, d) (w, x, y, z)
329+instance (Encodes a v, Encodes b w, Encodes c x, Encodes d y, Encodes e z) =>
330+ Encodes (a, b, c, d, e) (v, w, x, y, z)
333+litTable :: Encodes a exprs => a -> exprs
441−litTable :: Encodes a b => a -> b
337+litHTable :: HTable t => t (Col Identity) -> t (Col Expr)
442−litTable (toColumns -> as) = fromColumns $ htabulate $ \field ->
338+litHTable as = htabulate $ \field ->
443 case hfield hspecs field of
339 case hfield hspecs field of
444 SSpec {nullability, info} -> case hfield as field of
340 SSpec {nullability, info} -> case hfield as field of
445 Result value -> DB (slitExpr nullability info value)
341 Result value -> DB (slitExpr nullability info value)
448−parseTable :: Table Identity a => Hasql.Row a
344+parseHTable :: HTable t => Hasql.Row (t (Col Identity))
449−parseTable = fmap fromColumns $ unwrapApplicative $ htabulateA $ \field ->
345+parseHTable = unwrapApplicative $ htabulateA $ \field ->
450 WrapApplicative $ case hfield hspecs field of
346 WrapApplicative $ case hfield hspecs field of
451 SSpec {nullability, info} -> Result <$> sparseValue nullability info
347 SSpec {nullability, info} -> Result <$> sparseValue nullability info