@@ -38,7 +38,6 @@ module Rel8
3838 , HMaybe
3939 , HList
4040 , HNonEmpty
41− , HNull
4241 , HThese
4342 , Lift
4443
@@ -47,18 +46,16 @@ module Rel8
4746 , Transposes
4847 , AltTable((<|>:))
4948 , AlternativeTable( emptyTable )
50− , EqTable(..), (==:), (/=:)
51− , OrdTable(..), (<:), (<=:), (>:), (>=:), ascTable, descTable, greatest, least
49+ , EqTable, (==:), (/=:)
50+ , OrdTable, ascTable, descTable
5251 , lit
5352 , bool
5453 , case_
55− , castTable
5654
5755 -- ** @MaybeTable@
5856 , MaybeTable
5957 , maybeTable, ($?), nothingTable, justTable
6058 , isNothingTable, isJustTable
61− , fromMaybeTable
6259 , optional
6360 , catMaybeTable
6461 , traverseMaybeTable
@@ -81,7 +78,6 @@ module Rel8
8178 , isThisTable, isThatTable, isThoseTable
8279 , hasHereTable, hasThereTable
8380 , justHereTable, justThereTable
84− , alignMaybeTable
8581 , alignBy
8682 , keepHereTable, loseHereTable
8783 , keepThereTable, loseThereTable
@@ -94,7 +90,7 @@ module Rel8
9490
9591 -- ** @ListTable@
9692 , ListTable
97− , listTable, ($*)
93+ , listTable
9894 , nameListTable
9995 , many
10096 , manyExpr
@@ -103,44 +99,21 @@ module Rel8
10399
104100 -- ** @NonEmptyTable@
105101 , NonEmptyTable
106− , nonEmptyTable, ($+)
102+ , nonEmptyTable
107103 , nameNonEmptyTable
108104 , some
109105 , someExpr
110106 , catNonEmptyTable
111107 , catNonEmpty
112108
113− -- ** @NullTable@
114− , NullTable
115− , nullableTable, nullTable, nullifyTable
116− , isNullTable, isNonNullTable
117− , catNullTable
118− , nameNullTable
119− , toNullTable, toMaybeTable
120−
121− -- ** Algebraic data types / sum types
122− -- $adts
123−
124− -- *** Naming of ADTs
125− -- $naming
126− , NameADT, nameADT
109+ -- ** @ADT@
127110 , ADT, ADTable
128−
129− -- *** Deconstruction of ADTs
130− -- $deconstruction
131− , DeconstructADT, deconstructADT
132−
133− -- *** Construction of ADTs
134− -- $construction
135111 , BuildADT, buildADT
136112 , ConstructADT, constructADT
137−
138− -- *** Other ADT operations
113+ , DeconstructADT, deconstructADT
114+ , NameADT, nameADT
139115 , AggregateADT, aggregateADT
140116
141− -- *** Miscellaneous notes
142− -- $misc-notes
143−
144117 -- ** @HKD@
145118 , HKD, HKDable
146119 , BuildHKD, buildHKD
@@ -165,7 +138,6 @@ module Rel8
165138 -- ** @null@
166139 , NotNull
167140 , Nullable
168− , Homonullable
169141 , null
170142 , nullify
171143 , nullable
@@ -184,7 +156,6 @@ module Rel8
184156 , (==.), (/=.), (==?), (/=?)
185157 , in_
186158 , boolExpr, caseExpr
187− , like, ilike
188159
189160 -- ** Ordering
190161 , DBOrd
@@ -253,11 +224,10 @@ module Rel8
253224 , countRows
254225 , groupBy
255226 , listAgg, listAggExpr
256− , mode
257227 , nonEmptyAgg, nonEmptyAggExpr
258228 , DBMax, max
259229 , DBMin, min
260− , DBSum, sum, sumWhere, avg
230+ , DBSum, sum, sumWhere
261231 , DBString, stringAgg
262232 , count
263233 , countStar
@@ -275,26 +245,6 @@ module Rel8
275245 , nullsLast
276246
277247 -- ** Window functions
278− , Window
279− , window
280− , Partition
281− , over
282− , partitionBy
283− , orderPartitionBy
284− , cumulative
285− , cumulative_
286− , currentRow
287− , rowNumber
288− , rank
289− , denseRank
290− , percentRank
291− , cumeDist
292− , ntile
293− , lag
294− , lead
295− , firstValue
296− , lastValue
297− , nthValue
298248 , indexed
299249
300250 -- ** Bindings
@@ -334,7 +284,6 @@ module Rel8
334284
335285 -- ** @CREATE VIEW@
336286 , createView
337− , createOrReplaceView
338287
339288 -- ** Sequences
340289 , nextval
@@ -353,7 +302,6 @@ import Rel8.Column.Lift
353302import Rel8.Column.List
354303import Rel8.Column.Maybe
355304import Rel8.Column.NonEmpty
356−import Rel8.Column.Null
357305import Rel8.Column.These
358306import Rel8.Expr
359307import Rel8.Expr.Aggregate
@@ -367,8 +315,6 @@ import Rel8.Expr.Ord
367315import Rel8.Expr.Order
368316import Rel8.Expr.Serialize
369317import Rel8.Expr.Sequence
370−import Rel8.Expr.Text ( like, ilike )
371−import Rel8.Expr.Window hiding ( cumulative )
372318import Rel8.Generic.Rel8able ( KRel8able, Rel8able )
373319import Rel8.Order
374320import Rel8.Query
@@ -385,12 +331,10 @@ import Rel8.Query.List
385331import Rel8.Query.Maybe
386332import Rel8.Query.Null
387333import Rel8.Query.Order
388−import Rel8.Query.Rebind
389334import Rel8.Query.SQL (showQuery)
390335import Rel8.Query.Set
391336import Rel8.Query.These
392337import Rel8.Query.Values
393−import Rel8.Query.Window
394338import Rel8.Schema.Field
395339import Rel8.Schema.HTable
396340import Rel8.Schema.Name
@@ -417,8 +361,6 @@ import Rel8.Table.List
417361import Rel8.Table.Maybe
418362import Rel8.Table.Name
419363import Rel8.Table.NonEmpty
420−import Rel8.Table.Null
421−import Rel8.Table.Opaleye ( castTable )
422364import Rel8.Table.Ord
423365import Rel8.Table.Order
424366import Rel8.Table.Projection
@@ -426,7 +368,6 @@ import Rel8.Table.Rel8able ()
426368import Rel8.Table.Serialize
427369import Rel8.Table.These
428370import Rel8.Table.Transpose
429−import Rel8.Table.Window
430371import Rel8.Type
431372import Rel8.Type.Composite
432373import Rel8.Type.Eq
@@ -441,7 +382,6 @@ import Rel8.Type.ReadShow
441382import Rel8.Type.Semigroup
442383import Rel8.Type.String
443384import Rel8.Type.Sum
444−import Rel8.Window
445385
446386
447387-- $running
@@ -449,208 +389,3 @@ import Rel8.Window
449389-- provides 'select', 'insert', 'update' and 'delete' functions. Note that
450390-- 'insert', 'update' and 'delete' will generally need the
451391-- `DuplicateRecordFields` language extension enabled.
452−
453−-- $adts
454−-- Algebraic data types can be modelled between Haskell and SQL.
455−--
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.
459−--
460−-- The documentation in this section will assume a set of database types like this:
461−--
462−-- @
463−-- data Thing f = ThingEmployer (Employer f) | ThingPotato (Potato f) | Nullary
464−-- deriving stock Generic
465−--
466−-- data Employer f = Employer { employerId :: f Int32, employerName :: f Text}
467−-- deriving stock Generic
468−-- deriving anyclass Rel8able
469−--
470−-- data Potato f = Potato { size :: f Int32, grower :: f Text }
471−-- deriving stock Generic
472−-- deriving anyclass Rel8able
473−-- @
474−
475−-- $naming
476−--
477−-- First, in your 'TableSchema', name your type like this:
478−--
479−-- @
480−-- thingSchema :: TableSchema (ADT Thing Name)
481−-- thingSchema =
482−-- TableSchema
483−-- { schema = Nothing,
484−-- name = \"thing\",
485−-- columns =
486−-- nameADT @Thing
487−-- \"tag\"
488−-- Employer
489−-- { employerName = \"name\",
490−-- employerId = \"id\"
491−-- }
492−-- Potato {size = \"size\", grower = \"Mary\"}
493−-- }
494−-- @
495−--
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.
499−
500−-- $deconstruction
501−--
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'.
505−--
506−-- @
507−-- query :: Query (ADT Thing Expr)
508−-- query = do
509−-- thingExpr <- each thingSchema
510−-- where_ $
511−-- deconstructADT @Thing
512−-- (\employer -> employerName employer ==. lit \"Mary\")
513−-- (\potato -> grower potato ==. lit \"Mary\")
514−-- (lit False) -- Nullary case
515−-- thingExpr
516−-- pure thingExpr
517−-- @
518−--
519−-- SQL output:
520−--
521−-- @
522−-- SELECT
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"
528−-- FROM (SELECT
529−-- *
530−-- FROM (SELECT
531−-- "tag" as "tag0_1",
532−-- "id" as "id1_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"
539−-- @
540−
541−-- $construction
542−--
543−-- To construct an ADT, you can use 'buildADT' or 'constructADT'. Consider the following type:
544−--
545−-- @
546−-- data Task f = Pending | Complete (CompletedTask f)
547−-- @
548−--
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':
555−--
556−-- @
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
561−-- @
562−--
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@:
566−--
567−-- @
568−-- > :t lit . Complete
569−-- lit . Complete :: CompletedTask Result -> ADT Task Expr
570−-- @
571−--
572−--
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
576−-- query.
577−--
578−-- To show what this can look like in SQL, consider:
579−--
580−-- @
581−-- > :{
582−-- showQuery $ values
583−-- [ buildADT @Task @\"Pending\"
584−-- , buildADT @Task @\"Complete\" CompletedTask {date = Rel8.Expr.Time.now}
585−-- ]
586−-- :}
587−-- @
588−--
589−-- This produces the following SQL:
590−--
591−-- @
592−-- SELECT
593−-- CAST(\"values0_1\" AS text) as \"tag\",
594−-- CAST(\"values1_1\" AS timestamptz) as \"Complete/_1/date\"
595−-- FROM (SELECT
596−-- *
597−-- FROM (SELECT \"column1\" as \"values0_1\",
598−-- \"column2\" as \"values1_1\"
599−-- FROM
600−-- (VALUES
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\"
603−-- @
604−--
605−-- This is what you get if you run it in @psql@:
606−--
607−--
608−-- @
609−-- tag | Complete/_1/date
610−-- ----------+-------------------------------
611−-- Pending |
612−-- Complete | 2022-05-19 21:28:23.969065+00
613−-- (2 rows)
614−-- @
615−--
616−-- "constructADT" is less convenient but more general alternative to
617−-- "buildADT". It requires only one type-level argument for its type
618−-- to make sense:
619−--
620−-- @
621−-- > :t constructADT @Task
622−-- constructADT @Task
623−-- :: (forall r. r -> (CompletedTask Expr -> r) -> r) -> ADT Task Expr
624−-- @
625−--
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:
629−--
630−-- @
631−-- let
632−-- pending :: ADT Task Expr
633−-- pending = constructADT @Task $ \pending _complete -> pending
634−--
635−-- complete :: ADT Task Expr
636−-- complete = constructADT @Task $ \_pending complete -> complete CompletedTask {date = Rel8.Expr.Time.now}
637−-- @
638−--
639−-- These values are otherwise identical to the ones we saw above with
640−-- @buildADT@, it's just a different style of constructing them.
641−--
642−
643−-- $misc-notes
644−--
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.
650−--
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.