@@ -38,7 +38,6 @@ module Rel8
40 , HNonEmpty
40 , HNonEmpty
@@ -47,18 +46,16 @@ module Rel8
47 , Transposes
46 , Transposes
48 , AltTable((<|>:))
47 , AltTable((<|>:))
49 , AlternativeTable( emptyTable )
48 , AlternativeTable( emptyTable )
50− , EqTable(..), (==:), (/=:)
49+ , EqTable, (==:), (/=:)
51− , OrdTable(..), (<:), (<=:), (>:), (>=:), ascTable, descTable, greatest, least
50+ , OrdTable, ascTable, descTable
57 -- ** @MaybeTable@
55 -- ** @MaybeTable@
58 , MaybeTable
56 , MaybeTable
59 , maybeTable, ($?), nothingTable, justTable
57 , maybeTable, ($?), nothingTable, justTable
60 , isNothingTable, isJustTable
58 , isNothingTable, isJustTable
62 , optional
59 , optional
63 , catMaybeTable
60 , catMaybeTable
64 , traverseMaybeTable
61 , traverseMaybeTable
@@ -81,7 +78,6 @@ module Rel8
81 , isThisTable, isThatTable, isThoseTable
78 , isThisTable, isThatTable, isThoseTable
82 , hasHereTable, hasThereTable
79 , hasHereTable, hasThereTable
83 , justHereTable, justThereTable
80 , justHereTable, justThereTable
86 , keepHereTable, loseHereTable
82 , keepHereTable, loseHereTable
87 , keepThereTable, loseThereTable
83 , keepThereTable, loseThereTable
@@ -110,37 +106,14 @@ module Rel8
110 , catNonEmptyTable
106 , catNonEmptyTable
111 , catNonEmpty
107 , catNonEmpty
113− -- ** @NullTable@
109+ -- ** @ADT@
115− , nullableTable, nullTable, nullifyTable
116− , isNullTable, isNonNullTable
119− , toNullTable, toMaybeTable
121− -- ** Algebraic data types / sum types
124− -- *** Naming of ADTs
127 , ADT, ADTable
110 , ADT, ADTable
129− -- *** Deconstruction of ADTs
131− , DeconstructADT, deconstructADT
133− -- *** Construction of ADTs
135 , BuildADT, buildADT
111 , BuildADT, buildADT
136 , ConstructADT, constructADT
112 , ConstructADT, constructADT
137−
113+ , DeconstructADT, deconstructADT
138− -- *** Other ADT operations
114+ , NameADT, nameADT
139 , AggregateADT, aggregateADT
115 , AggregateADT, aggregateADT
141− -- *** Miscellaneous notes
144 -- ** @HKD@
117 -- ** @HKD@
145 , HKD, HKDable
118 , HKD, HKDable
146 , BuildHKD, buildHKD
119 , BuildHKD, buildHKD
@@ -165,7 +138,6 @@ module Rel8
165 -- ** @null@
138 -- ** @null@
166 , NotNull
139 , NotNull
167 , Nullable
140 , Nullable
170 , nullify
142 , nullify
171 , nullable
143 , nullable
@@ -184,7 +156,6 @@ module Rel8
184 , (==.), (/=.), (==?), (/=?)
156 , (==.), (/=.), (==?), (/=?)
186 , boolExpr, caseExpr
158 , boolExpr, caseExpr
189 -- ** Ordering
160 -- ** Ordering
@@ -253,11 +224,10 @@ module Rel8
253 , countRows
224 , countRows
254 , groupBy
225 , groupBy
255 , listAgg, listAggExpr
226 , listAgg, listAggExpr
257 , nonEmptyAgg, nonEmptyAggExpr
227 , nonEmptyAgg, nonEmptyAggExpr
258 , DBMax, max
228 , DBMax, max
259 , DBMin, min
229 , DBMin, min
260− , DBSum, sum, sumWhere, avg
230+ , DBSum, sum, sumWhere
261 , DBString, stringAgg
231 , DBString, stringAgg
263 , countStar
233 , countStar
@@ -275,26 +245,6 @@ module Rel8
275 , nullsLast
245 , nullsLast
277 -- ** Window functions
247 -- ** Window functions
298 , indexed
248 , indexed
300 -- ** Bindings
250 -- ** Bindings
@@ -334,7 +284,6 @@ module Rel8
335 -- ** @CREATE VIEW@
285 -- ** @CREATE VIEW@
336 , createView
286 , createView
337− , createOrReplaceView
339 -- ** Sequences
288 -- ** Sequences
340 , nextval
289 , nextval
@@ -353,7 +302,6 @@ import Rel8.Column.Lift
353import Rel8.Column.List
302import Rel8.Column.List
354import Rel8.Column.Maybe
303import Rel8.Column.Maybe
355import Rel8.Column.NonEmpty
304import Rel8.Column.NonEmpty
356−import Rel8.Column.Null
357import Rel8.Column.These
305import Rel8.Column.These
358import Rel8.Expr
306import Rel8.Expr
359import Rel8.Expr.Aggregate
307import Rel8.Expr.Aggregate
@@ -367,8 +315,6 @@ import Rel8.Expr.Ord
367import Rel8.Expr.Order
315import Rel8.Expr.Order
368import Rel8.Expr.Serialize
316import Rel8.Expr.Serialize
369import Rel8.Expr.Sequence
317import Rel8.Expr.Sequence
370−import Rel8.Expr.Text ( like, ilike )
371−import Rel8.Expr.Window hiding ( cumulative )
372import Rel8.Generic.Rel8able ( KRel8able, Rel8able )
318import Rel8.Generic.Rel8able ( KRel8able, Rel8able )
373import Rel8.Order
319import Rel8.Order
374import Rel8.Query
320import Rel8.Query
@@ -385,12 +331,10 @@ import Rel8.Query.List
385import Rel8.Query.Maybe
331import Rel8.Query.Maybe
386import Rel8.Query.Null
332import Rel8.Query.Null
387import Rel8.Query.Order
333import Rel8.Query.Order
388−import Rel8.Query.Rebind
389import Rel8.Query.SQL (showQuery)
334import Rel8.Query.SQL (showQuery)
390import Rel8.Query.Set
335import Rel8.Query.Set
391import Rel8.Query.These
336import Rel8.Query.These
392import Rel8.Query.Values
337import Rel8.Query.Values
393−import Rel8.Query.Window
394import Rel8.Schema.Field
338import Rel8.Schema.Field
395import Rel8.Schema.HTable
339import Rel8.Schema.HTable
396import Rel8.Schema.Name
340import Rel8.Schema.Name
@@ -417,8 +361,6 @@ import Rel8.Table.List
417import Rel8.Table.Maybe
361import Rel8.Table.Maybe
418import Rel8.Table.Name
362import Rel8.Table.Name
419import Rel8.Table.NonEmpty
363import Rel8.Table.NonEmpty
420−import Rel8.Table.Null
421−import Rel8.Table.Opaleye ( castTable )
422import Rel8.Table.Ord
364import Rel8.Table.Ord
423import Rel8.Table.Order
365import Rel8.Table.Order
424import Rel8.Table.Projection
366import Rel8.Table.Projection
@@ -426,7 +368,6 @@ import Rel8.Table.Rel8able ()
426import Rel8.Table.Serialize
368import Rel8.Table.Serialize
427import Rel8.Table.These
369import Rel8.Table.These
428import Rel8.Table.Transpose
370import Rel8.Table.Transpose
429−import Rel8.Table.Window
430import Rel8.Type
371import Rel8.Type
431import Rel8.Type.Composite
372import Rel8.Type.Composite
432import Rel8.Type.Eq
373import Rel8.Type.Eq
@@ -441,7 +382,6 @@ import Rel8.Type.ReadShow
441import Rel8.Type.Semigroup
382import Rel8.Type.Semigroup
442import Rel8.Type.String
383import Rel8.Type.String
443import Rel8.Type.Sum
384import Rel8.Type.Sum
447-- $running
387-- $running
@@ -449,208 +389,3 @@ import Rel8.Window
449-- provides 'select', 'insert', 'update' and 'delete' functions. Note that
389-- provides 'select', 'insert', 'update' and 'delete' functions. Note that
450-- 'insert', 'update' and 'delete' will generally need the
390-- 'insert', 'update' and 'delete' will generally need the
451-- `DuplicateRecordFields` language extension enabled.
391-- `DuplicateRecordFields` language extension enabled.
454−-- Algebraic data types can be modelled between Haskell and SQL.
456−-- * Your SQL table needs a certain text field that tags which Haskell constructor is in use.
457−-- * You have to use a few combinators to specify the sum type's individual constructors.
458−-- * If you want to do case analysis at the @Expr@ (SQL) level, you can use 'maybe'/'either'-like eliminators.
460−-- The documentation in this section will assume a set of database types like this:
463−-- data Thing f = ThingEmployer (Employer f) | ThingPotato (Potato f) | Nullary
464−-- deriving stock Generic
466−-- data Employer f = Employer { employerId :: f Int32, employerName :: f Text}
467−-- deriving stock Generic
468−-- deriving anyclass Rel8able
470−-- data Potato f = Potato { size :: f Int32, grower :: f Text }
471−-- deriving stock Generic
472−-- deriving anyclass Rel8able
477−-- First, in your 'TableSchema', name your type like this:
480−-- thingSchema :: TableSchema (ADT Thing Name)
483−-- { schema = Nothing,
489−-- { employerName = \"name\",
490−-- employerId = \"id\"
492−-- Potato {size = \"size\", grower = \"Mary\"}
496−-- Note that @nameADT \@Thing "tag"@ is variadic: it accepts one
497−-- argument per constructor, except the nullary ones (Nullary) because
498−-- there's nothing to do for them.
502−-- To deconstruct sum types at the SQL level, use 'deconstructADT',
503−-- which is also variadic, and has one argument for each
504−-- constructor. Similar to 'maybe'.
507−-- query :: Query (ADT Thing Expr)
509−-- thingExpr <- each thingSchema
511−-- deconstructADT @Thing
512−-- (\employer -> employerName employer ==. lit \"Mary\")
513−-- (\potato -> grower potato ==. lit \"Mary\")
514−-- (lit False) -- Nullary case
523−-- CAST("tag0_1" AS text) as "tag",
524−-- CAST("id1_1" AS int4) as "ThingEmployer/_1/employerId",
525−-- CAST("name2_1" AS text) as "ThingEmployer/_1/employerName",
526−-- CAST("size3_1" AS int4) as "ThingPotato/_1/size",
527−-- CAST("Mary4_1" AS text) as "ThingPotato/_1/grower"
531−-- "tag" as "tag0_1",
533−-- "name" as "name2_1",
534−-- "size" as "size3_1",
535−-- "Mary" as "Mary4_1"
536−-- FROM "thing" as "T1") as "T1"
537−-- WHERE (CASE WHEN ("tag0_1") = (CAST(E'ThingPotato' AS text)) THEN ("Mary4_1") = (CAST(E'Mary' AS text))
538−-- WHEN ("tag0_1") = (CAST(E'Nullary' AS text)) THEN CAST(FALSE AS bool) ELSE ("name2_1") = (CAST(E'Mary' AS text)) END)) as "T1"
543−-- To construct an ADT, you can use 'buildADT' or 'constructADT'. Consider the following type:
546−-- data Task f = Pending | Complete (CompletedTask f)
549−-- 'buildADT' is for constructing values of 'Task' in the 'Expr'
550−-- context. 'buildADT' needs two type-level arguments before its type
551−-- makes any sense. The first argument is the type of the "ADT", which
552−-- in our case is 'Task'. The second is the name of the constructor we
553−-- want to use. So that means we have the following possible
554−-- instantiations of 'buildADT' for 'Task':
557−-- > :t buildADT @Task @\"Pending\"
558−-- buildADT @Task @\"Pending\" :: ADT Task Expr
559−-- > :t buildADT @Task @\"Complete\"
560−-- buildADT @Task @\"Complete\" :: CompletedTask Expr -> ADT Task Expr
563−-- Note that as the "Pending" constructor has no fields, @buildADT
564−-- \@Task \@"Pending"@ is equivalent to @lit Pending@. But @buildADT
565−-- \@Task \@"Complete"@ is not the same as @lit . Complete@:
568−-- > :t lit . Complete
569−-- lit . Complete :: CompletedTask Result -> ADT Task Expr
573−-- Note that the former takes a @CompletedTask Expr@ while the latter
574−-- takes a @CompletedTask Result@. The former is more powerful because
575−-- you can construct @Task@s using dynamic values coming a database
578−-- To show what this can look like in SQL, consider:
582−-- showQuery $ values
583−-- [ buildADT @Task @\"Pending\"
584−-- , buildADT @Task @\"Complete\" CompletedTask {date = Rel8.Expr.Time.now}
589−-- This produces the following SQL:
593−-- CAST(\"values0_1\" AS text) as \"tag\",
594−-- CAST(\"values1_1\" AS timestamptz) as \"Complete/_1/date\"
597−-- FROM (SELECT \"column1\" as \"values0_1\",
598−-- \"column2\" as \"values1_1\"
601−-- (CAST(E'Pending' AS text),CAST(NULL AS timestamptz)),
602−-- (CAST(E'Complete' AS text),CAST(now() AS timestamptz))) as \"V\") as \"T1\") as \"T1\"
605−-- This is what you get if you run it in @psql@:
609−-- tag | Complete/_1/date
610−-- ----------+-------------------------------
612−-- Complete | 2022-05-19 21:28:23.969065+00
616−-- "constructADT" is less convenient but more general alternative to
617−-- "buildADT". It requires only one type-level argument for its type
621−-- > :t constructADT @Task
622−-- constructADT @Task
623−-- :: (forall r. r -> (CompletedTask Expr -> r) -> r) -> ADT Task Expr
626−-- This might still seem a bit opaque, but basically it gives you a
627−-- Church-encoded constructor for arbitrary algebraic data types. You
628−-- might use it as follows:
632−-- pending :: ADT Task Expr
633−-- pending = constructADT @Task $ \pending _complete -> pending
635−-- complete :: ADT Task Expr
636−-- complete = constructADT @Task $ \_pending complete -> complete CompletedTask {date = Rel8.Expr.Time.now}
639−-- These values are otherwise identical to the ones we saw above with
640−-- @buildADT@, it's just a different style of constructing them.
645−-- 1. Note that the order of the arguments for all of these functions
646−-- is determined by the order of the constructors in the data
647−-- definition. If it were @data Task = Complete (CompletedTask f) |
648−-- Pending@ then the order of all the invocations of @constructADT@
649−-- and @deconstructADT@ would need to change.
651−-- 2. Maybe this is obvious, but just to spell it out: once you're in
652−-- the @Result@ context, you can of course construct @Task@ values
653−-- normally and use standard Haskell pattern-matching. @constructADT@
654−-- and @deconstructADT@ are specifically only needed in the @Expr@
655−-- context, and they allow you to do the equivalent of pattern
656−-- matching in PostgreSQL.