@@ -38,7 +38,6 @@ module Rel8
3838 , HMaybe
3939 , HList
4040 , HNonEmpty
41− , HNull
4241 , HThese
4342 , Lift
4443
@@ -47,8 +46,8 @@ module Rel8
4746 , Transposes
4847 , AltTable((<|>:))
4948 , AlternativeTable( emptyTable )
50− , EqTable(..), (==:), (/=:)
51− , OrdTable(..), (<:), (<=:), (>:), (>=:), ascTable, descTable, greatest, least
49+ , EqTable, (==:), (/=:)
50+ , OrdTable, (<:), (<=:), (>:), (>=:), ascTable, descTable, greatest, least
5251 , lit
5352 , bool
5453 , case_
@@ -58,7 +57,6 @@ module Rel8
5857 , MaybeTable
5958 , maybeTable, ($?), nothingTable, justTable
6059 , isNothingTable, isJustTable
61− , fromMaybeTable
6260 , optional
6361 , catMaybeTable
6462 , traverseMaybeTable
@@ -81,7 +79,6 @@ module Rel8
8179 , isThisTable, isThatTable, isThoseTable
8280 , hasHereTable, hasThereTable
8381 , justHereTable, justThereTable
84− , alignMaybeTable
8582 , alignBy
8683 , keepHereTable, loseHereTable
8784 , keepThereTable, loseThereTable
@@ -110,37 +107,14 @@ module Rel8
110107 , catNonEmptyTable
111108 , catNonEmpty
112109
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
110+ -- ** @ADT@
127111 , ADT, ADTable
128−
129− -- *** Deconstruction of ADTs
130− -- $deconstruction
131− , DeconstructADT, deconstructADT
132−
133− -- *** Construction of ADTs
134− -- $construction
135112 , BuildADT, buildADT
136113 , ConstructADT, constructADT
137−
138− -- *** Other ADT operations
114+ , DeconstructADT, deconstructADT
115+ , NameADT, nameADT
139116 , AggregateADT, aggregateADT
140117
141− -- *** Miscellaneous notes
142− -- $misc-notes
143−
144118 -- ** @HKD@
145119 , HKD, HKDable
146120 , BuildHKD, buildHKD
@@ -165,7 +139,6 @@ module Rel8
165139 -- ** @null@
166140 , NotNull
167141 , Nullable
168− , Homonullable
169142 , null
170143 , nullify
171144 , nullable
@@ -184,7 +157,6 @@ module Rel8
184157 , (==.), (/=.), (==?), (/=?)
185158 , in_
186159 , boolExpr, caseExpr
187− , like, ilike
188160
189161 -- ** Ordering
190162 , DBOrd
@@ -253,11 +225,10 @@ module Rel8
253225 , countRows
254226 , groupBy
255227 , listAgg, listAggExpr
256− , mode
257228 , nonEmptyAgg, nonEmptyAggExpr
258229 , DBMax, max
259230 , DBMin, min
260− , DBSum, sum, sumWhere, avg
231+ , DBSum, sum, sumWhere
261232 , DBString, stringAgg
262233 , count
263234 , countStar
@@ -275,26 +246,6 @@ module Rel8
275246 , nullsLast
276247
277248 -- ** 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
298249 , indexed
299250
300251 -- ** Bindings
@@ -334,7 +285,6 @@ module Rel8
334285
335286 -- ** @CREATE VIEW@
336287 , createView
337− , createOrReplaceView
338288
339289 -- ** Sequences
340290 , nextval
@@ -353,7 +303,6 @@ import Rel8.Column.Lift
353303import Rel8.Column.List
354304import Rel8.Column.Maybe
355305import Rel8.Column.NonEmpty
356−import Rel8.Column.Null
357306import Rel8.Column.These
358307import Rel8.Expr
359308import Rel8.Expr.Aggregate
@@ -367,8 +316,6 @@ import Rel8.Expr.Ord
367316import Rel8.Expr.Order
368317import Rel8.Expr.Serialize
369318import Rel8.Expr.Sequence
370−import Rel8.Expr.Text ( like, ilike )
371−import Rel8.Expr.Window hiding ( cumulative )
372319import Rel8.Generic.Rel8able ( KRel8able, Rel8able )
373320import Rel8.Order
374321import Rel8.Query
@@ -390,7 +337,6 @@ import Rel8.Query.SQL (showQuery)
390337import Rel8.Query.Set
391338import Rel8.Query.These
392339import Rel8.Query.Values
393−import Rel8.Query.Window
394340import Rel8.Schema.Field
395341import Rel8.Schema.HTable
396342import Rel8.Schema.Name
@@ -417,7 +363,6 @@ import Rel8.Table.List
417363import Rel8.Table.Maybe
418364import Rel8.Table.Name
419365import Rel8.Table.NonEmpty
420−import Rel8.Table.Null
421366import Rel8.Table.Opaleye ( castTable )
422367import Rel8.Table.Ord
423368import Rel8.Table.Order
@@ -426,7 +371,6 @@ import Rel8.Table.Rel8able ()
426371import Rel8.Table.Serialize
427372import Rel8.Table.These
428373import Rel8.Table.Transpose
429−import Rel8.Table.Window
430374import Rel8.Type
431375import Rel8.Type.Composite
432376import Rel8.Type.Eq
@@ -441,7 +385,6 @@ import Rel8.Type.ReadShow
441385import Rel8.Type.Semigroup
442386import Rel8.Type.String
443387import Rel8.Type.Sum
444−import Rel8.Window
445388
446389
447390-- $running
@@ -449,208 +392,3 @@ import Rel8.Window
449392-- provides 'select', 'insert', 'update' and 'delete' functions. Note that
450393-- 'insert', 'update' and 'delete' will generally need the
451394-- `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.