on 7bd2aa5 help sign in
139 files changed+5218−6040
since splitsnapshots
unifiedsplit
.github/workflows/build.yaml+3−3 Changelog.md−156 README.md−4 cabal.project+5 cabal.project.haskell-nix+6 default.nix+5−5 docs/concepts/insert.rst−181 docs/concepts/queries.rst+9−9 docs/cookbook.rst+8−10 docs/index.rst+1−2 docs/tutorial.rst+31−48 nix/sources.json+9−9 nix/sources.nix+20−66 rel8.cabal+18−45 shell.nix+1−1 src/Rel8.hs+23−315 src/Rel8/Aggregate.hs+109−37 src/Rel8/Column.hs+77−5 src/Rel8/Column/ADT.hs+68−3 src/Rel8/Column/Either.hs+140−4 src/Rel8/Column/Lift.hs+67−3 src/Rel8/Column/List.hs+126−6 src/Rel8/Column/Maybe.hs+105−4 src/Rel8/Column/NonEmpty.hs+127−6 src/Rel8/Column/Null.hs−26 src/Rel8/Column/These.hs+142−5 src/Rel8/Expr.hs+47−17 src/Rel8/Expr.hs-boot+8−6 src/Rel8/Expr/Aggregate.hs+64−25 src/Rel8/Expr/Default.hs−35 src/Rel8/Expr/Eq.hs+1−1 src/Rel8/Expr/Num.hs+6−39 src/Rel8/Expr/Opaleye.hs+3−12 src/Rel8/Expr/Sequence.hs+1−2 src/Rel8/Expr/Text.hs+2−23 src/Rel8/Expr/Window.hs−121 src/Rel8/FCF.hs+3−3 src/Rel8/Generic/Construction.hs+165−146 src/Rel8/Generic/Construction/ADT.hs+47−32 src/Rel8/Generic/Construction/Record.hs+11−6 src/Rel8/Generic/Map.hs+64−20 src/Rel8/Generic/Record.hs−2 src/Rel8/Generic/Reify.hs+24 src/Rel8/Generic/Rel8able.hs+72−178 tests/Rel8/Generic/Rel8able/Test.hs → src/Rel8/Generic/Rel8able/Test.hs+16−47 src/Rel8/Generic/Table.hs+109−24 src/Rel8/Generic/Table/ADT.hs+203−61 src/Rel8/Generic/Table/Record.hs+99−74 src/Rel8/Kind/Context.hs+33−8 src/Rel8/Kind/Labels.hs+57 src/Rel8/Order.hs+9−2 src/Rel8/Query.hs+7−181 src/Rel8/Query.hs-boot+1−3 src/Rel8/Query/Aggregate.hs−19 src/Rel8/Query/Distinct.hs+9−4 src/Rel8/Query/Each.hs+3−3 src/Rel8/Query/Either.hs+6−10 src/Rel8/Query/Evaluate.hs+61−28 src/Rel8/Query/Exists.hs+16−14 src/Rel8/Query/Indexed.hs−21 src/Rel8/Query/List.hs+15−19 src/Rel8/Query/Maybe.hs+29−24 src/Rel8/Query/Null.hs−15 src/Rel8/Query/Opaleye.hs+5−41 src/Rel8/Query/Rebind.hs−27 src/Rel8/Query/SQL.hs+59−6 src/Rel8/Query/Set.hs+6−6 src/Rel8/Query/These.hs+39−52 src/Rel8/Query/Window.hs−26 src/Rel8/Schema/Context.hs+20 src/Rel8/Schema/Context/Label.hs+62 src/Rel8/Schema/Context/Nullify.hs+149−105 src/Rel8/Schema/Field.hs−50 src/Rel8/Schema/HTable.hs+24−51 src/Rel8/Schema/HTable/Either.hs+3−2 src/Rel8/Schema/HTable/Identity.hs+28−16 src/Rel8/Schema/HTable/Label.hs+50−37 src/Rel8/Schema/HTable/MapTable.hs+12−22 src/Rel8/Schema/HTable/Maybe.hs+3−2 src/Rel8/Schema/HTable/Nullify.hs+32−47 src/Rel8/Schema/HTable/These.hs+3−2 src/Rel8/Schema/HTable/Vectorize.hs+45−37 src/Rel8/Schema/Kind.hs+15−6 src/Rel8/Schema/Name.hs+56−34 src/Rel8/Schema/Reify.hs+74 src/Rel8/Schema/Result.hs+41−16 src/Rel8/Schema/Spec.hs+37−17 src/Rel8/Schema/Spec/ConstrainDBType.hs+89 src/Rel8/Schema/Table.hs−20 src/Rel8/Statement/Delete.hs+48−35 src/Rel8/Statement/Insert.hs+72−43 src/Rel8/Statement/OnConflict.hs−106 src/Rel8/Statement/Returning.hs+10−114 src/Rel8/Statement/SQL.hs−29 src/Rel8/Statement/Select.hs+35−120 src/Rel8/Statement/Set.hs−33 src/Rel8/Statement/Update.hs+56−38 src/Rel8/Statement/Using.hs−36 src/Rel8/Statement/View.hs+28−43 src/Rel8/Statement/Where.hs−31 src/Rel8/Table.hs+252−88 src/Rel8/Table/ADT.hs+65−33 src/Rel8/Table/Aggregate.hs+13−24 src/Rel8/Table/Bool.hs+6−5 src/Rel8/Table/Cols.hs−50 src/Rel8/Table/Either.hs+131−126 src/Rel8/Table/Eq.hs+39−12 src/Rel8/Table/HKD.hs+105−65 src/Rel8/Table/List.hs+55−65 src/Rel8/Table/Maybe.hs+118−110 src/Rel8/Table/Name.hs+22−17 src/Rel8/Table/NonEmpty.hs+54−66 src/Rel8/Table/Null.hs−147 src/Rel8/Table/Nullify.hs−211 src/Rel8/Table/Opaleye.hs+64−78 src/Rel8/Table/Ord.hs+45−16 src/Rel8/Table/Order.hs+16−13 src/Rel8/Table/Projection.hs−73 src/Rel8/Table/Recontextualize.hs+90 src/Rel8/Table/Rel8able.hs+85−29 src/Rel8/Table/Serialize.hs+122−22 src/Rel8/Table/Tag.hs+97 src/Rel8/Table/These.hs+148−164 src/Rel8/Table/Transpose.hs−46 src/Rel8/Table/Undefined.hs+5−5 src/Rel8/Table/Unreify.hs+102 src/Rel8/Table/Window.hs−43 src/Rel8/Tabulate.hs−640 src/Rel8/Type.hs+8−9 src/Rel8/Type/Composite.hs+19−20 src/Rel8/Type/Enum.hs−1 src/Rel8/Type/JSONBEncoded.hs−4 src/Rel8/Type/JSONEncoded.hs−4 src/Rel8/Type/Num.hs+1−4 src/Rel8/Type/Ord.hs+2 src/Rel8/Type/ReadShow.hs−3 src/Rel8/Type/Tag.hs+1−2 src/Rel8/Window.hs−97 tests/Main.hs+161−320
.github/workflows/build.yaml+3−3
@@ -9,14 +9,14 @@ jobs:
99 persist-credentials: false
1010 submodules: true
1111
12− - uses: cachix/install-nix-action@v14.1
12+ - uses: cachix/install-nix-action@v12
1313 with:
1414 nix_path: nixpkgs=channel:nixos-unstable
1515 extra_nix_config: |
1616 trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=
17− substituters = https://cache.nixos.org/ https://cache.iog.io
17+ substituters = https://cache.nixos.org/ https://hydra.iohk.io
1818
19− - uses: cachix/cachix-action@v10
19+ - uses: cachix/cachix-action@v8
2020 with:
2121 name: rel8
2222 authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
Changelog.md−156
@@ -1,159 +1,3 @@
1−# 1.4.0.0 (2002-08-17)
2−
3−## Breaking changes
4−
5−* The behavior of `greatest`/`least` has been corrected, and was previously flipped. ([#183](https://github.com/circuithub/rel8/pull/183))
6−
7−## New features
8−
9−* `NullTable`/`HNull` have been added. This is an alternative to `MaybeTable` that doesn't use a tag columns. It's less flexible (no `Functor` or `Applicative` instance) and is meaningless when used with a table that has no non-nullable columns (so nesting `NullTable` is redundant). But in situations where the underlying `Table` does have non-nullable columns, it can losslessly converted to and from `MaybeTable`. It is useful for embedding into a base table when you don't want to store the extra tag column in your schema. ([#173](https://github.com/circuithub/rel8/pull/173))
10−* Add `fromMaybeTable`. ([#179](https://github.com/circuithub/rel8/pull/179))
11−* Add `alignMaybeTable`. ([#196](https://github.com/circuithub/rel8/pull/196))
12−
13−## Improvements
14−
15−* Optimize implementation of `AltTable` for `Tabulation` ([#178](https://github.com/circuithub/rel8/pull/178))
16−
17−## Other
18−
19−* Documentation improvements for `HADT`. ([#177](https://github.com/circuithub/rel8/pull/177))
20−* Document example usage of `groupBy`. ([#184](https://github.com/circuithub/rel8/pull/184))
21−* Build with and require Opaleye >= 0.9.3.3. ([#190](https://github.com/circuithub/rel8/pull/190))
22−* Build with `hasql` 1.6. ([#195](https://github.com/circuithub/rel8/pull/195))
23−
24−# 1.3.1.0 (2022-01-20)
25−
26−## Other
27−
28−* Rel8 now requires Opaleye >= 0.9.1. ([#165](https://github.com/circuithub/rel8/pull/165))
29−
30−# 1.3.0.0 (2022-01-31)
31−
32−## Breaking changes
33−
34−* `div` and `mod` have been changed to match Haskell semantics. If you need the PostgreSQL `div()` and `mod()` functions, use `quot` and `rem`. While this is not an API change, we feel this is a breaking change in semantics and have bumped the major version number. ([#155](https://github.com/circuithub/rel8/pull/155))
35−
36−## New features
37−
38−* `divMod` and `quotRem` functions have been added, matching Haskell's `Prelude` functions. ([#155](https://github.com/circuithub/rel8/pull/155))
39−* `avg` and `mode` aggregation functions to find the mean value of an expression, or the most common row in a query, respectively. ([#152](https://github.com/circuithub/rel8/pull/152))
40−* The full `EqTable` and `OrdTable` classes have been exported, allowing for instances to be manually created. ([#157](https://github.com/circuithub/rel8/pull/157))
41−* Added `like` and `ilike` (for the `LIKE` and `ILIKE` operators). ([#146](https://github.com/circuithub/rel8/pull/146))
42−
43−## Other
44−
45−* Rel8 now requires Opaleye 0.9. ([#158](https://github.com/circuithub/rel8/pull/158))
46−* Rel8's test suite supports Hedgehog 1.1. ([#160](https://github.com/circuithub/rel8/pull/160))
47−* The documentation for binary operations has been corrected. ([#162](https://github.com/circuithub/rel8/pull/162))
48−
49−# 1.2.2.0 (2021-11-21)
50−
51−## Other
52−
53−* Support GHC 9.2 ([#145](https://github.com/circuithub/rel8/pull/145))
54−* Correct the documentation for `except` ([#147](https://github.com/circuithub/rel8/pull/147))
55−* Support `hasql` 1.5 ([#149](https://github.com/circuithub/rel8/pull/149))
56−
57−# 1.2.1.0 (2021-11-21)
58−
59−## New features
60−
61−* `castTable` has been added, which casts all columns selected from in `Query` according to the types of columns. This can occasionally be useful when using `many`/`some` on older PostgreSQL versions. ([#137](https://github.com/circuithub/rel8/pull/137))
62−
63−* Added `<:`, `<=:`, `>:`, `>=:`, `greatest` and `least`, which sort all columns in a table lexicographically. These operators are like the `<.` operators which operate on `Expr`s, but the `<:` operate on entire tables. ([#139](https://github.com/circuithub/rel8/pull/139))
64−
65−## Other
66−
67−* Support opaleye-0.8 ([#142](https://github.com/circuithub/rel8/pull/142))
68−
69−# 1.2.0.0 (2021-10-22)
70−
71−## New features
72−
73−* New `$*` and `$+` operators for projecting out of `ListTable` and `NonEmptyTable` respectively (analogous to the existing `$?` for `MaybeTable`). ([#125](https://github.com/circuithub/rel8/pull/125))
74−
75−## Bug fixes
76−
77−* Fix `UPSERT` support (and add tests), which was broken due to a typo inside Rel8 that made it impossible to construct `Projection`s which are needed by `UPSERT`. ([#134](https://github.com/circuithub/rel8/pull/134))
78−
79−* Remove `DBMin` and `DBMax` instances for `Bool`, which are invalid as far as Postgres is concerned. `and` and `or` can be used to achieve the same thing. ([#126](https://github.com/circuithub/rel8/pull/126))
80−
81−* Fix `aggregateMaybeTable`, `aggregateEitherTable` and `aggregateTheseTable`, which generated invalid SQL previously. ([#127](https://github.com/circuithub/rel8/pull/127))
82−
83−## Breaking changes
84−
85−* `rebind` now takes an additional argument for the "name" of the binding. ([#128](https://github.com/circuithub/rel8/pull/128))
86−
87−# 1.1.0.0 (2021-07-16)
88−
89−## New features
90−
91−* You can derive `Rel8able` for "vanilla" higher-kinded data types - data types that don't use the `Column` type family. For example, the following is now possible:
92−
93− ```haskell
94− data MyTable f = MyTable
95− { myTableX :: f Int32
96− , myTableY :: f Text
97− } deriving (Generic, Rel8able)
98− ```
99−
100−* The `Projectable` and `Biprojectable` type classes have been introduced. These classes model a type of functors and bifunctors (respectively) on tables, where the mapping function is only able to rearrange columns. For example, the following is now possible:
101−
102− ```haskell
103− x <- project myTableX <$> many ...
104− ```
105−
106−* `Insert`'s `onConflict` field now has a new option - `DoUpdate`. This maps to PostgreSQL's `ON CONFLICT DO UPDATE` syntax, which provides upsert support. ([#85](https://github.com/circuithub/rel8/pull/85))
107−
108−* `Insert`, `Update` and `Delete` has all been expanded to work with `Query`s. In particular:
109−
110− * `Delete` now supports a `USING` sub-query
111− * `Update` now supports a `FROM` sub-query
112− * `Insert` can delete the result of any arbitrary `Query` (providing the types match)
113−
114−* The new `Rel8.Tabulate` module has been added, which gives a `Map`-esque interface to writing and composing queries. ([#70](https://github.com/circuithub/rel8/pull/70))
115−
116−* The new `indexed` `Query -> Query` function was added. This function augments each row in a query with it's 0-based index. ([#117](https://github.com/circuithub/rel8/pull/117))
117−
118−## Breaking changes
119−
120−* `Insert`, `Delete` and `Update` have all changed. In particular, for `Insert` users should now replace `rows = xs` with `rows = values xs`. ([#85](https://github.com/circuithub/rel8/pull/85))
121−
122−* Rename `whereExists` and `whereNotExists` to `present` and `absent` respectively. ([#57](https://github.com/circuithub/rel8/pull/57))
123−
124−* Simplify `evaluate` to run directly inside the `Query` monad, rendering the `Evaluate` monad unnecessary. ([#56](https://github.com/circuithub/rel8/pull/56))
125−
126−* The `Labelable` type class has been removed as it's no longer necessary. This class was mostly internal, though was visible in some public API functions. ([#76](https://github.com/circuithub/rel8/pull/76))
127−
128−* `EitherTable`, `ListTable`, `MaybeTable`, `NonEmptyTable` and `TheseTable` now all have an extra parameter which tracks the context of the table. If you manually specified any types before, you need to provide this parameter - usually `Expr` will be the correct choice. ([#101](https://github.com/circuithub/rel8/pull/101))
129−
130−* We've done a lot of work to simplify the internals of Rel8, but some of these are breaking changes. However, most of these API changes shouldn't affect users as they are mostly types used in type inference, or affect classes where users don't need to define their own instances.
131−
132− * The kind of a `Rel8able` is now `(Type -> Type) -> Type`. Previously it was `(X -> Type) -> Type`.
133− * `Table` has a new associated type - `FromExprs`. This was previously an open type family.
134− * `Table` has a new associated type - `Transpose` - and `Recontextualise` has been renamed to `Transposes`. This `Transposes` class now operates in terms of `Transpose`.
135−
136−* `select`, `insert`, `update` and `delete` now produce Hasql `Statement`s, rather than actually running the statement as IO. This allows Rel8 to be used with transaction/connection-managing monads like [`hasql-transaction`](https://hackage.haskell.org/package/hasql-transaction). ([#94](https://github.com/circuithub/rel8/pull/94))
137−
138−## Bug fixes
139−
140−* Fixes a bug where cartesian products of queries using `catListTable`, `catNonEmptyTable`, `catList` and `catNonEmpty` would incorrectly be zipped instead. ([#61](https://github.com/circuithub/rel8/pull/61))
141−
142−* Require Opaleye 0.7.3.0. This version has better support for lateral queries, which can improve query plans, especially in `optional`/`LEFT JOIN` ([#72](https://github.com/circuithub/rel8/pull/72))
143−
144−* Rel8 now compiles with GHC 9. ([#97](https://github.com/circuithub/rel8/pull/97))
145−
146−## Other changes
147−
148−* `exists` is now implemented in terms of the SQL `EXISTS` keyword. ([#69](https://github.com/circuithub/rel8/pull/69))
149−
150−* `alignBy` no longer requires `Table`s. ([#67](https://github.com/circuithub/rel8/pull/67))
151−
152−
153−# 1.0.0.1 (2021-06-21)
154−
155−This release contains various fixes for documentation.
156−
1571# 1.0.0.0 (2021-06-18)
1582
1593* Initial release.
README.md−4
@@ -10,8 +10,4 @@ The main objectives of Rel8 are:
1010
1111* *Familiar*: writing Rel8 queries should feel like normal Haskell programming.
1212
13−Rel8 was presented at ZuriHac 2021. If you want to have a brief overview of what Rel8 is, and a tour of the API - check out the video below:
14−
15−[![Rel8 presentation at ZuriHac 2021](https://img.youtube.com/vi/3uwrtjxiq6E/hqdefault.jpg)](http://www.youtube.com/watch?v=3uwrtjxiq6E)
16−
1713For more details, check out the [official documentation](https://rel8.readthedocs.io/en/latest/).
cabal.project+5
@@ -1 +1,6 @@
11packages: .
2+
3+allow-newer: generic-monoid:base
4+allow-newer: cryptohash-sha1:base
5+allow-newer: cryptohash-md5:base
6+allow-newer: entropy:Cabal
cabal.project.haskell-nix+6
@@ -2,3 +2,9 @@
22-- will interpret them as local packages, and try to build them when we cabal
33-- build. The only reason we have to specify these is for Haskell.nix to know to
44-- override these packages by fetching them rather than using Hackage.
5+
6+source-repository-package
7+ type: git
8+ location: git://github.com/jcpetruzza/barbies
9+ tag: f99b05454874192e3511bd133555dfb6cc6a6ecb
10+ --sha256: 0yy2i2jbllwavv5d2176rf8lmm4l1ws90lxkmdlfgvfzqxidx0gi
default.nix+5−5
@@ -1,15 +1,15 @@
1−let
1+let
22 haskellNix = import (import ./nix/sources.nix)."haskell.nix" {};
33
4− nixpkgsSrc = haskellNix.sources.nixpkgs-unstable;
4+ nixpkgsSrc = haskellNix.sources.nixpkgs-2009;
55
66 nixpkgsArgs = haskellNix.nixpkgsArgs;
77
8− compiler-nix-name = "ghc924";
8+ compiler-nix-name = "ghc8104";
99
1010 pkgs = import nixpkgsSrc nixpkgsArgs;
1111
12−in
12+in
1313pkgs.haskell-nix.project {
1414 inherit compiler-nix-name;
1515
@@ -20,7 +20,7 @@ pkgs.haskell-nix.project {
2020 src = ./.;
2121 };
2222
23− modules = [
23+ modules = [
2424 { packages.rel8 = {
2525 preCheck = ''
2626 export PATH="${pkgs.postgresql}/bin:${"$PATH"}"
docs/concepts/insert.rstdeleted−181
@@ -1,181 +0,0 @@
1−``INSERT``, ``UPDATE`` and ``DELETE``
2−=====================================
3−
4−While the majority of Rel8 is about building and executing ``SELECT``
5−statement, Rel8 also has support for ``INSERT``, ``UPDATE`` and ``DELETE``.
6−These statements are all executed using the ``insert``, ``update`` and
7−``delete`` functions, all of which take a record of parameters.
8−
9−.. note::
10−
11− This part of Rel8's API uses the ``DuplicateRecordFields`` language
12− extension. In code that needs to use this API, you should also enable this
13− language extension, or you may get errors about ambiguous field names.
14−
15−``DELETE``
16−----------
17−
18−To perform a ``DELETE`` statement, construct a ``Delete`` value and execute it
19−using ``delete``. ``Delete`` takes:
20−
21−``from``
22− The ``TableSchema`` for the table to delete rows from.
23−
24−``using``
25− This is a simple ``Query`` that forms the ``USING`` clause of the ``DELETE``
26− statement. This can be used to join against other tables, and the results
27− can be referenced in the ``deleteWhere`` parameter. For simple ``DELETE``\s
28− where you don't need to do this, you can set ``using = pure ()``.
29−
30−``deleteWhere``
31− The ``WHERE`` clause of the ``DELETE`` statement. This is a function that
32− takes two inputs: the result of the ``using`` query, and the current value
33− of the row.
34−
35−``returning``
36− What to return - see :ref:`returning`.
37−
38−``UPDATE``
39−----------
40−
41−To perform a ``UPDATE`` statement, construct a ``Update`` value and execute it
42−using ``update``. ``Update`` takes:
43−
44−``target``
45− The ``TableSchema`` for the table to update rows in.
46−
47−``from``
48− This is a simple ``Query`` that forms the ``FROM`` clause of the ``UPDATE``
49− statement. This can be used to join against other tables, and the results
50− can be referenced in the ``set`` and ``updateWhere`` parameters. For simple
51− ``UPDATE``\s where you don't need to do this, you can set ``from = pure ()``.
52−
53−``set``
54− A row to row transformation function, indicating how to update selected rows.
55− This function takes rows of the same shape as ``target`` but in the ``Expr``
56− context. One way to write this function is to use record update syntax::
57−
58− set = \from row -> row { rowName = "new name" }
59−
60−``updateWhere``
61− The ``WHERE`` clause of the ``UPDATE`` statement. This is a function that
62− takes two inputs: the result of the ``from`` query, and the current value of
63− the row.
64−
65−``returning``
66− What to return - see :ref:`returning`.
67−
68−``INSERT``
69−----------
70−
71−To perform a ``INSERT`` statement, construct a ``Insert`` value and execute it
72−using ``insert``. ``Insert`` takes:
73−
74−``into``
75− The ``TableSchema`` for the table to insert rows into.
76−
77−``rows``
78− The rows to insert. These are the same as ``into``, but in the ``Expr``
79− context. You can construct rows from their individual fields::
80−
81− rows = values [ MyTable { myTableA = lit "A", myTableB = lit 42 }
82−
83− or you can use ``lit`` on a table value in the ``Result`` context::
84−
85− rows = values [ lit MyTable { myTableA = "A", myTableB = 42 }
86−
87−``onConflict``
88− What should happen if an insert clashes with rows that already exist. This
89− corresponds to PostgreSQL's ``ON CONFLICT`` clause. You can specify:
90−
91− ``Abort``
92− PostgreSQL should abort the ``INSERT`` with an exception
93−
94− ``DoNothing``
95− PostgreSQL should not insert the duplicate rows.
96−
97− ``DoUpdate``
98− PostgreSQL should instead try to update any existing rows that conflict
99− with rows proposed for insertion.
100−
101−``returning``
102− What to return - see :ref:`returning`.
103−
104−.. _returning:
105−
106−``RETURNING``
107−-------------
108−
109−PostgreSQL has the ability to return extra information after a ``DELETE``,
110−``INSERT`` or ``UPDATE`` statement by attaching a ``RETURNING`` clause. A common
111−use of this clause is to return any automatically generated sequence values for
112−primary key columns. Rel8 supports ``RETURNING`` clauses by filling in the
113−``returning`` field and specifying a ``Projection``. A ``Projection`` is a row
114−to row transformation, allowing you to project out a subset of fields.
115−
116−For example, if we are inserting orders, we might want the order ids returned::
117−
118− insert Insert
119− { into = orderSchema
120− , rows = values [ order ]
121− , onConflict = Abort
122− , returning = Projection orderId
123− }
124−
125−If we don't want to return anything, we can use ``pure ()``::
126−
127− insert Insert
128− { into = orderSchema
129− , rows = values [ order ]
130− , onConflict = Abort
131− , returning = pure ()
132− }
133−
134−Default values
135−--------------
136−
137−It is fairly common to define tables with default values. While Rel8 does not
138−have specific functionality for ``DEFAULT``, there are a few options:
139−
140−``unsafeDefault``
141−~~~~~~~~~~~~~~~~~~~~~~~~~~~
142−
143−Rel8 does not have any special support for ``DEFAULT``. If you need to use
144−default column values in inserts, you can use ``unsafeDefault`` to
145−construct the ``DEFAULT`` expression::
146−
147− insert Insert
148− { into = orderSchema
149− , rows = values [ Order { orderId = unsafeDefault, ... } ]
150− , onConflict = Abort
151− , returning = Projection orderId
152− }
153−
154−.. warning::
155− As the name suggests, this is an unsafe operation. In particular, Rel8 is not
156− able to prove that this column does have a default value, so it may be
157− possible to introduce a runtime error. Furthermore, ``DEFAULT`` is fairly
158− special in SQL, and cannot be combined like other expressions. For example,
159− the innocuous expression::
160−
161− unsafeDefault + 1
162−
163− will lead to a runtime crash.
164−
165−Reimplement default values in Rel8
166−~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
167−
168−If you only need to access default values in Rel8, another option is to specify
169−them in Rel8, rather than in your database schema.
170−
171−.. hint::
172− A common default value for primary keys is to use `nextval` to obtain the
173− next value from a sequence. This can be done in Rel8 by using the ``nextval``
174− function::
175−
176− insert Insert
177− { into = orderSchema
178− , rows = values [ Order { orderId = nextval "order_id_seq", ... } ]
179− , onConflict = Abort
180− , returning = Projection orderId
181− }
docs/concepts/queries.rst+9−9
@@ -88,7 +88,7 @@ notation allows you to write these queries in a simpler form, where we don't
8888have to introduce functions. Expanding on the latter query, we could write::
8989
9090 do user <- getAllUsers
91− order <- getOrdersForUser user
91+ order <- getOrdersForUser (user)
9292 return (user, order)
9393
9494Now we have a query that, for each ``User``, fetches all orders for that user.
@@ -138,20 +138,20 @@ a query. The above query could thus be written as::
138138
139139``where_`` and ``filter`` allow you to filter rows based on an expression, but
140140sometimes we want to filter based on another query. For this, Rel8 offers
141−``present`` and ``absent``. For example, if all blog posts have a
142−list of tags, we could use ``present`` to find all blog posts that have been
141+``whereExists`` and ``whereNotExists``. For example, if all blog posts have a
142+list of tags, we could use ``whereExists`` to find all blog posts that have been
143143tagged as "Haskell"::
144144
145145 blogPost <- each blogPostSchema
146− present do
146+ whereExists do
147147 filter (("Haskell" ==.) . tagName) =<< tagFromBlogPost blogPost
148148
149−Notice that this example uses ``present`` with a query that itself uses
150−``filter``. For each blog post, ``present`` causes that row to be selected
149+Notice that this example uses ``whereExists`` with a query that itself uses
150+``filter``. For each blog post, ``whereExists`` causes that row to be selected
151151only if the associated query finds a tag for that blog post with the ``tagName``
152152"Haskell".
153153
154−Like ``filter`` there is also a chaining variant of ``present`` - ``with``.
154+Like ``filter`` there is also a chaining variant of ``whereExists`` - ``with``.
155155We could rewrite the above query using ``with`` as::
156156
157157 haskellBlogPost <-
@@ -194,11 +194,11 @@ and this query can be written in Rel8 as::
194194
195195 authorForBlogPost :: BlogPost Expr -> Query (Author Expr)
196196 authorForBlogPost blogPost =
197− filter ((blogPostAuthorId blogPost ==.) . authorId) =<<
197+ filter ((blogPostAuthorId blogPost ==.) authorId author) =<<
198198 each authorSchema
199199
200200 While this is a little more code over all, in our experience this style
201− dramatically increases the readability of queries using joins.
201+ dramatically increases the readabality of queries using joins.
202202
203203Left (outer) joins with ``optional``
204204------------------------------------
docs/cookbook.rst+8−10
@@ -72,7 +72,7 @@ can be written as::
7272 each myTableSchema
7373
7474Note that we use ``>$<`` (from ``Data.Functor.Contravariant``) to select table
75−columns, and we can use ``mconcat`` to combine orderings.
75+columns, and we can use `mconcat` to combine orderings.
7676
7777If all columns of a table have an ordering, you can also use ``ascTable`` and
7878``descTable``. For example::
@@ -142,25 +142,23 @@ rows, but can also return *tree*\s.
142142To understand what this means, we'll consider a small example query for blog
143143posts. We want our query to return:
144144
145−1. The latest 5 blog posts that have at least one tag each.
145+1. The latest 5 blog posts.
1461462. For each blog post, all tags.
147−3. For each blog post, the latest 3 comments if they exist.
147+3. For each blog post, the latest 3 comments.
148148
149149In Rel8, we can write this query as::
150150
151151 latestBlogPosts = do
152152 post <- each postSchema
153153
154− -- Returns a `NonEmptyTable a` which ends up as a `Data.List.NonEmpty a` after the query is run
155− tags <- some $ do
154+ tags <- aggregate do
156155 tag <- each tagSchema
157156 where_ (tagPostId tag ==. postId post)
158− return (tagName tag)
157+ return (listAgg (tagName tag))
159158
160− -- Returns a `ListTable a` which ends up as a `[a]` after the query is run
161− latestComments <-
162− many $
163− limit 3 $
159+ latestComments <-
160+ many $
161+ limit 3 $
164162 orderBy (commentCreatedAt >$< desc) do
165163 comment <- each commentSchema
166164 where_ (commentPostId comment ==. postId post)
docs/index.rst+1−2
@@ -30,7 +30,6 @@ The main objectives of Rel8 are:
3030 concepts/expr
3131 concepts/tables
3232 concepts/queries
33− concepts/insert
3433
3534.. toctree::
3635 :caption: Tips and tricks
@@ -50,4 +49,4 @@ More Resources
5049
5150* If you think you've found a bug, confusing behavior, or have a feature
5251 request, please raise an issue at `Rel8's issue tracker
53− <https://github.com/circuithub/rel8>`_.
52+ <https://github.com/circuithub/rel8>`_.
docs/tutorial.rst+31−48
@@ -10,20 +10,19 @@ tables to Haskell, and then look at writing some simple queries.
1010Before we get started, we'll be using the following language extensions and
1111imports throughout this guide::
1212
13− {-# language BlockArguments #-}
14− {-# language DeriveAnyClass #-}
15− {-# language DeriveGeneric #-}
16− {-# language DerivingStrategies #-}
17− {-# language DerivingVia #-}
18− {-# language DuplicateRecordFields #-}
19− {-# language GeneralizedNewtypeDeriving #-}
20− {-# language OverloadedStrings #-}
21− {-# language StandaloneDeriving #-}
22− {-# language TypeApplications #-}
23− {-# language TypeFamilies #-}
13+ {-# language -XBlockArguments #-}
14+ {-# language -XDeriveAnyClass #-}
15+ {-# language -XDeriveGeneric #-}
16+ {-# language -XDerivingStrategies #-}
17+ {-# language -XDerivingVia #-}
18+ {-# language -XDuplicateRecordFields #-}
19+ {-# language -XGeneralizedNewtypeDeriving #-}
20+ {-# language -XOverloadedStrings #-}
21+ {-# language -XStandaloneDeriving #-}
22+ {-# language -XTypeApplications #-}
23+ {-# language -XTypeFamilies #-}
2424
2525 import Prelude
26− import Rel8
2726
2827The Example Schema
2928------------------
@@ -126,9 +125,9 @@ associated with the ``Author`` type::
126125
127126 authorSchema :: TableSchema (Author Name)
128127 authorSchema = TableSchema
129− { name = "author"
130− , schema = Nothing
131− , columns = Author
128+ { tableName = "author"
129+ , tableSchema = Nothing
130+ , tableColumns = Author
132131 { authorId = "author_id"
133132 , authorName = "name"
134133 , authorUrl = "url"
@@ -139,27 +138,14 @@ And likewise for ``project`` and ``Project``::
139138
140139 projectSchema :: TableSchema (Project Name)
141140 projectSchema = TableSchema
142− { name = "project"
143− , schema = Nothing
144− , columns = Project
141+ { tableName = "project"
142+ , tableSchema = Nothing
143+ , tableColumns = Project
145144 { projectAuthorId = "author_id"
146145 , projectName = "name"
147146 }
148147 }
149148
150−There is also some generics machinery available if you want to grab the field
151−information from your ``Rel8able`` type::
152−
153− projectSchema :: TableSchema (Project Name)
154− projectSchema = TableSchema
155− { name = "project"
156− , schema = Nothing
157− , columns = namesFromLabels @(Project Name)
158− }
159−
160−This will create a ``TableSchema`` for ``Project`` where every column name
161−corresponds exactly to the name of the field. If you need more flexibility, you
162−can use ``namesFromLabelsWith``, which takes a transformation function.
163149
164150.. note::
165151
@@ -204,15 +190,12 @@ Recall we defined ``projectAuthorId`` as ``Column f AuthorId``. Now we have
204190We'll see more about ``Expr`` soon, but you can think of ``Expr a`` as "SQL
205191expressions of type ``a``\".
206192
207−To execute this ``Query``, we pass it to ``select``. This function takes both a
208−database connection (which can be obtained using hasql's ``acquire`` function),
209−and the ``Query`` to run::
193+To execute this ``Query``, we pass it to ``select``::
210194
211− >>> Right conn <- acquire "user=postgres"
212− >>> :t select conn (each projectSchema)
213− select conn (each projectSchema) :: MonadIO m => m [Project Result]
195+ >>> :t select c (each projectSchema)
196+ select c (each projectSchema) :: MonadIO m => m [Project Result]
214197
215−When we ``select`` things containing ``Expr``\s, Rel8 builds a new response
198+When we ``select`` things containing ``Expr``s, Rel8 builds a new response
216199table with the ``Result`` interpretation. This means you'll get back plain
217200Haskell values. Studying ``projectAuthorId`` again, we have::
218201
@@ -225,7 +208,7 @@ wrappping type at all.
225208
226209Putting this all together, we can run our first query::
227210
228− >>> select conn (each projectSchema) >>= mapM_ print
211+ >>> select c (each projectSchema) >>= mapM_ print
229212 Project {projectAuthorId = 1, projectName = "rel8"}
230213 Project {projectAuthorId = 2, projectName = "aeson"}
231214 Project {projectAuthorId = 2, projectName = "text"}
@@ -235,7 +218,7 @@ sometimes we're only interested in a subset of the columns of a table. To
235218restrict the returned columns, we can specify a projection by using ``Query``\s
236219``Functor`` instance::
237220
238− >>> select conn $ projectName <$> each projectSchema
221+ >>> select c $ projectName <$> each projectSchema
239222 ["rel8","aeson","text"]
240223
241224Joins
@@ -251,7 +234,7 @@ We can do this by simply calling ``each`` twice, and then returning a tuple of
251234their results::
252235
253236 >>> :{
254− mapM_ print =<< select conn do
237+ mapM_ print =<< select c do
255238 author <- each authorSchema
256239 project <- each projectSchema
257240 return (projectName project, authorName author)
@@ -270,7 +253,7 @@ This isn't quite right, though, as we have ended up pairing up the wrong
270253projects and authors. To fix this, we can use ``where_`` to restrict the
271254returned rows. We could write::
272255
273− select conn $ do
256+ select c $ do
274257 author <- each authorSchema
275258 project <- each projectSchema
276259 where_ $ projectAuthorId project ==. authorId author
@@ -287,7 +270,7 @@ for the particular joins in your database. In our case, this would be::
287270Our final query is then::
288271
289272 >>> :{
290− mapM_ print =<< select conn do
273+ mapM_ print =<< select c do
291274 author <- each authorSchema
292275 project <- projectsForAuthor author
293276 return (projectName project, authorName author)
@@ -306,7 +289,7 @@ transformer to allow for the possibility of the join to fail.
306289In our test database, we can see that there's another author who we haven't
307290seen yet::
308291
309− >>> select conn $ authorName <$> each authorSchema
292+ >>> select c $ authorName <$> each authorSchema
310293 ["Ollie","Bryan O'Sullivan","Emily Pillmore"]
311294
312295Emily wasn't returned in our earlier query because - in our database - she
@@ -314,7 +297,7 @@ doesn't have any registered projects. We can account for this partiality in our
314297original query by wrapping the ``projectsForAuthor`` call with ``optional``::
315298
316299 >>> :{
317− mapM_ print =<< select conn do
300+ mapM_ print =<< select c do
318301 author <- each authorSchema
319302 mproject <- optional $ projectsForAuthor author
320303 return (authorName author, projectName <$> mproject)
@@ -359,8 +342,8 @@ focus on the /type/ of that query::
359342 return (author, project)
360343 where
361344
362− >>> :t select conn authorsAndProjects
363− select conn authorsAndProjects
345+ >>> :t select c authorsAndProjects
346+ select c authorsAndProjects
364347 :: MonadIO m => m [(Author Result, Project Result)]
365348
366349
@@ -377,7 +360,7 @@ simply wrapping the call to ``projectsForAuthor`` with either ``some`` or
377360author to have no projects::
378361
379362 >>> :{
380− mapM_ print =<< select conn do
363+ mapM_ print =<< select c do
381364 author <- each authorSchema
382365 projectNames <- many $ projectName <$> projectsForAuthor author
383366 return (authorName author, projectNames)
nix/sources.json+9−9
@@ -5,10 +5,10 @@
55 "homepage": "https://input-output-hk.github.io/haskell.nix",
66 "owner": "input-output-hk",
77 "repo": "haskell.nix",
8− "rev": "0837156c56cb5350dd9bd84eb424187e00aa1e8a",
9− "sha256": "1djmv5a0vfj78csw2cad6nicrs8iqrp928b9b3v4h7lwwhhzliwl",
8+ "rev": "5b5a18e442df6e27ad8549d69b8868b62adb08c3",
9+ "sha256": "055mk7kmjppng66ppnb8kr96v4wwc8pcxfsls5gl77129hsz9aph",
1010 "type": "tarball",
11− "url": "https://github.com/input-output-hk/haskell.nix/archive/0837156c56cb5350dd9bd84eb424187e00aa1e8a.tar.gz",
11+ "url": "https://github.com/input-output-hk/haskell.nix/archive/5b5a18e442df6e27ad8549d69b8868b62adb08c3.tar.gz",
1212 "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
1313 },
1414 "niv": {
@@ -17,10 +17,10 @@
1717 "homepage": "https://github.com/nmattia/niv",
1818 "owner": "nmattia",
1919 "repo": "niv",
20− "rev": "acdb1726783637852c01e0ad639ac9cbec28d8c8",
21− "sha256": "1wszl8q70z6wfgkh2dvgmhhry6qp9nar421qq2ifd17hq2jj9ck3",
20+ "rev": "af958e8057f345ee1aca714c1247ef3ba1c15f5e",
21+ "sha256": "1qjavxabbrsh73yck5dcq8jggvh3r2jkbr6b5nlz5d9yrqm9255n",
2222 "type": "tarball",
23− "url": "https://github.com/nmattia/niv/archive/acdb1726783637852c01e0ad639ac9cbec28d8c8.tar.gz",
23+ "url": "https://github.com/nmattia/niv/archive/af958e8057f345ee1aca714c1247ef3ba1c15f5e.tar.gz",
2424 "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
2525 },
2626 "nixpkgs": {
@@ -29,10 +29,10 @@
2929 "homepage": null,
3030 "owner": "nixos",
3131 "repo": "nixpkgs",
32− "rev": "1c1f5649bb9c1b0d98637c8c365228f57126f361",
33− "sha256": "0f2nvdijyxfgl5kwyb4465pppd5vkhqxddx6v40k2s0z9jfhj0xl",
32+ "rev": "a6847cb5460b47241eb35d1a6588652411642066",
33+ "sha256": "0wb5hwgffr4in66qng22sfvbascwnq078q81ziwwayvmnkn73rrm",
3434 "type": "tarball",
35− "url": "https://github.com/nixos/nixpkgs/archive/1c1f5649bb9c1b0d98637c8c365228f57126f361.tar.gz",
35+ "url": "https://github.com/nixos/nixpkgs/archive/a6847cb5460b47241eb35d1a6588652411642066.tar.gz",
3636 "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
3737 }
3838}
nix/sources.nix+20−66
@@ -6,53 +6,25 @@ let
66 # The fetchers. fetch_<type> fetches specs of type <type>.
77 #
88
9− fetch_file = pkgs: name: spec:
10− let
11− name' = sanitizeName name + "-src";
12− in
13− if spec.builtin or true then
14− builtins_fetchurl { inherit (spec) url sha256; name = name'; }
15− else
16− pkgs.fetchurl { inherit (spec) url sha256; name = name'; };
9+ fetch_file = pkgs: spec:
10+ if spec.builtin or true then
11+ builtins_fetchurl { inherit (spec) url sha256; }
12+ else
13+ pkgs.fetchurl { inherit (spec) url sha256; };
1714
1815 fetch_tarball = pkgs: name: spec:
1916 let
20− name' = sanitizeName name + "-src";
17+ ok = str: ! builtins.isNull (builtins.match "[a-zA-Z0-9+-._?=]" str);
18+ # sanitize the name, though nix will still fail if name starts with period
19+ name' = stringAsChars (x: if ! ok x then "-" else x) "${name}-src";
2120 in
2221 if spec.builtin or true then
2322 builtins_fetchTarball { name = name'; inherit (spec) url sha256; }
2423 else
2524 pkgs.fetchzip { name = name'; inherit (spec) url sha256; };
2625
27− fetch_git = name: spec:
28− let
29− ref =
30− if spec ? ref then spec.ref else
31− if spec ? branch then "refs/heads/${spec.branch}" else
32− if spec ? tag then "refs/tags/${spec.tag}" else
33− abort "In git source '${name}': Please specify `ref`, `tag` or `branch`!";
34− submodules = if spec ? submodules then spec.submodules else false;
35− submoduleArg =
36− let
37− nixSupportsSubmodules = builtins.compareVersions builtins.nixVersion "2.4" >= 0;
38− emptyArgWithWarning =
39− if submodules == true
40− then
41− builtins.trace
42− (
43− "The niv input \"${name}\" uses submodules "
44− + "but your nix's (${builtins.nixVersion}) builtins.fetchGit "
45− + "does not support them"
46− )
47− {}
48− else {};
49− in
50− if nixSupportsSubmodules
51− then { inherit submodules; }
52− else emptyArgWithWarning;
53− in
54− builtins.fetchGit
55− ({ url = spec.repo; inherit (spec) rev; inherit ref; } // submoduleArg);
26+ fetch_git = spec:
27+ builtins.fetchGit { url = spec.repo; inherit (spec) rev ref; };
5628
5729 fetch_local = spec: spec.path;
5830
@@ -68,21 +40,11 @@ let
6840 # Various helpers
6941 #
7042
71− # https://github.com/NixOS/nixpkgs/pull/83241/files#diff-c6f540a4f3bfa4b0e8b6bafd4cd54e8bR695
72− sanitizeName = name:
73− (
74− concatMapStrings (s: if builtins.isList s then "-" else s)
75− (
76− builtins.split "[^[:alnum:]+._?=-]+"
77− ((x: builtins.elemAt (builtins.match "\\.*(.*)" x) 0) name)
78− )
79− );
80−
8143 # The set of packages used when specs are fetched using non-builtins.
82− mkPkgs = sources: system:
44+ mkPkgs = sources:
8345 let
8446 sourcesNixpkgs =
85− import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) { inherit system; };
47+ import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) {};
8648 hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath;
8749 hasThisAsNixpkgsPath = <nixpkgs> == ./.;
8850 in
@@ -102,9 +64,9 @@ let
10264
10365 if ! builtins.hasAttr "type" spec then
10466 abort "ERROR: niv spec ${name} does not have a 'type' attribute"
105− else if spec.type == "file" then fetch_file pkgs name spec
67+ else if spec.type == "file" then fetch_file pkgs spec
10668 else if spec.type == "tarball" then fetch_tarball pkgs name spec
107− else if spec.type == "git" then fetch_git name spec
69+ else if spec.type == "git" then fetch_git spec
10870 else if spec.type == "local" then fetch_local spec
10971 else if spec.type == "builtin-tarball" then fetch_builtin-tarball name
11072 else if spec.type == "builtin-url" then fetch_builtin-url name
@@ -118,10 +80,7 @@ let
11880 saneName = stringAsChars (c: if isNull (builtins.match "[a-zA-Z0-9]" c) then "_" else c) name;
11981 ersatz = builtins.getEnv "NIV_OVERRIDE_${saneName}";
12082 in
121− if ersatz == "" then drv else
122− # this turns the string into an actual Nix path (for both absolute and
123− # relative paths)
124− if builtins.substring 0 1 ersatz == "/" then /. + ersatz else /. + builtins.getEnv "PWD" + "/${ersatz}";
83+ if ersatz == "" then drv else ersatz;
12584
12685 # Ports of functions for older nix versions
12786
@@ -139,29 +98,25 @@ let
13998
14099 # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269
141100 stringAsChars = f: s: concatStrings (map f (stringToCharacters s));
142− concatMapStrings = f: list: concatStrings (map f list);
143101 concatStrings = builtins.concatStringsSep "";
144102
145− # https://github.com/NixOS/nixpkgs/blob/8a9f58a375c401b96da862d969f66429def1d118/lib/attrsets.nix#L331
146− optionalAttrs = cond: as: if cond then as else {};
147−
148103 # fetchTarball version that is compatible between all the versions of Nix
149− builtins_fetchTarball = { url, name ? null, sha256 }@attrs:
104+ builtins_fetchTarball = { url, name, sha256 }@attrs:
150105 let
151106 inherit (builtins) lessThan nixVersion fetchTarball;
152107 in
153108 if lessThan nixVersion "1.12" then
154− fetchTarball ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; }))
109+ fetchTarball { inherit name url; }
155110 else
156111 fetchTarball attrs;
157112
158113 # fetchurl version that is compatible between all the versions of Nix
159− builtins_fetchurl = { url, name ? null, sha256 }@attrs:
114+ builtins_fetchurl = { url, sha256 }@attrs:
160115 let
161116 inherit (builtins) lessThan nixVersion fetchurl;
162117 in
163118 if lessThan nixVersion "1.12" then
164− fetchurl ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; }))
119+ fetchurl { inherit url; }
165120 else
166121 fetchurl attrs;
167122
@@ -180,8 +135,7 @@ let
180135 mkConfig =
181136 { sourcesFile ? if builtins.pathExists ./sources.json then ./sources.json else null
182137 , sources ? if isNull sourcesFile then {} else builtins.fromJSON (builtins.readFile sourcesFile)
183− , system ? builtins.currentSystem
184− , pkgs ? mkPkgs sources system
138+ , pkgs ? mkPkgs sources
185139 }: rec {
186140 # The sources, i.e. the attribute set of spec name to spec
187141 inherit sources;
rel8.cabal+18−45
@@ -1,52 +1,41 @@
11cabal-version: 2.0
22name: rel8
3−version: 1.4.0.0
3+version: 1.0.0.0
44synopsis: Hey! Hey! Can u rel8?
55license: BSD3
66license-file: LICENSE
77author: Oliver Charles
88maintainer: ollie@ocharles.org.uk
9−homepage: https://github.com/circuithub/rel8
10−bug-reports: https://github.com/circuithub/rel8/issues
119build-type: Simple
1210extra-doc-files:
1311 README.md
1412 Changelog.md
1513
16−source-repository head
17− type: git
18− location: https://github.com/circuithub/rel8
19−
2014library
2115 build-depends:
2216 aeson
23− , base ^>= 4.14 || ^>=4.15 || ^>=4.16
24− , bifunctors
17+ , base ^>= 4.14 || ^>=4.15
2518 , bytestring
2619 , case-insensitive
27− , comonad
2820 , contravariant
29− , hasql ^>= 1.4.5.1 || ^>= 1.5.0.0 || ^>= 1.6.0.0
30− , opaleye ^>= 0.9.6.1
31− , pretty
21+ , hasql ^>= 1.4.5.1
22+ , opaleye ^>= 0.7.1.0
3223 , profunctors
33− , product-profunctors
3424 , scientific
3525 , semialign
3626 , semigroupoids
3727 , text
3828 , these
3929 , time
30+ , transformers
4031 , uuid
4132 default-language:
4233 Haskell2010
4334 ghc-options:
44− -Werror=missing-methods -Werror=incomplete-patterns -Werror=missing-fields
4535 -Weverything -Wno-unsafe -Wno-safe -Wno-missing-safe-haskell-mode
4636 -Wno-missing-import-lists -Wno-prepositive-qualified-module
4737 -Wno-monomorphism-restriction
4838 -Wno-missing-local-signatures
49− -Wno-missing-kind-signatures
5039 hs-source-dirs:
5140 src
5241 exposed-modules:
@@ -54,7 +43,6 @@ library
5443 Rel8.Expr.Num
5544 Rel8.Expr.Text
5645 Rel8.Expr.Time
57− Rel8.Tabulate
5846
5947 other-modules:
6048 Rel8.Aggregate
@@ -66,14 +54,12 @@ library
6654 Rel8.Column.List
6755 Rel8.Column.Maybe
6856 Rel8.Column.NonEmpty
69− Rel8.Column.Null
7057 Rel8.Column.These
7158
7259 Rel8.Expr
7360 Rel8.Expr.Aggregate
7461 Rel8.Expr.Array
7562 Rel8.Expr.Bool
76− Rel8.Expr.Default
7763 Rel8.Expr.Eq
7864 Rel8.Expr.Function
7965 Rel8.Expr.Null
@@ -82,19 +68,21 @@ library
8268 Rel8.Expr.Order
8369 Rel8.Expr.Sequence
8470 Rel8.Expr.Serialize
85− Rel8.Expr.Window
8671
8772 Rel8.FCF
8873
8974 Rel8.Kind.Algebra
9075 Rel8.Kind.Context
76+ Rel8.Kind.Labels
9177
9278 Rel8.Generic.Construction
9379 Rel8.Generic.Construction.ADT
9480 Rel8.Generic.Construction.Record
9581 Rel8.Generic.Map
9682 Rel8.Generic.Record
83+ Rel8.Generic.Reify
9784 Rel8.Generic.Rel8able
85+ Rel8.Generic.Rel8able.Test
9886 Rel8.Generic.Table
9987 Rel8.Generic.Table.ADT
10088 Rel8.Generic.Table.Record
@@ -109,23 +97,21 @@ library
10997 Rel8.Query.Evaluate
11098 Rel8.Query.Exists
11199 Rel8.Query.Filter
112− Rel8.Query.Indexed
113100 Rel8.Query.Limit
114101 Rel8.Query.List
115102 Rel8.Query.Maybe
116103 Rel8.Query.Null
117104 Rel8.Query.Opaleye
118105 Rel8.Query.Order
119− Rel8.Query.Rebind
120106 Rel8.Query.Set
121107 Rel8.Query.SQL
122108 Rel8.Query.These
123109 Rel8.Query.Values
124− Rel8.Query.Window
125110
111+ Rel8.Schema.Context
112+ Rel8.Schema.Context.Label
126113 Rel8.Schema.Context.Nullify
127114 Rel8.Schema.Dict
128− Rel8.Schema.Field
129115 Rel8.Schema.HTable
130116 Rel8.Schema.HTable.Either
131117 Rel8.Schema.HTable.Identity
@@ -141,28 +127,24 @@ library
141127 Rel8.Schema.Kind
142128 Rel8.Schema.Name
143129 Rel8.Schema.Null
130+ Rel8.Schema.Reify
144131 Rel8.Schema.Result
145132 Rel8.Schema.Spec
133+ Rel8.Schema.Spec.ConstrainDBType
146134 Rel8.Schema.Table
147135
148136 Rel8.Statement.Delete
149137 Rel8.Statement.Insert
150− Rel8.Statement.OnConflict
151138 Rel8.Statement.Returning
152139 Rel8.Statement.Select
153− Rel8.Statement.Set
154− Rel8.Statement.SQL
155140 Rel8.Statement.Update
156− Rel8.Statement.Using
157141 Rel8.Statement.View
158− Rel8.Statement.Where
159142
160143 Rel8.Table
161144 Rel8.Table.ADT
162145 Rel8.Table.Aggregate
163146 Rel8.Table.Alternative
164147 Rel8.Table.Bool
165− Rel8.Table.Cols
166148 Rel8.Table.Either
167149 Rel8.Table.Eq
168150 Rel8.Table.HKD
@@ -170,18 +152,16 @@ library
170152 Rel8.Table.Maybe
171153 Rel8.Table.Name
172154 Rel8.Table.NonEmpty
173− Rel8.Table.Null
174− Rel8.Table.Nullify
175155 Rel8.Table.Opaleye
176156 Rel8.Table.Ord
177157 Rel8.Table.Order
178− Rel8.Table.Projection
158+ Rel8.Table.Recontextualize
179159 Rel8.Table.Rel8able
180160 Rel8.Table.Serialize
161+ Rel8.Table.Tag
181162 Rel8.Table.These
182− Rel8.Table.Transpose
183163 Rel8.Table.Undefined
184− Rel8.Table.Window
164+ Rel8.Table.Unreify
185165
186166 Rel8.Type
187167 Rel8.Type.Array
@@ -200,9 +180,6 @@ library
200180 Rel8.Type.Sum
201181 Rel8.Type.Tag
202182
203− Rel8.Window
204−
205−
206183test-suite tests
207184 type: exitcode-stdio-1.0
208185 build-depends:
@@ -211,9 +188,9 @@ test-suite tests
211188 , case-insensitive
212189 , containers
213190 , hasql
214− , hasql-transaction
215− , hedgehog ^>= 1.0 || ^>= 1.1
216− , mmorph
191+ , hedgehog ^>=1.0.2
192+ , lifted-base ^>=0.2.3.12
193+ , monad-control ^>=1.0.2.3
217194 , rel8
218195 , scientific
219196 , tasty
@@ -221,12 +198,8 @@ test-suite tests
221198 , text
222199 , time
223200 , tmp-postgres ^>=1.34.1.0
224− , transformers
225201 , uuid
226202
227− other-modules:
228− Rel8.Generic.Rel8able.Test
229−
230203 main-is: Main.hs
231204 hs-source-dirs: tests
232205 default-language: Haskell2010
shell.nix+1−1
@@ -1,6 +1,6 @@
11let
22 sources = import ./nix/sources.nix;
3− pkgs = import sources.nixpkgs {overlays = [];};
3+ pkgs = import sources.nixpkgs {};
44 hsPkgs = import ./default.nix;
55in
66 hsPkgs.shellFor {
src/Rel8.hs+23−315
@@ -38,31 +38,27 @@ module Rel8
3838 , HMaybe
3939 , HList
4040 , HNonEmpty
41− , HNull
4241 , HThese
4342 , Lift
4443
4544 , Table(..)
4645 , HTable
47− , Transposes
46+ , Recontextualize
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
65− , aggregateMaybeTable
6662 , nameMaybeTable
6763
6864 -- ** @EitherTable@
@@ -72,7 +68,6 @@ module Rel8
7268 , keepLeftTable
7369 , keepRightTable
7470 , bitraverseEitherTable
75− , aggregateEitherTable
7671 , nameEitherTable
7772
7873 -- ** @TheseTable@
@@ -81,7 +76,6 @@ module Rel8
8176 , isThisTable, isThatTable, isThoseTable
8277 , hasHereTable, hasThereTable
8378 , justHereTable, justThereTable
84− , alignMaybeTable
8579 , alignBy
8680 , keepHereTable, loseHereTable
8781 , keepThereTable, loseThereTable
@@ -89,12 +83,11 @@ module Rel8
8983 , keepThatTable, loseThatTable
9084 , keepThoseTable, loseThoseTable
9185 , bitraverseTheseTable
92− , aggregateTheseTable
9386 , nameTheseTable
9487
9588 -- ** @ListTable@
9689 , ListTable
97− , listTable, ($*)
90+ , listTable
9891 , nameListTable
9992 , many
10093 , manyExpr
@@ -103,44 +96,21 @@ module Rel8
10396
10497 -- ** @NonEmptyTable@
10598 , NonEmptyTable
106− , nonEmptyTable, ($+)
99+ , nonEmptyTable
107100 , nameNonEmptyTable
108101 , some
109102 , someExpr
110103 , catNonEmptyTable
111104 , catNonEmpty
112105
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
106+ -- ** @ADT@
127107 , ADT, ADTable
128−
129− -- *** Deconstruction of ADTs
130− -- $deconstruction
131− , DeconstructADT, deconstructADT
132−
133− -- *** Construction of ADTs
134− -- $construction
135108 , BuildADT, buildADT
136109 , ConstructADT, constructADT
137−
138− -- *** Other ADT operations
110+ , DeconstructADT, deconstructADT
111+ , NameADT, nameADT
139112 , AggregateADT, aggregateADT
140113
141− -- *** Miscellaneous notes
142− -- $misc-notes
143−
144114 -- ** @HKD@
145115 , HKD, HKDable
146116 , BuildHKD, buildHKD
@@ -165,7 +135,6 @@ module Rel8
165135 -- ** @null@
166136 , NotNull
167137 , Nullable
168− , Homonullable
169138 , null
170139 , nullify
171140 , nullable
@@ -184,7 +153,6 @@ module Rel8
184153 , (==.), (/=.), (==?), (/=?)
185154 , in_
186155 , boolExpr, caseExpr
187− , like, ilike
188156
189157 -- ** Ordering
190158 , DBOrd
@@ -202,13 +170,6 @@ module Rel8
202170 , Query
203171 , showQuery
204172
205− -- ** Projection
206− , Projection
207− , Projectable( project )
208− , Biprojectable( biproject )
209− , Projecting
210− , Field
211−
212173 -- ** Selecting rows
213174 , Selects
214175 , each
@@ -217,8 +178,8 @@ module Rel8
217178 -- ** Filtering
218179 , filter
219180 , where_
220− , present
221− , absent
181+ , whereExists
182+ , whereNotExists
222183 , distinct
223184 , distinctOn
224185 , distinctOnBy
@@ -253,11 +214,10 @@ module Rel8
253214 , countRows
254215 , groupBy
255216 , listAgg, listAggExpr
256− , mode
257217 , nonEmptyAgg, nonEmptyAggExpr
258218 , DBMax, max
259219 , DBMin, min
260− , DBSum, sum, sumWhere, avg
220+ , DBSum, sum, sumWhere
261221 , DBString, stringAgg
262222 , count
263223 , countStar
@@ -274,71 +234,45 @@ module Rel8
274234 , nullsFirst
275235 , nullsLast
276236
277− -- ** 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
298− , indexed
299−
300− -- ** Bindings
301− , rebind
302−
303237 -- * IO
304238 , Serializable
305− , ToExprs
239+ , ToExprs(..)
240+ , FromExprs
306241 , Result
307242
308243 -- * Running statements
309− -- $running
310−
311244 -- ** @SELECT@
312245 , select
313246
314247 -- ** @INSERT@
315248 , Insert(..)
316249 , OnConflict(..)
317− , Upsert(..)
318250 , insert
319− , unsafeDefault
320− , showInsert
321251
322252 -- ** @DELETE@
323253 , Delete(..)
324254 , delete
325− , showDelete
326255
327256 -- ** @UPDATE@
328− , Update(..)
329257 , update
330− , showUpdate
258+ , Update(..)
331259
332260 -- ** @.. RETURNING@
333261 , Returning(..)
334262
335263 -- ** @CREATE VIEW@
336264 , createView
337− , createOrReplaceView
338265
339266 -- ** Sequences
340267 , nextval
268+
269+ , Evaluate
270+ , eval
341271 , evaluate
272+
273+ -- * Implementation details
274+ , Labelable
275+ , HKDT(..)
342276 ) where
343277
344278-- base
@@ -353,12 +287,10 @@ import Rel8.Column.Lift
353287import Rel8.Column.List
354288import Rel8.Column.Maybe
355289import Rel8.Column.NonEmpty
356−import Rel8.Column.Null
357290import Rel8.Column.These
358291import Rel8.Expr
359292import Rel8.Expr.Aggregate
360293import Rel8.Expr.Bool
361−import Rel8.Expr.Default
362294import Rel8.Expr.Eq
363295import Rel8.Expr.Function
364296import Rel8.Expr.Null
@@ -367,8 +299,6 @@ import Rel8.Expr.Ord
367299import Rel8.Expr.Order
368300import Rel8.Expr.Serialize
369301import Rel8.Expr.Sequence
370−import Rel8.Expr.Text ( like, ilike )
371−import Rel8.Expr.Window hiding ( cumulative )
372302import Rel8.Generic.Rel8able ( KRel8able, Rel8able )
373303import Rel8.Order
374304import Rel8.Query
@@ -379,19 +309,16 @@ import Rel8.Query.Either
379309import Rel8.Query.Evaluate
380310import Rel8.Query.Exists
381311import Rel8.Query.Filter
382−import Rel8.Query.Indexed
383312import Rel8.Query.Limit
384313import Rel8.Query.List
385314import Rel8.Query.Maybe
386315import Rel8.Query.Null
387316import Rel8.Query.Order
388−import Rel8.Query.Rebind
389317import Rel8.Query.SQL (showQuery)
390318import Rel8.Query.Set
391319import Rel8.Query.These
392320import Rel8.Query.Values
393−import Rel8.Query.Window
394−import Rel8.Schema.Field
321+import Rel8.Schema.Context.Label
395322import Rel8.Schema.HTable
396323import Rel8.Schema.Name
397324import Rel8.Schema.Null hiding ( nullable )
@@ -399,10 +326,8 @@ import Rel8.Schema.Result ( Result )
399326import Rel8.Schema.Table
400327import Rel8.Statement.Delete
401328import Rel8.Statement.Insert
402−import Rel8.Statement.OnConflict
403329import Rel8.Statement.Returning
404330import Rel8.Statement.Select
405−import Rel8.Statement.SQL
406331import Rel8.Statement.Update
407332import Rel8.Statement.View
408333import Rel8.Table
@@ -417,16 +342,12 @@ import Rel8.Table.List
417342import Rel8.Table.Maybe
418343import Rel8.Table.Name
419344import Rel8.Table.NonEmpty
420−import Rel8.Table.Null
421−import Rel8.Table.Opaleye ( castTable )
422345import Rel8.Table.Ord
423346import Rel8.Table.Order
424−import Rel8.Table.Projection
347+import Rel8.Table.Recontextualize
425348import Rel8.Table.Rel8able ()
426349import Rel8.Table.Serialize
427350import Rel8.Table.These
428−import Rel8.Table.Transpose
429−import Rel8.Table.Window
430351import Rel8.Type
431352import Rel8.Type.Composite
432353import Rel8.Type.Eq
@@ -441,216 +362,3 @@ import Rel8.Type.ReadShow
441362import Rel8.Type.Semigroup
442363import Rel8.Type.String
443364import Rel8.Type.Sum
444−import Rel8.Window
445−
446−
447−-- $running
448−-- To run queries and otherwise interact with a PostgreSQL database, Rel8
449−-- provides 'select', 'insert', 'update' and 'delete' functions. Note that
450−-- 'insert', 'update' and 'delete' will generally need the
451−-- `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.
src/Rel8/Aggregate.hs+109−37
@@ -1,83 +1,155 @@
11{-# language DataKinds #-}
2+{-# language DerivingVia #-}
23{-# language FlexibleContexts #-}
34{-# language FlexibleInstances #-}
5+{-# language GADTs #-}
46{-# language MultiParamTypeClasses #-}
5−{-# language RankNTypes #-}
7+{-# language NamedFieldPuns #-}
8+{-# language PolyKinds #-}
69{-# language StandaloneKindSignatures #-}
710{-# language TypeFamilies #-}
811{-# language UndecidableInstances #-}
12+{-# language UndecidableSuperClasses #-}
913
1014module Rel8.Aggregate
11− ( Aggregate(..), zipOutputs
12− , unsafeMakeAggregate
15+ ( Aggregate(..), foldInputs, mapInputs
16+ , Aggregator(..), unsafeMakeAggregate
1317 , Aggregates
18+ , Col( A, unA )
1419 )
1520where
1621
1722-- base
18−import Control.Applicative ( liftA2 )
19−import Data.Functor.Identity ( Identity( Identity ) )
23+import Data.Functor.Const ( Const( Const ), getConst )
24+import Data.Functor.Identity ( Identity )
2025import Data.Kind ( Constraint, Type )
2126import Prelude
2227
23−-- profunctors
24−import Data.Profunctor ( dimap )
25−
2628-- opaleye
27−import qualified Opaleye.Aggregate as Opaleye
29+import qualified Opaleye.Internal.Aggregate as Opaleye
2830import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
29−import qualified Opaleye.Internal.Column as Opaleye
31+import qualified Opaleye.Internal.PackMap as Opaleye
3032
3133-- rel8
3234import Rel8.Expr ( Expr )
33−import Rel8.Schema.HTable.Identity ( HIdentity(..) )
34−import qualified Rel8.Schema.Kind as K
35+import Rel8.Schema.Context ( Interpretation(..) )
36+import Rel8.Schema.Context.Label ( Labelable(..) )
37+import Rel8.Schema.HTable.Identity ( HIdentity(..), HType )
38+import Rel8.Schema.Name ( Name )
3539import Rel8.Schema.Null ( Sql )
40+import Rel8.Schema.Reify ( notReify )
41+import Rel8.Schema.Result ( Result )
42+import Rel8.Schema.Spec ( Spec( Spec ) )
3643import Rel8.Table
3744 ( Table, Columns, Context, fromColumns, toColumns
38− , FromExprs, fromResult, toResult
39− , Transpose
45+ , reify, unreify
4046 )
41−import Rel8.Table.Transpose ( Transposes )
47+import Rel8.Table.Recontextualize ( Recontextualize )
4248import Rel8.Type ( DBType )
4349
4450
45−-- | 'Aggregate' is a special context used by 'Rel8.aggregate'.
46−type Aggregate :: K.Context
47−newtype Aggregate a = Aggregate (Opaleye.Aggregator () (Expr a))
51+-- | An @Aggregate a@ describes how to aggregate @Table@s of type @a@. You can
52+-- unpack an @Aggregate@ back to @a@ by running it with 'Rel8.aggregate'. As
53+-- @Aggregate@ is almost an 'Applicative' functor - but there is no 'pure'
54+-- operation. This means 'Aggregate' is an instance of 'Apply', and you can
55+-- combine @Aggregate@s using the @<.>@ combinator.
56+type Aggregate :: k -> Type
57+data Aggregate a where
58+ Aggregate :: !(Opaleye.Aggregator () (Expr a)) -> Aggregate a
59+
60+
61+instance Interpretation Aggregate where
62+ data Col Aggregate _spec where
63+ A :: ()
64+ => { unA :: !(Aggregate a) }
65+ -> Col Aggregate ('Spec labels a)
4866
4967
5068instance Sql DBType a => Table Aggregate (Aggregate a) where
51− type Columns (Aggregate a) = HIdentity a
69+ type Columns (Aggregate a) = HType a
5270 type Context (Aggregate a) = Aggregate
53− type FromExprs (Aggregate a) = a
54− type Transpose to (Aggregate a) = to a
5571
56− toColumns = HIdentity
57− fromColumns (HIdentity a) = a
58− toResult = HIdentity . Identity
59− fromResult (HIdentity (Identity a)) = a
72+ toColumns = HIdentity . A
73+ fromColumns (HIdentity (A a)) = a
74+
75+ reify = notReify
76+ unreify = notReify
77+
78+
79+instance Sql DBType a =>
80+ Recontextualize Aggregate Aggregate (Aggregate a) (Aggregate a)
81+
82+
83+instance Sql DBType a =>
84+ Recontextualize Aggregate Expr (Aggregate a) (Expr a)
85+
86+
87+instance Sql DBType a =>
88+ Recontextualize Aggregate Result (Aggregate a) (Identity a)
89+
90+
91+instance Sql DBType a =>
92+ Recontextualize Aggregate Name (Aggregate a) (Name a)
6093
6194
62−-- | @Aggregates a b@ means that the columns in @a@ are all 'Aggregate's
63−-- for the 'Expr' columns in @b@.
95+instance Sql DBType a =>
96+ Recontextualize Expr Aggregate (Expr a) (Aggregate a)
97+
98+
99+instance Sql DBType a =>
100+ Recontextualize Result Aggregate (Identity a) (Aggregate a)
101+
102+
103+instance Sql DBType a =>
104+ Recontextualize Name Aggregate (Name a) (Aggregate a)
105+
106+
107+instance Labelable Aggregate where
108+ labeler (A aggregate) = A aggregate
109+ unlabeler (A aggregate) = A aggregate
110+
111+
112+-- | @Aggregates a b@ means that the columns in @a@ are all 'Aggregate' 'Expr's
113+-- for the columns in @b@.
64114type Aggregates :: Type -> Type -> Constraint
65−class Transposes Aggregate Expr aggregates exprs => Aggregates aggregates exprs
66−instance Transposes Aggregate Expr aggregates exprs => Aggregates aggregates exprs
115+class Recontextualize Aggregate Expr aggregates exprs => Aggregates aggregates exprs
116+instance Recontextualize Aggregate Expr aggregates exprs => Aggregates aggregates exprs
117+
118+
119+foldInputs :: Monoid b
120+ => (Maybe Aggregator -> Opaleye.PrimExpr -> b) -> Aggregate a -> b
121+foldInputs f (Aggregate (Opaleye.Aggregator (Opaleye.PackMap agg))) =
122+ getConst $ flip agg () $ \(aggregator, a) ->
123+ Const $ f (detuplize <$> aggregator) a
124+ where
125+ detuplize (operation, ordering, distinction) =
126+ Aggregator {operation, ordering, distinction}
127+
128+
129+mapInputs :: ()
130+ => (Opaleye.PrimExpr -> Opaleye.PrimExpr) -> Aggregate a -> Aggregate a
131+mapInputs transform (Aggregate (Opaleye.Aggregator (Opaleye.PackMap agg))) =
132+ Aggregate $ Opaleye.Aggregator $ Opaleye.PackMap $ agg . \f input ->
133+ f (fmap transform input)
67134
68135
69−zipOutputs :: ()
70− => (Expr a -> Expr b -> Expr c) -> Aggregate a -> Aggregate b -> Aggregate c
71−zipOutputs f (Aggregate a) (Aggregate b) = Aggregate (liftA2 f a b)
136+type Aggregator :: Type
137+data Aggregator = Aggregator
138+ { operation :: Opaleye.AggrOp
139+ , ordering :: [Opaleye.OrderExpr]
140+ , distinction :: Opaleye.AggrDistinct
141+ }
72142
73143
74−unsafeMakeAggregate :: forall (input :: Type) (output :: Type) n n' a a'. ()
144+unsafeMakeAggregate :: ()
75145 => (Expr input -> Opaleye.PrimExpr)
76146 -> (Opaleye.PrimExpr -> Expr output)
77− -> Opaleye.Aggregator (Opaleye.Field_ n a) (Opaleye.Field_ n' a')
147+ -> Maybe Aggregator
78148 -> Expr input
79149 -> Aggregate output
80150unsafeMakeAggregate input output aggregator expr =
81− Aggregate $ dimap in_ out aggregator
82− where out = output . Opaleye.unColumn
83− in_ = Opaleye.Column . input . const expr
151+ Aggregate $ Opaleye.Aggregator $ Opaleye.PackMap $ \f _ ->
152+ output <$> f (tuplize <$> aggregator, input expr)
153+ where
154+ tuplize Aggregator {operation, ordering, distinction} =
155+ (operation, ordering, distinction)
src/Rel8/Column.hs+77−5
@@ -1,21 +1,38 @@
11{-# language DataKinds #-}
2+{-# language FlexibleContexts #-}
3+{-# language LambdaCase #-}
4+{-# language MultiParamTypeClasses #-}
25{-# language StandaloneKindSignatures #-}
36{-# language TypeFamilies #-}
47
58module Rel8.Column
6− ( Column
9+ ( Column, AColumn(..)
710 , TColumn
811 )
912where
1013
1114-- base
1215import Data.Kind ( Type )
13−import Prelude ()
16+import Prelude
1417
1518-- rel8
19+import Rel8.Aggregate ( Aggregate, Col( A ) )
20+import Rel8.Expr ( Expr, Col( E ) )
1621import Rel8.FCF ( Eval, Exp )
22+import Rel8.Kind.Context ( SContext(..), Reifiable( contextSing ) )
23+import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) )
1724import qualified Rel8.Schema.Kind as K
18−import Rel8.Schema.Result ( Result )
25+import Rel8.Schema.Name ( Name(..), Col( N ) )
26+import Rel8.Schema.Null ( Sql )
27+import Rel8.Schema.Reify ( Reify, Col(..) )
28+import Rel8.Schema.Result ( Col( R ), Result )
29+import Rel8.Schema.Spec ( Spec( Spec ) )
30+import Rel8.Table
31+ ( Table, Columns, Context, fromColumns, toColumns
32+ , Unreify, reify, unreify
33+ )
34+import Rel8.Table.Recontextualize ( Recontextualize )
35+import Rel8.Type ( DBType )
1936
2037
2138-- | This type family is used to specify columns in 'Rel8able's. In @Column f
@@ -23,8 +40,63 @@ import Rel8.Schema.Result ( Result )
2340-- 'Rel8able' definitions), and @a@ is the type of the column.
2441type Column :: K.Context -> Type -> Type
2542type family Column context a where
26− Column Result a = a
27− Column context a = context a
43+ Column (Reify context) a = AColumn context a
44+ Column Aggregate a = Aggregate a
45+ Column Expr a = Expr a
46+ Column Name a = Name a
47+ Column Result a = a
48+
49+
50+type AColumn :: K.Context -> Type -> Type
51+newtype AColumn context a = AColumn (Column context a)
52+
53+
54+instance (Reifiable context, Sql DBType a) =>
55+ Table (Reify context) (AColumn context a)
56+ where
57+ type Context (AColumn context a) = Reify context
58+ type Columns (AColumn context a) = HIdentity ('Spec '[] a)
59+ type Unreify (AColumn context a) = Column context a
60+
61+ fromColumns (HIdentity (Reify a)) = sfromColumn contextSing a
62+ toColumns = HIdentity . Reify . stoColumn contextSing
63+ reify _ = AColumn
64+ unreify _ (AColumn a) = a
65+
66+
67+instance
68+ ( Reifiable context, Reifiable context'
69+ , Sql DBType a
70+ ) =>
71+ Recontextualize
72+ (Reify context)
73+ (Reify context')
74+ (AColumn context a)
75+ (AColumn context' a)
76+
77+
78+sfromColumn :: ()
79+ => SContext context
80+ -> Col context ('Spec labels a)
81+ -> AColumn context a
82+sfromColumn = \case
83+ SAggregate -> \(A a) -> AColumn a
84+ SExpr -> \(E a) -> AColumn a
85+ SName -> \(N a) -> AColumn a
86+ SResult -> \(R a) -> AColumn a
87+ SReify context -> \(Reify a) -> AColumn (sfromColumn context a)
88+
89+
90+stoColumn :: ()
91+ => SContext context
92+ -> AColumn context a
93+ -> Col context ('Spec labels a)
94+stoColumn = \case
95+ SAggregate -> \(AColumn a) -> A a
96+ SExpr -> \(AColumn a) -> E a
97+ SName -> \(AColumn a) -> N a
98+ SResult -> \(AColumn a) -> R a
99+ SReify context -> \(AColumn a) -> Reify (stoColumn context a)
28100
29101
30102data TColumn :: K.Context -> Type -> Exp Type
src/Rel8/Column/ADT.hs+68−3
@@ -1,23 +1,88 @@
11{-# language DataKinds #-}
2+{-# language LambdaCase #-}
3+{-# language MultiParamTypeClasses #-}
24{-# language StandaloneKindSignatures #-}
35{-# language TypeFamilies #-}
6+{-# language UndecidableInstances #-}
47
58module Rel8.Column.ADT
6− ( HADT
9+ ( HADT, AHADT(..)
710 )
811where
912
1013-- base
1114import Data.Kind ( Type )
12−import Prelude ()
15+import Prelude
1316
1417-- rel8
18+import Rel8.Generic.Rel8able ( GColumns )
19+import Rel8.Kind.Context ( SContext(..), Reifiable, contextSing )
20+import Rel8.Schema.Context ( Col )
1521import qualified Rel8.Schema.Kind as K
22+import Rel8.Schema.Reify ( Reify, hreify, hunreify )
1623import Rel8.Schema.Result ( Result )
17−import Rel8.Table.ADT ( ADT )
24+import Rel8.Table
25+ ( Table, Columns, Context, fromColumns, toColumns
26+ , Unreify, reify, unreify
27+ )
28+import Rel8.Table.ADT ( ADT( ADT ), ADTable, fromADT, toADT )
29+import Rel8.Table.Recontextualize ( Recontextualize )
1830
1931
2032type HADT :: K.Context -> K.Rel8able -> Type
2133type family HADT context t where
34+ HADT (Reify context) t = AHADT context t
2235 HADT Result t = t Result
2336 HADT context t = ADT t context
37+
38+
39+type AHADT :: K.Context -> K.Rel8able -> Type
40+newtype AHADT context t = AHADT (HADT context t)
41+
42+
43+instance (ADTable t, Reifiable context) =>
44+ Table (Reify context) (AHADT context t)
45+ where
46+ type Context (AHADT context t) = Reify context
47+ type Columns (AHADT context t) = GColumns (ADT t)
48+ type Unreify (AHADT context t) = HADT context t
49+
50+ fromColumns = sfromColumnsADT contextSing
51+ toColumns = stoColumnsADT contextSing
52+ reify _ = AHADT
53+ unreify _ (AHADT a) = a
54+
55+
56+instance
57+ ( Reifiable context, Reifiable context'
58+ , ADTable t, t ~ t'
59+ )
60+ => Recontextualize
61+ (Reify context)
62+ (Reify context')
63+ (AHADT context t)
64+ (AHADT context' t')
65+
66+
67+sfromColumnsADT :: ADTable t
68+ => SContext context
69+ -> GColumns (ADT t) (Col (Reify context))
70+ -> AHADT context t
71+sfromColumnsADT = \case
72+ SAggregate -> AHADT . ADT . hunreify
73+ SExpr -> AHADT . ADT . hunreify
74+ SName -> AHADT . ADT . hunreify
75+ SResult -> AHADT . fromADT . ADT . hunreify
76+ SReify context -> AHADT . sfromColumnsADT context . hunreify
77+
78+
79+stoColumnsADT :: ADTable t
80+ => SContext context
81+ -> AHADT context t
82+ -> GColumns (ADT t) (Col (Reify context))
83+stoColumnsADT = \case
84+ SAggregate -> hreify . (\(AHADT (ADT a)) -> a)
85+ SExpr -> hreify . (\(AHADT (ADT a)) -> a)
86+ SName -> hreify . (\(AHADT (ADT a)) -> a)
87+ SResult -> hreify . (\(ADT a) -> a) . toADT . (\(AHADT a) -> a)
88+ SReify context -> hreify . stoColumnsADT context . (\(AHADT a) -> a)
src/Rel8/Column/Either.hs+140−4
@@ -1,26 +1,162 @@
11{-# language DataKinds #-}
2+{-# language FlexibleContexts #-}
3+{-# language LambdaCase #-}
4+{-# language MultiParamTypeClasses #-}
25{-# language StandaloneKindSignatures #-}
3−{-# language TypeFamilyDependencies #-}
6+{-# language TypeFamilies #-}
7+{-# language UndecidableInstances #-}
48
59module Rel8.Column.Either
6− ( HEither
10+ ( HEither, AHEither(..)
711 )
812where
913
1014-- base
15+import Control.Applicative ( liftA2 )
16+import Data.Bifunctor ( Bifunctor, bimap )
1117import Data.Kind ( Type )
1218import Prelude
1319
1420-- rel8
21+import Rel8.Aggregate ( Aggregate )
22+import Rel8.Expr ( Expr )
23+import Rel8.Kind.Context ( SContext(..), Reifiable( contextSing ) )
24+import Rel8.Schema.Context ( Col )
25+import Rel8.Schema.HTable.Either ( HEitherTable )
1526import qualified Rel8.Schema.Kind as K
27+import Rel8.Schema.Name ( Name(..) )
28+import Rel8.Schema.Reify ( Reify, hreify, hunreify )
1629import Rel8.Schema.Result ( Result )
30+import Rel8.Table
31+ ( Table, Columns, Context, fromColumns, toColumns
32+ , Unreify, reify, unreify
33+ )
1734import Rel8.Table.Either ( EitherTable )
35+import Rel8.Table.Recontextualize ( Recontextualize )
1836
1937
2038-- | Nest an 'Either' value within a 'Rel8able'. @HEither f a b@ will produce a
2139-- 'EitherTable' @a b@ in the 'Expr' context, and a 'Either' @a b@ in the
2240-- 'Result' context.
2341type HEither :: K.Context -> Type -> Type -> Type
24−type family HEither context = either | either -> context where
42+type family HEither context where
43+ HEither (Reify context) = AHEither context
44+ HEither Aggregate = EitherTable
45+ HEither Expr = EitherTable
46+ HEither Name = EitherTable
2547 HEither Result = Either
26− HEither context = EitherTable context
48+
49+
50+type AHEither :: K.Context -> Type -> Type -> Type
51+newtype AHEither context a b = AHEither (HEither context a b)
52+
53+
54+instance Reifiable context => Bifunctor (AHEither context) where
55+ bimap = sbimapEither contextSing
56+
57+
58+instance Reifiable context => Functor (AHEither context a) where
59+ fmap = bimap id
60+
61+
62+instance (Reifiable context, Table (Reify context) a, Table (Reify context) b)
63+ => Table (Reify context) (AHEither context a b)
64+ where
65+ type Context (AHEither context a b) = Reify context
66+ type Columns (AHEither context a b) = HEitherTable (Columns a) (Columns b)
67+ type Unreify (AHEither context a b) = HEither context (Unreify a) (Unreify b)
68+
69+ fromColumns = sfromColumnsEither contextSing
70+ toColumns = stoColumnsEither contextSing
71+ reify proof = liftA2 bimap reify reify proof . AHEither
72+ unreify proof = (\(AHEither a) -> a) . liftA2 bimap unreify unreify proof
73+
74+
75+instance
76+ ( Reifiable context, Reifiable context'
77+ , Recontextualize (Reify context) (Reify context') a a'
78+ , Recontextualize (Reify context) (Reify context') b b'
79+ ) =>
80+ Recontextualize
81+ (Reify context)
82+ (Reify context')
83+ (AHEither context a b)
84+ (AHEither context' a' b')
85+
86+
87+sbimapEither :: ()
88+ => SContext context
89+ -> (a -> c)
90+ -> (b -> d)
91+ -> AHEither context a b
92+ -> AHEither context c d
93+sbimapEither = \case
94+ SAggregate -> \f g (AHEither a) -> AHEither (bimap f g a)
95+ SExpr -> \f g (AHEither a) -> AHEither (bimap f g a)
96+ SResult -> \f g (AHEither a) -> AHEither (bimap f g a)
97+ SName -> \f g (AHEither a) -> AHEither (bimap f g a)
98+ SReify context -> \f g (AHEither a) -> AHEither (sbimapEither context f g a)
99+
100+
101+sfromColumnsEither :: (Table (Reify context) a, Table (Reify context) b)
102+ => SContext context
103+ -> HEitherTable (Columns a) (Columns b) (Col (Reify context))
104+ -> AHEither context a b
105+sfromColumnsEither = \case
106+ SAggregate ->
107+ AHEither .
108+ bimap (fromColumns . hreify) (fromColumns . hreify) .
109+ fromColumns .
110+ hunreify
111+ SExpr ->
112+ AHEither .
113+ bimap (fromColumns . hreify) (fromColumns . hreify) .
114+ fromColumns .
115+ hunreify
116+ SResult ->
117+ AHEither .
118+ bimap (fromColumns . hreify) (fromColumns . hreify) .
119+ fromColumns .
120+ hunreify
121+ SName ->
122+ AHEither .
123+ bimap (fromColumns . hreify) (fromColumns . hreify) .
124+ fromColumns .
125+ hunreify
126+ SReify context ->
127+ AHEither .
128+ sbimapEither context (fromColumns . hreify) (fromColumns . hreify) .
129+ sfromColumnsEither context .
130+ hunreify
131+
132+
133+stoColumnsEither :: (Table (Reify context) a, Table (Reify context) b)
134+ => SContext context
135+ -> AHEither context a b
136+ -> HEitherTable (Columns a) (Columns b) (Col (Reify context))
137+stoColumnsEither = \case
138+ SAggregate ->
139+ hreify .
140+ toColumns .
141+ bimap (hunreify . toColumns) (hunreify . toColumns) .
142+ (\(AHEither a) -> a)
143+ SExpr ->
144+ hreify .
145+ toColumns .
146+ bimap (hunreify . toColumns) (hunreify . toColumns) .
147+ (\(AHEither a) -> a)
148+ SResult ->
149+ hreify .
150+ toColumns .
151+ bimap (hunreify . toColumns) (hunreify . toColumns) .
152+ (\(AHEither a) -> a)
153+ SName ->
154+ hreify .
155+ toColumns .
156+ bimap (hunreify . toColumns) (hunreify . toColumns) .
157+ (\(AHEither a) -> a)
158+ SReify context ->
159+ hreify .
160+ stoColumnsEither context .
161+ sbimapEither context (hunreify . toColumns) (hunreify . toColumns) .
162+ (\(AHEither a) -> a)
src/Rel8/Column/Lift.hs+67−3
@@ -1,23 +1,87 @@
11{-# language DataKinds #-}
2+{-# language LambdaCase #-}
3+{-# language MultiParamTypeClasses #-}
24{-# language StandaloneKindSignatures #-}
35{-# language TypeFamilies #-}
46
57module Rel8.Column.Lift
6− ( Lift
8+ ( Lift, ALift(..)
79 )
810where
911
1012-- base
1113import Data.Kind ( Type )
12−import Prelude ()
14+import Prelude
1315
1416-- rel8
17+import Rel8.Generic.Rel8able ( GColumns )
18+import Rel8.Kind.Context ( Reifiable(..), SContext(..) )
19+import Rel8.Schema.Context ( Col )
1520import qualified Rel8.Schema.Kind as K
21+import Rel8.Schema.Reify ( Reify, hreify, hunreify )
1622import Rel8.Schema.Result ( Result )
17−import Rel8.Table.HKD ( HKD )
23+import Rel8.Table
24+ ( Table, Columns, Context, fromColumns, toColumns
25+ , Unreify, reify, unreify
26+ )
27+import Rel8.Table.Rel8able ()
28+import Rel8.Table.HKD ( HKD( HKD ), HKDable, fromHKD, toHKD )
29+import Rel8.Table.Recontextualize ( Recontextualize )
1830
1931
2032type Lift :: K.Context -> Type -> Type
2133type family Lift context a where
34+ Lift (Reify context) a = ALift context a
2235 Lift Result a = a
2336 Lift context a = HKD a context
37+
38+
39+type ALift :: K.Context -> Type -> Type
40+newtype ALift context a = ALift
41+ { unALift :: Lift context a
42+ }
43+
44+
45+instance (Reifiable context, HKDable a) =>
46+ Table (Reify context) (ALift context a)
47+ where
48+ type Context (ALift context a) = Reify context
49+ type Columns (ALift context a) = GColumns (HKD a)
50+ type Unreify (ALift context a) = Lift context a
51+
52+ fromColumns = sfromColumnsLift contextSing
53+ toColumns = stoColumnsLift contextSing
54+ reify _ = ALift
55+ unreify _ (ALift a) = a
56+
57+
58+instance (Reifiable context, Reifiable context', HKDable a) =>
59+ Recontextualize
60+ (Reify context)
61+ (Reify context')
62+ (ALift context a)
63+ (ALift context' a)
64+
65+
66+sfromColumnsLift :: HKDable a
67+ => SContext context
68+ -> GColumns (HKD a) (Col (Reify context))
69+ -> ALift context a
70+sfromColumnsLift = \case
71+ SAggregate -> ALift . fromColumns . hunreify
72+ SExpr -> ALift . fromColumns . hunreify
73+ SName -> ALift . fromColumns . hunreify
74+ SResult -> ALift . fromHKD . HKD . hunreify
75+ SReify context -> ALift . sfromColumnsLift context . hunreify
76+
77+
78+stoColumnsLift :: HKDable a
79+ => SContext context
80+ -> ALift context a
81+ -> GColumns (HKD a) (Col (Reify context))
82+stoColumnsLift = \case
83+ SAggregate -> hreify . toColumns . unALift
84+ SExpr -> hreify . toColumns . unALift
85+ SName -> hreify . toColumns . unALift
86+ SResult -> hreify . (\(HKD a) -> a) . toHKD . unALift
87+ SReify context -> hreify . stoColumnsLift context . unALift
src/Rel8/Column/List.hs+126−6
@@ -1,25 +1,145 @@
11{-# language DataKinds #-}
2+{-# language FlexibleContexts #-}
3+{-# language LambdaCase #-}
4+{-# language MultiParamTypeClasses #-}
25{-# language StandaloneKindSignatures #-}
3−{-# language TypeFamilyDependencies #-}
6+{-# language TypeFamilies #-}
7+{-# language UndecidableInstances #-}
48
59module Rel8.Column.List
6− ( HList
10+ ( HList, AHList(..)
711 )
812where
913
1014-- base
1115import Data.Kind ( Type )
12−import Prelude ()
16+import Data.Type.Equality ( (:~:)( Refl ) )
17+import Prelude
1318
1419-- rel8
20+import Rel8.Aggregate ( Aggregate )
21+import Rel8.Expr ( Expr )
22+import Rel8.Kind.Context ( SContext(..), Reifiable( contextSing ) )
23+import Rel8.Schema.Context ( Col )
24+import Rel8.Schema.HTable.List ( HListTable )
1525import qualified Rel8.Schema.Kind as K
26+import Rel8.Schema.Name ( Name )
27+import Rel8.Schema.Reify ( Reify, hreify, hunreify )
1628import Rel8.Schema.Result ( Result )
17−import Rel8.Table.List ( ListTable )
29+import Rel8.Table
30+ ( Table, Columns, Congruent, Context, fromColumns, toColumns
31+ , Unreify, reify, unreify
32+ )
33+import Rel8.Table.List ( ListTable( ListTable ) )
34+import Rel8.Table.Recontextualize ( Recontextualize )
35+import Rel8.Table.Unreify ( Unreifiability(..), Unreifiable, unreifiability )
1836
1937
2038-- | Nest a list within a 'Rel8able'. @HList f a@ will produce a 'ListTable'
2139-- @a@ in the 'Expr' context, and a @[a]@ in the 'Result' context.
2240type HList :: K.Context -> Type -> Type
23−type family HList context = list | list -> context where
41+type family HList context where
42+ HList (Reify context) = AHList context
43+ HList Aggregate = ListTable
44+ HList Expr = ListTable
45+ HList Name = ListTable
2446 HList Result = []
25− HList context = ListTable context
47+
48+
49+type AHList :: K.Context -> Type -> Type
50+newtype AHList context a = AHList (HList context a)
51+
52+
53+instance (Reifiable context, Unreifiable a, Table (Reify context) a) =>
54+ Table (Reify context) (AHList context a)
55+ where
56+ type Context (AHList context a) = Reify context
57+ type Columns (AHList context a) = HListTable (Columns a)
58+ type Unreify (AHList context a) = HList context (Unreify a)
59+
60+ fromColumns = sfromColumnsList contextSing
61+ toColumns = stoColumnsList contextSing
62+
63+ reify _ = sreifyList (unreifiability contextSing)
64+ unreify _ = sunreifyList (unreifiability contextSing)
65+
66+
67+instance
68+ ( Reifiable context, Reifiable context'
69+ , Unreifiable a, Unreifiable a'
70+ , Recontextualize (Reify context) (Reify context') a a'
71+ )
72+ => Recontextualize
73+ (Reify context)
74+ (Reify context')
75+ (AHList context a)
76+ (AHList context' a')
77+
78+
79+smapList :: Congruent a b
80+ => SContext context
81+ -> (a -> b)
82+ -> (HListTable (Columns a) (Col (Context a)) -> HListTable (Columns b) (Col (Context b)))
83+ -> AHList context a
84+ -> AHList context b
85+smapList = \case
86+ SAggregate -> \_ f (AHList (ListTable a)) -> AHList (ListTable (f a))
87+ SExpr -> \_ f (AHList (ListTable a)) -> AHList (ListTable (f a))
88+ SResult -> \f _ (AHList as) -> AHList (fmap f as)
89+ SName -> \_ f (AHList (ListTable a)) -> AHList (ListTable (f a))
90+ SReify context -> \f g (AHList as) -> AHList (smapList context f g as)
91+
92+
93+sfromColumnsList :: Table (Reify context) a
94+ => SContext context
95+ -> HListTable (Columns a) (Col (Reify context))
96+ -> AHList context a
97+sfromColumnsList = \case
98+ SAggregate -> AHList . ListTable
99+ SExpr -> AHList . ListTable
100+ SResult -> AHList . fmap (fromColumns . hreify) . fromColumns . hunreify
101+ SName -> AHList . ListTable
102+ SReify context ->
103+ AHList .
104+ smapList context (fromColumns . hreify) hreify .
105+ sfromColumnsList context .
106+ hunreify
107+
108+
109+stoColumnsList :: Table (Reify context) a
110+ => SContext context
111+ -> AHList context a
112+ -> HListTable (Columns a) (Col (Reify context))
113+stoColumnsList = \case
114+ SAggregate -> \(AHList (ListTable a)) -> a
115+ SExpr -> \(AHList (ListTable a)) -> a
116+ SResult ->
117+ hreify . toColumns . fmap (hunreify . toColumns) . (\(AHList a) -> a)
118+ SName -> \(AHList (ListTable a)) -> a
119+ SReify context ->
120+ hreify .
121+ stoColumnsList context .
122+ smapList context (hunreify . toColumns) hunreify .
123+ (\(AHList a) -> a)
124+
125+
126+sreifyList :: Table (Reify context) a
127+ => Unreifiability context a
128+ -> HList context (Unreify a)
129+ -> AHList context a
130+sreifyList = \case
131+ UResult -> AHList . fmap (reify Refl)
132+ Unreifiability context ->
133+ smapList context (reify Refl) hreify .
134+ AHList
135+
136+
137+sunreifyList :: Table (Reify context) a
138+ => Unreifiability context a
139+ -> AHList context a
140+ -> HList context (Unreify a)
141+sunreifyList = \case
142+ UResult -> fmap (unreify Refl) . (\(AHList a) -> a)
143+ Unreifiability context ->
144+ (\(AHList a) -> a) .
145+ smapList context (unreify Refl) hunreify
src/Rel8/Column/Maybe.hs+105−4
@@ -1,9 +1,14 @@
11{-# language DataKinds #-}
2+{-# language FlexibleContexts #-}
3+{-# language GADTs #-}
4+{-# language LambdaCase #-}
5+{-# language MultiParamTypeClasses #-}
26{-# language StandaloneKindSignatures #-}
3−{-# language TypeFamilyDependencies #-}
7+{-# language TypeFamilies #-}
8+{-# language UndecidableInstances #-}
49
510module Rel8.Column.Maybe
6− ( HMaybe
11+ ( HMaybe, AHMaybe(..)
712 )
813where
914
@@ -12,15 +17,111 @@ import Data.Kind ( Type )
1217import Prelude
1318
1419-- rel8
20+import Rel8.Aggregate ( Aggregate )
21+import Rel8.Expr ( Expr )
22+import Rel8.Kind.Context ( SContext(..), Reifiable( contextSing ) )
23+import Rel8.Schema.Context ( Col )
24+import Rel8.Schema.HTable.Maybe ( HMaybeTable )
1525import qualified Rel8.Schema.Kind as K
26+import Rel8.Schema.Name ( Name )
27+import Rel8.Schema.Reify ( Reify, hreify, hunreify )
1628import Rel8.Schema.Result ( Result )
29+import Rel8.Table
30+ ( Table, Columns, Context, fromColumns, toColumns
31+ , Unreify, reify, unreify
32+ )
1733import Rel8.Table.Maybe ( MaybeTable )
34+import Rel8.Table.Recontextualize ( Recontextualize )
1835
1936
2037-- | Nest a 'Maybe' value within a 'Rel8able'. @HMaybe f a@ will produce a
2138-- 'MaybeTable' @a@ in the 'Expr' context, and a 'Maybe' @a@ in the 'Result'
2239-- context.
2340type HMaybe :: K.Context -> Type -> Type
24−type family HMaybe context = maybe | maybe -> context where
41+type family HMaybe context where
42+ HMaybe (Reify context) = AHMaybe context
43+ HMaybe Aggregate = MaybeTable
44+ HMaybe Expr = MaybeTable
45+ HMaybe Name = MaybeTable
2546 HMaybe Result = Maybe
26− HMaybe context = MaybeTable context
47+
48+
49+type AHMaybe :: K.Context -> Type -> Type
50+newtype AHMaybe context a = AHMaybe (HMaybe context a)
51+
52+
53+instance Reifiable context => Functor (AHMaybe context) where
54+ fmap = smapMaybe contextSing
55+
56+
57+instance (Reifiable context, Table (Reify context) a) =>
58+ Table (Reify context) (AHMaybe context a)
59+ where
60+ type Context (AHMaybe context a) = Reify context
61+ type Columns (AHMaybe context a) = HMaybeTable (Columns a)
62+ type Unreify (AHMaybe context a) = HMaybe context (Unreify a)
63+
64+ fromColumns = sfromColumnsMaybe contextSing
65+ toColumns = stoColumnsMaybe contextSing
66+ reify proof = fmap fmap reify proof . AHMaybe
67+ unreify proof = (\(AHMaybe a) -> a) . fmap fmap unreify proof
68+
69+
70+instance
71+ ( Reifiable context, Reifiable context'
72+ , Recontextualize (Reify context) (Reify context') a a'
73+ ) =>
74+ Recontextualize
75+ (Reify context)
76+ (Reify context')
77+ (AHMaybe context a)
78+ (AHMaybe context' a')
79+
80+
81+smapMaybe :: ()
82+ => SContext context
83+ -> (a -> b)
84+ -> AHMaybe context a
85+ -> AHMaybe context b
86+smapMaybe = \case
87+ SAggregate -> \f (AHMaybe a) -> AHMaybe (fmap f a)
88+ SExpr -> \f (AHMaybe a) -> AHMaybe (fmap f a)
89+ SResult -> \f (AHMaybe a) -> AHMaybe (fmap f a)
90+ SName -> \f (AHMaybe a) -> AHMaybe (fmap f a)
91+ SReify context -> \f (AHMaybe a) -> AHMaybe (smapMaybe context f a)
92+
93+
94+sfromColumnsMaybe :: Table (Reify context) a
95+ => SContext context
96+ -> HMaybeTable (Columns a) (Col (Reify context))
97+ -> AHMaybe context a
98+sfromColumnsMaybe = \case
99+ SAggregate -> AHMaybe . fmap (fromColumns . hreify) . fromColumns . hunreify
100+ SExpr -> AHMaybe . fmap (fromColumns . hreify) . fromColumns . hunreify
101+ SResult -> AHMaybe . fmap (fromColumns . hreify) . fromColumns . hunreify
102+ SName -> AHMaybe . fmap (fromColumns . hreify) . fromColumns . hunreify
103+ SReify context ->
104+ AHMaybe .
105+ smapMaybe context (fromColumns . hreify) .
106+ sfromColumnsMaybe context .
107+ hunreify
108+
109+
110+stoColumnsMaybe :: Table (Reify context) a
111+ => SContext context
112+ -> AHMaybe context a
113+ -> HMaybeTable (Columns a) (Col (Reify context))
114+stoColumnsMaybe = \case
115+ SAggregate ->
116+ hreify . toColumns . fmap (hunreify . toColumns) . (\(AHMaybe a) -> a)
117+ SExpr ->
118+ hreify . toColumns . fmap (hunreify . toColumns) . (\(AHMaybe a) -> a)
119+ SResult ->
120+ hreify . toColumns . fmap (hunreify . toColumns) . (\(AHMaybe a) -> a)
121+ SName ->
122+ hreify . toColumns . fmap (hunreify . toColumns) . (\(AHMaybe a) -> a)
123+ SReify context ->
124+ hreify .
125+ stoColumnsMaybe context .
126+ smapMaybe context (hunreify . toColumns) .
127+ (\(AHMaybe a) -> a)
src/Rel8/Column/NonEmpty.hs+127−6
@@ -1,27 +1,148 @@
11{-# language DataKinds #-}
2+{-# language FlexibleContexts #-}
3+{-# language LambdaCase #-}
4+{-# language MultiParamTypeClasses #-}
25{-# language StandaloneKindSignatures #-}
3−{-# language TypeFamilyDependencies #-}
6+{-# language TypeFamilies #-}
7+{-# language UndecidableInstances #-}
48
59module Rel8.Column.NonEmpty
6− ( HNonEmpty
10+ ( HNonEmpty, AHNonEmpty(..)
711 )
812where
913
1014-- base
1115import Data.Kind ( Type )
1216import Data.List.NonEmpty ( NonEmpty )
13−import Prelude ()
17+import Data.Type.Equality ( (:~:)( Refl ) )
18+import Prelude
1419
1520-- rel8
21+import Rel8.Aggregate ( Aggregate )
22+import Rel8.Expr ( Expr )
23+import Rel8.Kind.Context ( SContext(..), Reifiable( contextSing ) )
24+import Rel8.Schema.Context ( Col )
25+import Rel8.Schema.HTable.NonEmpty ( HNonEmptyTable )
1626import qualified Rel8.Schema.Kind as K
27+import Rel8.Schema.Name ( Name )
28+import Rel8.Schema.Reify ( Reify, hreify, hunreify )
1729import Rel8.Schema.Result ( Result )
18−import Rel8.Table.NonEmpty ( NonEmptyTable )
30+import Rel8.Table
31+ ( Table, Columns, Congruent, Context, fromColumns, toColumns
32+ , Unreify, reify, unreify
33+ )
34+import Rel8.Table.NonEmpty ( NonEmptyTable( NonEmptyTable ) )
35+import Rel8.Table.Recontextualize ( Recontextualize )
36+import Rel8.Table.Unreify ( Unreifiability(..), Unreifiable, unreifiability )
1937
2038
2139-- | Nest a 'NonEmpty' list within a 'Rel8able'. @HNonEmpty f a@ will produce a
2240-- 'NonEmptyTable' @a@ in the 'Expr' context, and a 'NonEmpty' @a@ in the
2341-- 'Result' context.
2442type HNonEmpty :: K.Context -> Type -> Type
25−type family HNonEmpty context = nonEmpty | nonEmpty -> context where
43+type family HNonEmpty context where
44+ HNonEmpty (Reify context) = AHNonEmpty context
45+ HNonEmpty Aggregate = NonEmptyTable
46+ HNonEmpty Expr = NonEmptyTable
47+ HNonEmpty Name = NonEmptyTable
2648 HNonEmpty Result = NonEmpty
27− HNonEmpty context = NonEmptyTable context
49+
50+
51+type AHNonEmpty :: K.Context -> Type -> Type
52+newtype AHNonEmpty context a = AHNonEmpty (HNonEmpty context a)
53+
54+
55+instance (Reifiable context, Unreifiable a, Table (Reify context) a) =>
56+ Table (Reify context) (AHNonEmpty context a)
57+ where
58+ type Context (AHNonEmpty context a) = Reify context
59+ type Columns (AHNonEmpty context a) = HNonEmptyTable (Columns a)
60+ type Unreify (AHNonEmpty context a) = HNonEmpty context (Unreify a)
61+
62+ fromColumns = sfromColumnsNonEmpty contextSing
63+ toColumns = stoColumnsNonEmpty contextSing
64+
65+ reify _ = sreifyNonEmpty (unreifiability contextSing)
66+ unreify _ = sunreifyNonEmpty (unreifiability contextSing)
67+
68+
69+instance
70+ ( Reifiable context, Reifiable context'
71+ , Unreifiable a, Unreifiable a'
72+ , Recontextualize (Reify context) (Reify context') a a'
73+ )
74+ => Recontextualize
75+ (Reify context)
76+ (Reify context')
77+ (AHNonEmpty context a)
78+ (AHNonEmpty context' a')
79+
80+
81+smapNonEmpty :: Congruent a b
82+ => SContext context
83+ -> (a -> b)
84+ -> (HNonEmptyTable (Columns a) (Col (Context a)) -> HNonEmptyTable (Columns b) (Col (Context b)))
85+ -> AHNonEmpty context a
86+ -> AHNonEmpty context b
87+smapNonEmpty = \case
88+ SAggregate -> \_ f (AHNonEmpty (NonEmptyTable a)) -> AHNonEmpty (NonEmptyTable (f a))
89+ SExpr -> \_ f (AHNonEmpty (NonEmptyTable a)) -> AHNonEmpty (NonEmptyTable (f a))
90+ SResult -> \f _ (AHNonEmpty as) -> AHNonEmpty (fmap f as)
91+ SName -> \_ f (AHNonEmpty (NonEmptyTable a)) -> AHNonEmpty (NonEmptyTable (f a))
92+ SReify context -> \f g (AHNonEmpty as) -> AHNonEmpty (smapNonEmpty context f g as)
93+
94+
95+sfromColumnsNonEmpty :: Table (Reify context) a
96+ => SContext context
97+ -> HNonEmptyTable (Columns a) (Col (Reify context))
98+ -> AHNonEmpty context a
99+sfromColumnsNonEmpty = \case
100+ SAggregate -> AHNonEmpty . NonEmptyTable
101+ SExpr -> AHNonEmpty . NonEmptyTable
102+ SResult ->
103+ AHNonEmpty . fmap (fromColumns . hreify) . fromColumns . hunreify
104+ SName -> AHNonEmpty . NonEmptyTable
105+ SReify context ->
106+ AHNonEmpty .
107+ smapNonEmpty context (fromColumns . hreify) hreify .
108+ sfromColumnsNonEmpty context .
109+ hunreify
110+
111+
112+stoColumnsNonEmpty :: Table (Reify context) a
113+ => SContext context
114+ -> AHNonEmpty context a
115+ -> HNonEmptyTable (Columns a) (Col (Reify context))
116+stoColumnsNonEmpty = \case
117+ SAggregate -> \(AHNonEmpty (NonEmptyTable a)) -> a
118+ SExpr -> \(AHNonEmpty (NonEmptyTable a)) -> a
119+ SResult ->
120+ hreify . toColumns . fmap (hunreify . toColumns) . (\(AHNonEmpty a) -> a)
121+ SName -> \(AHNonEmpty (NonEmptyTable a)) -> a
122+ SReify context ->
123+ hreify .
124+ stoColumnsNonEmpty context .
125+ smapNonEmpty context (hunreify . toColumns) hunreify .
126+ (\(AHNonEmpty a) -> a)
127+
128+
129+sreifyNonEmpty :: Table (Reify context) a
130+ => Unreifiability context a
131+ -> HNonEmpty context (Unreify a)
132+ -> AHNonEmpty context a
133+sreifyNonEmpty = \case
134+ UResult -> AHNonEmpty . fmap (reify Refl)
135+ Unreifiability context ->
136+ smapNonEmpty context (reify Refl) hreify .
137+ AHNonEmpty
138+
139+
140+sunreifyNonEmpty :: Table (Reify context) a
141+ => Unreifiability context a
142+ -> AHNonEmpty context a
143+ -> HNonEmpty context (Unreify a)
144+sunreifyNonEmpty = \case
145+ UResult -> fmap (unreify Refl) . (\(AHNonEmpty a) -> a)
146+ Unreifiability context ->
147+ (\(AHNonEmpty a) -> a) .
148+ smapNonEmpty context (unreify Refl) hunreify
src/Rel8/Column/Null.hsdeleted−26
@@ -1,26 +0,0 @@
1−{-# language DataKinds #-}
2−{-# language StandaloneKindSignatures #-}
3−{-# language TypeFamilyDependencies #-}
4−
5−module Rel8.Column.Null
6− ( HNull
7− )
8−where
9−
10−-- base
11−import Data.Kind ( Type )
12−import Prelude
13−
14−-- rel8
15−import qualified Rel8.Schema.Kind as K
16−import Rel8.Schema.Result ( Result )
17−import Rel8.Table.Null ( NullTable )
18−
19−
20−-- | Nest a 'Null' value within a 'Rel8able'. @HNull f a@ will produce a
21−-- 'NullTable' @a@ in the 'Expr' context, and a @'Maybe' a@ in the 'Result'
22−-- context.
23−type HNull :: K.Context -> Type -> Type
24−type family HNull context = maybe | maybe -> context where
25− HNull Result = Maybe
26− HNull context = NullTable context
src/Rel8/Column/These.hs+142−5
@@ -1,19 +1,38 @@
11{-# language DataKinds #-}
2+{-# language FlexibleContexts #-}
3+{-# language GADTs #-}
4+{-# language LambdaCase #-}
5+{-# language MultiParamTypeClasses #-}
26{-# language StandaloneKindSignatures #-}
3−{-# language TypeFamilyDependencies #-}
7+{-# language TypeFamilies #-}
8+{-# language UndecidableInstances #-}
49
510module Rel8.Column.These
6− ( HThese
11+ ( HThese, AHThese(..)
712 )
813where
914
1015-- base
16+import Control.Applicative ( liftA2 )
17+import Data.Bifunctor ( Bifunctor, bimap )
1118import Data.Kind ( Type )
12−import Prelude ()
19+import Prelude
1320
1421-- rel8
22+import Rel8.Aggregate ( Aggregate )
23+import Rel8.Expr ( Expr )
24+import Rel8.Kind.Context ( SContext(..), Reifiable( contextSing ) )
25+import Rel8.Schema.Context ( Col )
26+import Rel8.Schema.HTable.These ( HTheseTable )
1527import qualified Rel8.Schema.Kind as K
28+import Rel8.Schema.Name ( Name )
29+import Rel8.Schema.Reify ( Reify, hreify, hunreify )
1630import Rel8.Schema.Result ( Result )
31+import Rel8.Table
32+ ( Table, Columns, Context, fromColumns, toColumns
33+ , Unreify, reify, unreify
34+ )
35+import Rel8.Table.Recontextualize ( Recontextualize )
1736import Rel8.Table.These ( TheseTable )
1837
1938-- these
@@ -24,6 +43,124 @@ import Data.These ( These )
2443-- 'TheseTable' @a b@ in the 'Expr' context, and a 'These' @a b@ in the
2544-- 'Result' context.
2645type HThese :: K.Context -> Type -> Type -> Type
27−type family HThese context = these | these -> context where
46+type family HThese context where
47+ HThese (Reify context) = AHThese context
48+ HThese Aggregate = TheseTable
49+ HThese Expr = TheseTable
50+ HThese Name = TheseTable
2851 HThese Result = These
29− HThese context = TheseTable context
52+
53+
54+type AHThese :: K.Context -> Type -> Type -> Type
55+newtype AHThese context a b = AHThese (HThese context a b)
56+
57+
58+instance Reifiable context => Bifunctor (AHThese context) where
59+ bimap = sbimapThese contextSing
60+
61+
62+instance Reifiable context => Functor (AHThese context a) where
63+ fmap = bimap id
64+
65+
66+instance (Reifiable context, Table (Reify context) a, Table (Reify context) b)
67+ => Table (Reify context) (AHThese context a b)
68+ where
69+ type Context (AHThese context a b) = Reify context
70+ type Columns (AHThese context a b) = HTheseTable (Columns a) (Columns b)
71+ type Unreify (AHThese context a b) = HThese context (Unreify a) (Unreify b)
72+
73+ fromColumns = sfromColumnsThese contextSing
74+ toColumns = stoColumnsThese contextSing
75+ reify proof = liftA2 bimap reify reify proof . AHThese
76+ unreify proof = (\(AHThese a) -> a) . liftA2 bimap unreify unreify proof
77+
78+
79+instance
80+ ( Reifiable context, Reifiable context'
81+ , Recontextualize (Reify context) (Reify context') a a'
82+ , Recontextualize (Reify context) (Reify context') b b'
83+ ) =>
84+ Recontextualize
85+ (Reify context)
86+ (Reify context')
87+ (AHThese context a b)
88+ (AHThese context' a' b')
89+
90+
91+sbimapThese :: ()
92+ => SContext context
93+ -> (a -> c)
94+ -> (b -> d)
95+ -> AHThese context a b
96+ -> AHThese context c d
97+sbimapThese = \case
98+ SAggregate -> \f g (AHThese a) -> AHThese (bimap f g a)
99+ SExpr -> \f g (AHThese a) -> AHThese (bimap f g a)
100+ SResult -> \f g (AHThese a) -> AHThese (bimap f g a)
101+ SName -> \f g (AHThese a) -> AHThese (bimap f g a)
102+ SReify context -> \f g (AHThese a) -> AHThese (sbimapThese context f g a)
103+
104+
105+sfromColumnsThese :: (Table (Reify context) a, Table (Reify context) b)
106+ => SContext context
107+ -> HTheseTable (Columns a) (Columns b) (Col (Reify context))
108+ -> AHThese context a b
109+sfromColumnsThese = \case
110+ SAggregate ->
111+ AHThese .
112+ bimap (fromColumns . hreify) (fromColumns . hreify) .
113+ fromColumns .
114+ hunreify
115+ SExpr ->
116+ AHThese .
117+ bimap (fromColumns . hreify) (fromColumns . hreify) .
118+ fromColumns .
119+ hunreify
120+ SResult ->
121+ AHThese .
122+ bimap (fromColumns . hreify) (fromColumns . hreify) .
123+ fromColumns .
124+ hunreify
125+ SName ->
126+ AHThese .
127+ bimap (fromColumns . hreify) (fromColumns . hreify) .
128+ fromColumns .
129+ hunreify
130+ SReify context ->
131+ AHThese .
132+ sbimapThese context (fromColumns . hreify) (fromColumns . hreify) .
133+ sfromColumnsThese context .
134+ hunreify
135+
136+
137+stoColumnsThese :: (Table (Reify context) a, Table (Reify context) b)
138+ => SContext context
139+ -> AHThese context a b
140+ -> HTheseTable (Columns a) (Columns b) (Col (Reify context))
141+stoColumnsThese = \case
142+ SAggregate ->
143+ hreify .
144+ toColumns .
145+ bimap (hunreify . toColumns) (hunreify . toColumns) .
146+ (\(AHThese a) -> a)
147+ SExpr ->
148+ hreify .
149+ toColumns .
150+ bimap (hunreify . toColumns) (hunreify . toColumns) .
151+ (\(AHThese a) -> a)
152+ SResult ->
153+ hreify .
154+ toColumns .
155+ bimap (hunreify . toColumns) (hunreify . toColumns) .
156+ (\(AHThese a) -> a)
157+ SName ->
158+ hreify .
159+ toColumns .
160+ bimap (hunreify . toColumns) (hunreify . toColumns) .
161+ (\(AHThese a) -> a)
162+ SReify context ->
163+ hreify .
164+ stoColumnsThese context .
165+ sbimapThese context (hunreify . toColumns) (hunreify . toColumns) .
166+ (\(AHThese a) -> a)
src/Rel8/Expr.hs+47−17
@@ -1,21 +1,27 @@
11{-# language DataKinds #-}
22{-# language DerivingStrategies #-}
3−{-# language FlexibleContexts #-}
43{-# language FlexibleInstances #-}
4+{-# language GADTs #-}
55{-# language MultiParamTypeClasses #-}
6+{-# language PolyKinds #-}
7+{-# language RoleAnnotations #-}
68{-# language ScopedTypeVariables #-}
9+{-# language StandaloneDeriving #-}
710{-# language StandaloneKindSignatures #-}
811{-# language TypeApplications #-}
912{-# language TypeFamilies #-}
1013{-# language UndecidableInstances #-}
14+{-# language UndecidableSuperClasses #-}
1115
1216module Rel8.Expr
1317 ( Expr(..)
18+ , Col( E, unE )
1419 )
1520where
1621
1722-- base
18−import Data.Functor.Identity ( Identity( Identity ) )
23+import Data.Functor.Identity ( Identity )
24+import Data.Kind ( Type )
1925import Data.String ( IsString, fromString )
2026import Prelude hiding ( null )
2127
@@ -32,14 +38,17 @@ import Rel8.Expr.Opaleye
3238 , zipPrimExprsWith
3339 )
3440import Rel8.Expr.Serialize ( litExpr )
35−import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) )
36−import qualified Rel8.Schema.Kind as K
41+import Rel8.Schema.Context ( Interpretation, Col )
42+import Rel8.Schema.Context.Label ( Labelable, labeler, unlabeler )
43+import Rel8.Schema.HTable.Identity ( HIdentity( HType ), HType )
3744import Rel8.Schema.Null ( Nullity( Null, NotNull ), Sql, nullable )
45+import Rel8.Schema.Reify ( notReify )
46+import Rel8.Schema.Result ( Result )
47+import Rel8.Schema.Spec ( Spec( Spec ) )
3848import Rel8.Table
39− ( Table, Columns, Context, fromColumns, toColumns
40− , FromExprs, fromResult, toResult
41− , Transpose
49+ ( Table, Columns, Context, fromColumns, toColumns, reify, unreify
4250 )
51+import Rel8.Table.Recontextualize ( Recontextualize )
4352import Rel8.Type ( DBType )
4453import Rel8.Type.Monoid ( DBMonoid, memptyExpr )
4554import Rel8.Type.Num ( DBFloating, DBFractional, DBNum )
@@ -47,9 +56,13 @@ import Rel8.Type.Semigroup ( DBSemigroup, (<>.) )
4756
4857
4958-- | Typed SQL expressions.
50−type Expr :: K.Context
51−newtype Expr a = Expr Opaleye.PrimExpr
52− deriving stock Show
59+type role Expr representational
60+type Expr :: k -> Type
61+data Expr a where
62+ Expr :: k ~ Type => !Opaleye.PrimExpr -> Expr (a :: k)
63+
64+
65+deriving stock instance Show (Expr a)
5366
5467
5568instance Sql DBSemigroup a => Semigroup (Expr a) where
@@ -113,13 +126,30 @@ instance Sql DBFloating a => Floating (Expr a) where
113126 atanh = function "atanh"
114127
115128
129+instance Interpretation Expr where
130+ data Col Expr _spec where
131+ E :: {unE :: !(Expr a)} -> Col Expr ('Spec labels a)
132+
133+
116134instance Sql DBType a => Table Expr (Expr a) where
117− type Columns (Expr a) = HIdentity a
135+ type Columns (Expr a) = HType a
118136 type Context (Expr a) = Expr
119− type FromExprs (Expr a) = a
120− type Transpose to (Expr a) = to a
121137
122− toColumns a = HIdentity a
123− fromColumns (HIdentity a) = a
124− toResult a = HIdentity (Identity a)
125− fromResult (HIdentity (Identity a)) = a
138+ toColumns a = HType (E a)
139+ fromColumns (HType (E a)) = a
140+ reify = notReify
141+ unreify = notReify
142+
143+
144+instance Sql DBType a => Recontextualize Expr Expr (Expr a) (Expr a)
145+
146+
147+instance Sql DBType a => Recontextualize Expr Result (Expr a) (Identity a)
148+
149+
150+instance Sql DBType a => Recontextualize Result Expr (Identity a) (Expr a)
151+
152+
153+instance Labelable Expr where
154+ labeler (E a) = E a
155+ unlabeler (E a) = E a
src/Rel8/Expr.hs-boot+8−6
@@ -1,4 +1,6 @@
1−{-# language DataKinds #-}
1+{-# language GADTs #-}
2+{-# language PolyKinds #-}
3+{-# language RoleAnnotations #-}
24{-# language StandaloneKindSignatures #-}
35
46module Rel8.Expr
@@ -7,14 +9,14 @@ module Rel8.Expr
79where
810
911-- base
12+import Data.Kind ( Type )
1013import Prelude ()
1114
1215-- opaleye
1316import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
1417
15−-- rel8
16−import Rel8.Schema.Kind ( Context )
1718
18−
19−type Expr :: Context
20−newtype Expr a = Expr Opaleye.PrimExpr
19+type role Expr representational
20+type Expr :: k -> Type
21+data Expr a where
22+ Expr :: k ~ Type => !Opaleye.PrimExpr -> Expr (a :: k)
src/Rel8/Expr/Aggregate.hs+64−25
@@ -10,7 +10,6 @@ module Rel8.Expr.Aggregate
1010 , and, or
1111 , min, max
1212 , sum, sumWhere
13− , avg
1413 , stringAgg
1514 , groupByExpr
1615 , listAggExpr, nonEmptyAggExpr
@@ -24,12 +23,10 @@ import Data.List.NonEmpty ( NonEmpty )
2423import Prelude hiding ( and, max, min, null, or, sum )
2524
2625-- opaleye
27−import qualified Opaleye.Internal.Aggregate as Opaleye
28−import Opaleye.Internal.Column ( Field_( Column ) )
29−import qualified Opaleye.Aggregate as Opaleye
26+import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
3027
3128-- rel8
32−import Rel8.Aggregate ( Aggregate, unsafeMakeAggregate )
29+import Rel8.Aggregate ( Aggregate, Aggregator(..), unsafeMakeAggregate )
3330import Rel8.Expr ( Expr )
3431import Rel8.Expr.Bool ( caseExpr )
3532import Rel8.Expr.Opaleye
@@ -53,13 +50,23 @@ import Rel8.Type.Sum ( DBSum )
5350
5451-- | Count the occurances of a single column. Corresponds to @COUNT(a)@
5552count :: Expr a -> Aggregate Int64
56−count = unsafeMakeAggregate toPrimExpr fromPrimExpr Opaleye.count
53+count = unsafeMakeAggregate toPrimExpr fromPrimExpr $
54+ Just Aggregator
55+ { operation = Opaleye.AggrCount
56+ , ordering = []
57+ , distinction = Opaleye.AggrAll
58+ }
59+
5760
5861-- | Count the number of distinct occurances of a single column. Corresponds to
5962-- @COUNT(DISTINCT a)@
6063countDistinct :: Sql DBEq a => Expr a -> Aggregate Int64
6164countDistinct = unsafeMakeAggregate toPrimExpr fromPrimExpr $
62− Opaleye.distinctAggregator Opaleye.count
65+ Just Aggregator
66+ { operation = Opaleye.AggrCount
67+ , ordering = []
68+ , distinction = Opaleye.AggrDistinct
69+ }
6370
6471
6572-- | Corresponds to @COUNT(*)@.
@@ -74,39 +81,56 @@ countWhere condition = count (caseExpr [(condition, litExpr (Just True))] null)
7481
7582-- | Corresponds to @bool_and@.
7683and :: Expr Bool -> Aggregate Bool
77−and = unsafeMakeAggregate toPrimExpr fromPrimExpr Opaleye.boolAnd
84+and = unsafeMakeAggregate toPrimExpr fromPrimExpr $
85+ Just Aggregator
86+ { operation = Opaleye.AggrBoolAnd
87+ , ordering = []
88+ , distinction = Opaleye.AggrAll
89+ }
7890
7991
8092-- | Corresponds to @bool_or@.
8193or :: Expr Bool -> Aggregate Bool
82−or = unsafeMakeAggregate toPrimExpr fromPrimExpr Opaleye.boolOr
94+or = unsafeMakeAggregate toPrimExpr fromPrimExpr $
95+ Just Aggregator
96+ { operation = Opaleye.AggrBoolOr
97+ , ordering = []
98+ , distinction = Opaleye.AggrAll
99+ }
83100
84101
85102-- | Produce an aggregation for @Expr a@ using the @max@ function.
86103max :: Sql DBMax a => Expr a -> Aggregate a
87−max = unsafeMakeAggregate toPrimExpr fromPrimExpr Opaleye.unsafeMax
104+max = unsafeMakeAggregate toPrimExpr fromPrimExpr $
105+ Just Aggregator
106+ { operation = Opaleye.AggrMax
107+ , ordering = []
108+ , distinction = Opaleye.AggrAll
109+ }
88110
89111
90112-- | Produce an aggregation for @Expr a@ using the @max@ function.
91113min :: Sql DBMin a => Expr a -> Aggregate a
92−min = unsafeMakeAggregate toPrimExpr fromPrimExpr Opaleye.unsafeMin
114+min = unsafeMakeAggregate toPrimExpr fromPrimExpr $
115+ Just Aggregator
116+ { operation = Opaleye.AggrMin
117+ , ordering = []
118+ , distinction = Opaleye.AggrAll
119+ }
93120
94121-- | Corresponds to @sum@. Note that in SQL, @sum@ is type changing - for
95122-- example the @sum@ of @integer@ returns a @bigint@. Rel8 doesn't support
96−-- this, and will add explicit casts back to the original input type. This can
123+-- this, and will add explicit cast back to the original input type. This can
97124-- lead to overflows, and if you anticipate very large sums, you should upcast
98125-- your input.
99126sum :: Sql DBSum a => Expr a -> Aggregate a
100−sum = unsafeMakeAggregate toPrimExpr (castExpr . fromPrimExpr) Opaleye.unsafeSum
101−
127+sum = unsafeMakeAggregate toPrimExpr (castExpr . fromPrimExpr) $
128+ Just Aggregator
129+ { operation = Opaleye.AggrSum
130+ , ordering = []
131+ , distinction = Opaleye.AggrAll
132+ }
102133
103−-- | Corresponds to @avg@. Note that in SQL, @avg@ is type changing - for
104−-- example, the @avg@ of @integer@ returns a @numeric@. Rel8 doesn't support
105−-- this, and will add explicit casts back to the original input type. If you
106−-- need a fractional result on an integral column, you should cast your input
107−-- to 'Double' or 'Data.Scientific.Scientific' before calling 'avg'.
108−avg :: Sql DBSum a => Expr a -> Aggregate a
109−avg = unsafeMakeAggregate toPrimExpr (castExpr . fromPrimExpr) Opaleye.unsafeAvg
110134
111135-- | Take the sum of all expressions that satisfy a predicate.
112136sumWhere :: (Sql DBNum a, Sql DBSum a)
@@ -118,12 +142,17 @@ sumWhere condition a = sum (caseExpr [(condition, a)] 0)
118142stringAgg :: Sql DBString a
119143 => Expr db -> Expr a -> Aggregate a
120144stringAgg delimiter =
121− unsafeMakeAggregate toPrimExpr (castExpr . fromPrimExpr) (Opaleye.stringAgg (Column (toPrimExpr delimiter)))
145+ unsafeMakeAggregate toPrimExpr (castExpr . fromPrimExpr) $
146+ Just Aggregator
147+ { operation = Opaleye.AggrStringAggr (toPrimExpr delimiter)
148+ , ordering = []
149+ , distinction = Opaleye.AggrAll
150+ }
122151
123152
124153-- | Aggregate a value by grouping by it.
125154groupByExpr :: Sql DBEq a => Expr a -> Aggregate a
126−groupByExpr = unsafeMakeAggregate toPrimExpr fromPrimExpr Opaleye.groupBy
155+groupByExpr = unsafeMakeAggregate toPrimExpr fromPrimExpr Nothing
127156
128157
129158-- | Collect expressions values as a list.
@@ -138,13 +167,23 @@ nonEmptyAggExpr = snonEmptyAggExpr typeInformation
138167
139168slistAggExpr :: ()
140169 => TypeInformation (Unnullify a) -> Expr a -> Aggregate [a]
141−slistAggExpr info = unsafeMakeAggregate to fromPrimExpr Opaleye.arrayAgg
170+slistAggExpr info = unsafeMakeAggregate to fromPrimExpr $ Just
171+ Aggregator
172+ { operation = Opaleye.AggrArr
173+ , ordering = []
174+ , distinction = Opaleye.AggrAll
175+ }
142176 where
143177 to = encodeArrayElement info . toPrimExpr
144178
145179
146180snonEmptyAggExpr :: ()
147181 => TypeInformation (Unnullify a) -> Expr a -> Aggregate (NonEmpty a)
148−snonEmptyAggExpr info = unsafeMakeAggregate to fromPrimExpr Opaleye.arrayAgg
182+snonEmptyAggExpr info = unsafeMakeAggregate to fromPrimExpr $ Just
183+ Aggregator
184+ { operation = Opaleye.AggrArr
185+ , ordering = []
186+ , distinction = Opaleye.AggrAll
187+ }
149188 where
150189 to = encodeArrayElement info . toPrimExpr
src/Rel8/Expr/Default.hsdeleted−35
@@ -1,35 +0,0 @@
1−module Rel8.Expr.Default
2− ( unsafeDefault
3− )
4−where
5−
6−-- base
7−import Prelude ()
8−
9−-- opaleye
10−import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
11−
12−-- rel8
13−import Rel8.Expr ( Expr )
14−import Rel8.Expr.Opaleye ( fromPrimExpr )
15−
16−
17−-- | Corresponds to the SQL @DEFAULT@ expression.
18−--
19−-- This 'Expr' is unsafe for numerous reasons, and should be used with care:
20−--
21−-- 1. This 'Expr' only makes sense in an @INSERT@ or @UPDATE@ statement.
22−--
23−-- 2. Rel8 is not able to verify that a particular column actually has a
24−-- @DEFAULT@ value. Trying to use @unsafeDefault@ where there is no default
25−-- will cause a runtime crash
26−--
27−-- 3. @DEFAULT@ values can not be transformed. For example, the innocuous Rel8
28−-- code @unsafeDefault + 1@ will crash, despite type checking.
29−--
30−-- Given all these caveats, we suggest avoiding the use of default values where
31−-- possible, instead being explicit. A common scenario where default values are
32−-- used is with auto-incrementing identifier columns. In this case, we suggest
33−-- using 'Rel8.nextval' instead.
34−unsafeDefault :: Expr a
35−unsafeDefault = fromPrimExpr Opaleye.DefaultInsertExpr
src/Rel8/Expr/Eq.hs+1−1
@@ -22,7 +22,7 @@ import Prelude
2222import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
2323
2424-- rel8
25−import Rel8.Expr ( Expr )
25+import {-# SOURCE #-} Rel8.Expr ( Expr )
2626import Rel8.Expr.Bool ( (&&.), (||.), false, or_, coalesce )
2727import Rel8.Expr.Null ( isNull, unsafeLiftOpNull )
2828import Rel8.Expr.Opaleye ( fromPrimExpr, toPrimExpr, zipPrimExprsWith )
src/Rel8/Expr/Num.hs+6−39
@@ -4,24 +4,18 @@
44{-# options_ghc -fno-warn-redundant-constraints #-}
55
66module Rel8.Expr.Num
7− ( fromIntegral
8− , realToFrac
9− , div, mod, divMod
10− , quot, rem, quotRem
11− , ceiling, floor, round, truncate
7+ ( fromIntegral, realToFrac, div, mod, ceiling, floor, round, truncate
128 )
139where
1410
1511-- base
16−import Prelude ( (+), (-), fst, negate, signum, snd )
12+import Prelude ()
1713
1814-- rel
1915import Rel8.Expr ( Expr( Expr ) )
20−import Rel8.Expr.Eq ( (==.) )
2116import Rel8.Expr.Function ( function )
2217import Rel8.Expr.Opaleye ( castExpr )
2318import Rel8.Schema.Null ( Homonullable, Sql )
24−import Rel8.Table.Bool ( bool )
2519import Rel8.Type.Num ( DBFractional, DBIntegral, DBNum )
2620
2721
@@ -48,41 +42,14 @@ ceiling :: (Sql DBFractional a, Sql DBIntegral b, Homonullable a b)
4842ceiling = function "ceiling"
4943
5044
51−-- | Emulates the behaviour of the Haskell function 'Prelude.div' in
52−-- PostgreSQL.
45+-- | Perform integral division. Corresponds to the @div()@ function.
5346div :: Sql DBIntegral a => Expr a -> Expr a -> Expr a
54−div n d = fst (divMod n d)
47+div = function "div"
5548
5649
57−-- | Emulates the behaviour of the Haskell function 'Prelude.mod' in
58−-- PostgreSQL.
50+-- | Corresponds to the @mod()@ function.
5951mod :: Sql DBIntegral a => Expr a -> Expr a -> Expr a
60−mod n d = snd (divMod n d)
61−
62−
63−-- | Simultaneous 'div' and 'mod'.
64−divMod :: Sql DBIntegral a => Expr a -> Expr a -> (Expr a, Expr a)
65−divMod n d = bool qr (q - 1, r + d) (signum r ==. negate (signum d))
66− where
67− qr@(q, r) = quotRem n d
68−
69−
70−-- | Perform integral division. Corresponds to the @div()@ function in
71−-- PostgreSQL, which behaves like Haskell's 'Prelude.quot' rather than
72−-- 'Prelude.div'.
73−quot :: Sql DBIntegral a => Expr a -> Expr a -> Expr a
74−quot = function "div"
75−
76−
77−-- | Corresponds to the @mod()@ function in PostgreSQL, which behaves like
78−-- Haskell's 'Prelude.rem' rather than 'Prelude.mod'.
79−rem :: Sql DBIntegral a => Expr a -> Expr a -> Expr a
80−rem = function "mod"
81−
82−
83−-- | Simultaneous 'quot' and 'rem'.
84−quotRem :: Sql DBIntegral a => Expr a -> Expr a -> (Expr a, Expr a)
85−quotRem n d = (quot n d, rem n d)
52+mod = function "mod"
8653
8754
8855-- | Round a 'DFractional' to a 'DBIntegral' by rounding to the nearest smaller
src/Rel8/Expr/Opaleye.hs+3−12
@@ -10,7 +10,7 @@ module Rel8.Expr.Opaleye
1010 , scastExpr, sunsafeCastExpr
1111 , unsafeLiteral
1212 , fromPrimExpr, toPrimExpr, mapPrimExpr, zipPrimExprsWith, traversePrimExpr
13− , toColumn, fromColumn, traverseFieldP
13+ , toColumn, fromColumn
1414 )
1515where
1616
@@ -27,9 +27,6 @@ import Rel8.Schema.Null ( Unnullify, Sql )
2727import Rel8.Type ( DBType, typeInformation )
2828import Rel8.Type.Information ( TypeInformation(..) )
2929
30−-- profunctors
31−import Data.Profunctor ( Profunctor, dimap )
32−
3330
3431castExpr :: Sql DBType a => Expr a -> Expr a
3532castExpr = scastExpr typeInformation
@@ -83,15 +80,9 @@ traversePrimExpr :: Functor f
8380traversePrimExpr f = fmap fromPrimExpr . f . toPrimExpr
8481
8582
86−traverseFieldP :: Profunctor p
87− => p (Opaleye.Field_ n x) (Opaleye.Field_ m y)
88− -> p (Expr a) (Expr b)
89−traverseFieldP = dimap (toColumn . toPrimExpr) (fromPrimExpr . fromColumn)
90−
91−
92−toColumn :: Opaleye.PrimExpr -> Opaleye.Field_ n b
83+toColumn :: Opaleye.PrimExpr -> Opaleye.Column b
9384toColumn = Opaleye.Column
9485
9586
96−fromColumn :: Opaleye.Field_ n b -> Opaleye.PrimExpr
87+fromColumn :: Opaleye.Column b -> Opaleye.PrimExpr
9788fromColumn (Opaleye.Column a) = a
src/Rel8/Expr/Sequence.hs+1−2
@@ -11,7 +11,6 @@ import Prelude
1111import Rel8.Expr ( Expr )
1212import Rel8.Expr.Function ( function )
1313import Rel8.Expr.Serialize ( litExpr )
14−import Rel8.Expr.Text ( quoteIdent )
1514
1615-- text
1716import Data.Text ( pack )
@@ -19,4 +18,4 @@ import Data.Text ( pack )
1918
2019-- | See https://www.postgresql.org/docs/current/functions-sequence.html
2120nextval :: String -> Expr Int64
22−nextval = function "nextval" . quoteIdent . litExpr . pack
21+nextval = function "nextval" . litExpr . pack
src/Rel8/Expr/Text.hs+2−23
@@ -17,9 +17,6 @@ module Rel8.Expr.Text
1717 , pgClientEncoding, quoteIdent, quoteLiteral, quoteNullable, regexpReplace
1818 , regexpSplitToArray, repeat, replace, reverse, right, rpad, rtrim
1919 , splitPart, strpos, substr, translate
20−
21− -- * @LIKE@ and @ILIKE@
22− , like, ilike
2320 )
2421where
2522
@@ -27,7 +24,7 @@ where
2724import Data.Bool ( Bool )
2825import Data.Int ( Int32 )
2926import Data.Maybe ( Maybe( Nothing, Just ) )
30−import Prelude ( flip )
27+import Prelude ()
3128
3229-- bytestring
3330import Data.ByteString ( ByteString )
@@ -52,7 +49,7 @@ infixr 6 ++.
5249
5350
5451-- | Matches regular expression, case sensitive
55−--
52+--
5653-- Corresponds to the @~.@ operator.
5754(~.) :: Expr Text -> Expr Text -> Expr Bool
5855(~.) = binaryOperator "~."
@@ -276,21 +273,3 @@ substr a b Nothing = function "substr" a b
276273-- | Corresponds to the @translate@ function.
277274translate :: Expr Text -> Expr Text -> Expr Text -> Expr Text
278275translate = function "translate"
279−
280−
281−-- | @like x y@ corresponds to the expression @y LIKE x@.
282−--
283−-- Note that the arguments to @like@ are swapped. This is to aid currying, so
284−-- you can write expressions like
285−-- @filter (like "Rel%" . packageName) =<< each haskellPackages@
286−like :: Expr Text -> Expr Text -> Expr Bool
287−like = flip (binaryOperator "LIKE")
288−
289−
290−-- | @ilike x y@ corresponds to the expression @y ILIKE x@.
291−--
292−-- Note that the arguments to @ilike@ are swapped. This is to aid currying, so
293−-- you can write expressions like
294−-- @filter (ilike "Rel%" . packageName) =<< each haskellPackages@
295−ilike :: Expr Text -> Expr Text -> Expr Bool
296−ilike = flip (binaryOperator "ILIKE")
src/Rel8/Expr/Window.hsdeleted−121
@@ -1,121 +0,0 @@
1−module Rel8.Expr.Window
2− ( cumulative
3− , rowNumber
4− , rank
5− , denseRank
6− , percentRank
7− , cumeDist
8− , ntile
9− , lag
10− , lead
11− , firstValue
12− , lastValue
13− , nthValue
14− )
15−where
16−
17−-- base
18−import Data.Int ( Int32, Int64 )
19−import Prelude
20−
21−-- opaleye
22−import qualified Opaleye.Internal.Aggregate as Opaleye
23−import qualified Opaleye.Internal.PackMap as Opaleye
24−import qualified Opaleye.Internal.Window as Opaleye
25−import qualified Opaleye.Window as Opaleye
26−
27−-- profunctors
28−import Data.Profunctor (dimap)
29−
30−-- rel8
31−import Rel8.Aggregate ( Aggregate( Aggregate ) )
32−import Rel8.Expr ( Expr )
33−import Rel8.Expr.Opaleye ( fromColumn, fromPrimExpr, toColumn, toPrimExpr )
34−import Rel8.Schema.Null ( Nullify )
35−import Rel8.Window ( Window( Window ) )
36−
37−
38−cumulative :: (a -> Aggregate b) -> Window a (Expr b)
39−cumulative f =
40− fromWindowFunction $ Opaleye.aggregatorWindowFunction (fromAggregate f) id
41−
42−
43−-- | [@row_number()@](https://www.postgresql.org/docs/current/functions-window.html)
44−rowNumber :: Window a (Expr Int64)
45−rowNumber = fromWindowFunction $ fromPrimExpr . fromColumn <$> Opaleye.rowNumber
46−
47−
48−-- | [@rank()@](https://www.postgresql.org/docs/current/functions-window.html)
49−rank :: Window a (Expr Int64)
50−rank = fromWindowFunction $ fromPrimExpr . fromColumn <$> Opaleye.rank
51−
52−
53−-- | [@dense_rank()@](https://www.postgresql.org/docs/current/functions-window.html)
54−denseRank :: Window a (Expr Int64)
55−denseRank = fromWindowFunction $ fromPrimExpr . fromColumn <$> Opaleye.denseRank
56−
57−
58−-- | [@percent_rank()@](https://www.postgresql.org/docs/current/functions-window.html)
59−percentRank :: Window a (Expr Double)
60−percentRank = fromWindowFunction $ fromPrimExpr . fromColumn <$> Opaleye.percentRank
61−
62−
63−-- | [@cume_dist()@](https://www.postgresql.org/docs/current/functions-window.html)
64−cumeDist :: Window a (Expr Double)
65−cumeDist = fromWindowFunction $ fromPrimExpr . fromColumn <$> Opaleye.cumeDist
66−
67−
68−-- | [@ntile(num_buckets)@](https://www.postgresql.org/docs/current/functions-window.html)
69−ntile :: Expr Int32 -> Window a (Expr Int32)
70−ntile buckets = fromWindowFunction $ fromPrimExpr . fromColumn <$>
71− Opaleye.ntile (toColumn (toPrimExpr buckets))
72−
73−
74−-- | [@lag(value, offset, default)@](https://www.postgresql.org/docs/current/functions-window.html)
75−lag :: Expr Int32 -> Expr a -> Window (Expr a) (Expr a)
76−lag offset def =
77− fromWindowFunction $
78− dimap (toColumn . toPrimExpr) (fromPrimExpr . fromColumn) $
79− Opaleye.lag (toColumn (toPrimExpr offset)) (toColumn (toPrimExpr def))
80−
81−
82−-- | [@lead(value, offset, default)@](https://www.postgresql.org/docs/current/functions-window.html)
83−lead :: Expr Int32 -> Expr a -> Window (Expr a) (Expr a)
84−lead offset def =
85− fromWindowFunction $
86− dimap (toColumn . toPrimExpr) (fromPrimExpr . fromColumn) $
87− Opaleye.lead (toColumn (toPrimExpr offset)) (toColumn (toPrimExpr def))
88−
89−
90−-- | [@first_value(value)@](https://www.postgresql.org/docs/current/functions-window.html)
91−firstValue :: Window (Expr a) (Expr a)
92−firstValue =
93− fromWindowFunction $
94− dimap (toColumn . toPrimExpr) (fromPrimExpr . fromColumn)
95− Opaleye.firstValue
96−
97−
98−-- | [@last_value(value)@](https://www.postgresql.org/docs/current/functions-window.html)
99−lastValue :: Window (Expr a) (Expr a)
100−lastValue =
101− fromWindowFunction $
102− dimap (toColumn . toPrimExpr) (fromPrimExpr . fromColumn)
103− Opaleye.lastValue
104−
105−
106−-- | [@nth_value(value, n)@](https://www.postgresql.org/docs/current/functions-window.html)
107−nthValue :: Expr Int32 -> Window (Expr a) (Expr (Nullify a))
108−nthValue n =
109− fromWindowFunction $
110− dimap (toColumn . toPrimExpr) (fromPrimExpr . fromColumn) $
111− Opaleye.nthValue (toColumn (toPrimExpr n))
112−
113−
114−fromAggregate :: (a -> Aggregate b) -> Opaleye.Aggregator a (Expr b)
115−fromAggregate f = Opaleye.Aggregator $ Opaleye.PackMap $ \w a -> case f a of
116− Aggregate (Opaleye.Aggregator (Opaleye.PackMap x)) -> x w ()
117−
118−
119−fromWindowFunction :: Opaleye.WindowFunction a b -> Window a b
120−fromWindowFunction (Opaleye.WindowFunction (Opaleye.PackMap w)) =
121− Window $ Opaleye.Windows $ Opaleye.PackMap $ \f -> w $ \o -> f (o, mempty)
src/Rel8/FCF.hs+3−3
@@ -5,7 +5,7 @@
55
66module Rel8.FCF
77 ( Exp, Eval
8− , Id
8+ , Compose
99 )
1010where
1111
@@ -22,5 +22,5 @@ type Eval :: Exp e -> e
2222type family Eval a
2323
2424
25−data Id :: a -> Exp a
26−type instance Eval (Id a) = a
25+data Compose :: (Type -> Type) -> (Type -> Exp Type) -> Type -> Exp Type
26+type instance Eval (Compose f g a) = f (Eval (g a))
src/Rel8/Generic/Construction.hs+165−146
@@ -2,6 +2,9 @@
22{-# language ConstraintKinds #-}
33{-# language DataKinds #-}
44{-# language FlexibleContexts #-}
5+{-# language FlexibleInstances #-}
6+{-# language LambdaCase #-}
7+{-# language MultiParamTypeClasses #-}
58{-# language NamedFieldPuns #-}
69{-# language ScopedTypeVariables #-}
710{-# language StandaloneKindSignatures #-}
@@ -25,17 +28,18 @@ where
2528import Data.Bifunctor ( first )
2629import Data.Kind ( Constraint, Type )
2730import Data.List.NonEmpty ( NonEmpty( (:|) ) )
31+import Data.Type.Equality ( (:~:)( Refl ) )
2832import GHC.TypeLits ( Symbol )
2933import Prelude
3034
3135-- rel8
32−import Rel8.Aggregate ( Aggregate( Aggregate ) )
33−import Rel8.Expr ( Expr )
36+import Rel8.Aggregate ( Col( A ), Aggregate( Aggregate ) )
37+import Rel8.Expr ( Col( E ), Expr )
3438import Rel8.Expr.Aggregate ( groupByExpr )
3539import Rel8.Expr.Eq ( (==.) )
3640import Rel8.Expr.Null ( nullify, snull, unsafeUnnullify )
3741import Rel8.Expr.Serialize ( litExpr )
38−import Rel8.FCF ( Eval, Exp, Id )
42+import Rel8.FCF ( Eval, Exp )
3943import Rel8.Generic.Construction.ADT
4044 ( GConstructorADT, GMakeableADT, gmakeADT
4145 , GConstructableADT
@@ -55,16 +59,18 @@ import Rel8.Kind.Algebra
5559 , KnownAlgebra, algebraSing
5660 )
5761import qualified Rel8.Kind.Algebra as K
58−import Rel8.Schema.Context.Nullify ( sguard, snullify )
62+import Rel8.Schema.Context.Nullify ( runTag )
5963import Rel8.Schema.HTable ( HTable )
60−import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) )
64+import Rel8.Schema.HTable.Identity ( HIdentity( HType ) )
6165import qualified Rel8.Schema.Kind as K
62−import Rel8.Schema.Name ( Name( Name ) )
66+import Rel8.Schema.Name ( Col( N ), Name( Name ) )
6367import Rel8.Schema.Null ( Nullity( Null, NotNull ) )
64−import Rel8.Schema.Spec ( Spec( Spec, nullity, info ) )
68+import Rel8.Schema.Spec ( SSpec( SSpec, nullity, info ) )
69+import Rel8.Schema.Reify ( Col( Reify ), Reify, hreify, hunreify )
70+import Rel8.Schema.Result ( Result )
6571import Rel8.Table
66− ( TTable, TColumns
67− , Table, fromColumns, toColumns
72+ ( TTable, TColumns, TUnreify
73+ , Table, fromColumns, toColumns, reify, unreify
6874 )
6975import Rel8.Table.Bool ( case_ )
7076import Rel8.Type.Tag ( Tag )
@@ -73,10 +79,10 @@ import Rel8.Type.Tag ( Tag )
7379type GGBuildable :: K.Algebra -> Symbol -> (K.Context -> Exp (Type -> Type)) -> Constraint
7480type GGBuildable algebra name rep =
7581 ( KnownAlgebra algebra
76− , Eval (GGColumns algebra TColumns (Eval (rep Aggregate))) ~ Eval (GGColumns algebra TColumns (Eval (rep Expr)))
77− , Eval (GGColumns algebra TColumns (Eval (rep Expr))) ~ Eval (GGColumns algebra TColumns (Eval (rep Expr)))
78− , Eval (GGColumns algebra TColumns (Eval (rep Name))) ~ Eval (GGColumns algebra TColumns (Eval (rep Expr)))
79− , HTable (Eval (GGColumns algebra TColumns (Eval (rep Expr))))
82+ , Eval (GGColumns algebra TColumns (Eval (rep (Reify Aggregate)))) ~ Eval (GGColumns algebra TColumns (Eval (rep (Reify Result))))
83+ , Eval (GGColumns algebra TColumns (Eval (rep (Reify Expr)))) ~ Eval (GGColumns algebra TColumns (Eval (rep (Reify Result))))
84+ , Eval (GGColumns algebra TColumns (Eval (rep (Reify Name)))) ~ Eval (GGColumns algebra TColumns (Eval (rep (Reify Result))))
85+ , HTable (Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))))
8086 , GGBuildable' algebra name rep
8187 )
8288
@@ -84,63 +90,65 @@ type GGBuildable algebra name rep =
8490type GGBuildable' :: K.Algebra -> Symbol -> (K.Context -> Exp (Type -> Type)) -> Constraint
8591type family GGBuildable' algebra name rep where
8692 GGBuildable' 'K.Product name rep =
87− ( name ~ GConstructor (Eval (rep Expr))
88− , Representable Id (Eval (rep Expr))
89− , GConstructable (TTable Expr) TColumns Id Expr (Eval (rep Expr))
93+ ( name ~ GConstructor (Eval (rep (Reify Expr)))
94+ , Representable TUnreify (Eval (rep (Reify Expr)))
95+ , GConstructable (TTable (Reify Expr)) TColumns TUnreify (Col (Reify Expr)) (Eval (rep (Reify Expr)))
9096 )
9197 GGBuildable' 'K.Sum name rep =
92− ( Representable Id (GConstructorADT name (Eval (rep Expr)))
93− , GMakeableADT (TTable Expr) TColumns Id Expr name (Eval (rep Expr))
98+ ( Representable TUnreify (GConstructorADT name (Eval (rep (Reify Expr))))
99+ , GMakeableADT (TTable (Reify Expr)) TColumns TUnreify (Col (Reify Expr)) name (Eval (rep (Reify Expr)))
94100 )
95101
96102
97103type GGBuild :: K.Algebra -> Symbol -> (K.Context -> Exp (Type -> Type)) -> Type -> Type
98104type family GGBuild algebra name rep r where
99105 GGBuild 'K.Product _name rep r =
100− GConstruct Id (Eval (rep Expr)) r
106+ GConstruct TUnreify (Eval (rep (Reify Expr))) r
101107 GGBuild 'K.Sum name rep r =
102− GConstruct Id (GConstructorADT name (Eval (rep Expr))) r
108+ GConstruct TUnreify (GConstructorADT name (Eval (rep (Reify Expr)))) r
103109
104110
105111ggbuild :: forall algebra name rep a. GGBuildable algebra name rep
106− => (Eval (GGColumns algebra TColumns (Eval (rep Expr))) Expr -> a)
112+ => (Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))) (Col Expr) -> a)
107113 -> GGBuild algebra name rep a
108114ggbuild gfromColumns = case algebraSing @algebra of
109115 SProduct ->
110− gtabulate @Id @(Eval (rep Expr)) @a $
116+ gtabulate @TUnreify @(Eval (rep (Reify Expr))) @a $
111117 gfromColumns .
118+ hunreify .
112119 gconstruct
113− @(TTable Expr)
120+ @(TTable (Reify Expr))
114121 @TColumns
115− @Id
116− @Expr
117− @(Eval (rep Expr))
118− (const toColumns)
122+ @TUnreify
123+ @(Col (Reify Expr))
124+ @(Eval (rep (Reify Expr)))
125+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
119126 SSum ->
120− gtabulate @Id @(GConstructorADT name (Eval (rep Expr))) @a $
127+ gtabulate @TUnreify @(GConstructorADT name (Eval (rep (Reify Expr)))) @a $
121128 gfromColumns .
129+ hunreify .
122130 gmakeADT
123− @(TTable Expr)
131+ @(TTable (Reify Expr))
124132 @TColumns
125− @Id
126− @Expr
133+ @TUnreify
134+ @(Col (Reify Expr))
127135 @name
128− @(Eval (rep Expr))
129− (const toColumns)
130− (\Spec {info} -> snull info)
131− (\Spec {nullity} -> case nullity of
136+ @(Eval (rep (Reify Expr)))
137+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
138+ (\SSpec {info} -> Reify (E (snull info)))
139+ (\SSpec {nullity} -> case nullity of
132140 Null -> id
133− NotNull -> nullify)
134− (HIdentity . litExpr)
141+ NotNull -> \(Reify (E a)) -> Reify (E (nullify a)))
142+ (HType . Reify . E . litExpr)
135143
136144
137145type GGConstructable :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Constraint
138146type GGConstructable algebra rep =
139147 ( KnownAlgebra algebra
140− , Eval (GGColumns algebra TColumns (Eval (rep Aggregate))) ~ Eval (GGColumns algebra TColumns (Eval (rep Expr)))
141− , Eval (GGColumns algebra TColumns (Eval (rep Expr))) ~ Eval (GGColumns algebra TColumns (Eval (rep Expr)))
142− , Eval (GGColumns algebra TColumns (Eval (rep Name))) ~ Eval (GGColumns algebra TColumns (Eval (rep Expr)))
143− , HTable (Eval (GGColumns algebra TColumns (Eval (rep Expr))))
148+ , Eval (GGColumns algebra TColumns (Eval (rep (Reify Aggregate)))) ~ Eval (GGColumns algebra TColumns (Eval (rep (Reify Result))))
149+ , Eval (GGColumns algebra TColumns (Eval (rep (Reify Expr)))) ~ Eval (GGColumns algebra TColumns (Eval (rep (Reify Result))))
150+ , Eval (GGColumns algebra TColumns (Eval (rep (Reify Name)))) ~ Eval (GGColumns algebra TColumns (Eval (rep (Reify Result))))
151+ , HTable (Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))))
144152 , GGConstructable' algebra rep
145153 )
146154
@@ -148,100 +156,105 @@ type GGConstructable algebra rep =
148156type GGConstructable' :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Constraint
149157type family GGConstructable' algebra rep where
150158 GGConstructable' 'K.Product rep =
151− ( Representable Id (Eval (rep Aggregate))
152− , Representable Id (Eval (rep Expr))
153− , Representable Id (Eval (rep Name))
154− , GConstructable (TTable Aggregate) TColumns Id Aggregate (Eval (rep Aggregate))
155− , GConstructable (TTable Expr) TColumns Id Expr (Eval (rep Expr))
156− , GConstructable (TTable Name) TColumns Id Name (Eval (rep Name))
159+ ( Representable TUnreify (Eval (rep (Reify Aggregate)))
160+ , Representable TUnreify (Eval (rep (Reify Expr)))
161+ , Representable TUnreify (Eval (rep (Reify Name)))
162+ , GConstructable (TTable (Reify Aggregate)) TColumns TUnreify (Col (Reify Aggregate)) (Eval (rep (Reify Aggregate)))
163+ , GConstructable (TTable (Reify Expr)) TColumns TUnreify (Col (Reify Expr)) (Eval (rep (Reify Expr)))
164+ , GConstructable (TTable (Reify Name)) TColumns TUnreify (Col (Reify Name)) (Eval (rep (Reify Name)))
157165 )
158166 GGConstructable' 'K.Sum rep =
159− ( RepresentableConstructors Id (Eval (rep Expr))
160− , RepresentableFields Id (Eval (rep Aggregate))
161− , RepresentableFields Id (Eval (rep Expr))
162− , RepresentableFields Id (Eval (rep Name))
163− , Functor (GConstructors Id (Eval (rep Expr)))
164− , GConstructableADT (TTable Aggregate) TColumns Id Aggregate (Eval (rep Aggregate))
165− , GConstructableADT (TTable Expr) TColumns Id Expr (Eval (rep Expr))
166− , GConstructableADT (TTable Name) TColumns Id Name (Eval (rep Name))
167+ ( RepresentableConstructors TUnreify (Eval (rep (Reify Expr)))
168+ , RepresentableFields TUnreify (Eval (rep (Reify Aggregate)))
169+ , RepresentableFields TUnreify (Eval (rep (Reify Expr)))
170+ , RepresentableFields TUnreify (Eval (rep (Reify Name)))
171+ , Functor (GConstructors TUnreify (Eval (rep (Reify Expr))))
172+ , GConstructableADT (TTable (Reify Aggregate)) TColumns TUnreify (Col (Reify Aggregate)) (Eval (rep (Reify Aggregate)))
173+ , GConstructableADT (TTable (Reify Expr)) TColumns TUnreify (Col (Reify Expr)) (Eval (rep (Reify Expr)))
174+ , GConstructableADT (TTable (Reify Name)) TColumns TUnreify (Col (Reify Name)) (Eval (rep (Reify Name)))
167175 )
168176
169177
170178type GGConstruct :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Type -> Type
171179type family GGConstruct algebra rep r where
172− GGConstruct 'K.Product rep r = GConstruct Id (Eval (rep Expr)) r -> r
173− GGConstruct 'K.Sum rep r = GConstructADT Id (Eval (rep Expr)) r r
180+ GGConstruct 'K.Product rep r =
181+ GConstruct TUnreify (Eval (rep (Reify Expr))) r -> r
182+ GGConstruct 'K.Sum rep r =
183+ GConstructADT TUnreify (Eval (rep (Reify Expr))) r r
174184
175185
176186ggconstruct :: forall algebra rep a. GGConstructable algebra rep
177− => (Eval (GGColumns algebra TColumns (Eval (rep Expr))) Expr -> a)
187+ => (Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))) (Col Expr) -> a)
178188 -> GGConstruct algebra rep a -> a
179189ggconstruct gfromColumns f = case algebraSing @algebra of
180190 SProduct ->
181191 f $
182− gtabulate @Id @(Eval (rep Expr)) @a $
192+ gtabulate @TUnreify @(Eval (rep (Reify Expr))) @a $
183193 gfromColumns .
194+ hunreify .
184195 gconstruct
185− @(TTable Expr)
196+ @(TTable (Reify Expr))
186197 @TColumns
187− @Id
188− @Expr
189− @(Eval (rep Expr))
190− (const toColumns)
198+ @TUnreify
199+ @(Col (Reify Expr))
200+ @(Eval (rep (Reify Expr)))
201+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
191202 SSum ->
192− gcindex @Id @(Eval (rep Expr)) @a f $
193− fmap gfromColumns $
203+ gcindex @TUnreify @(Eval (rep (Reify Expr))) @a f $
204+ fmap (gfromColumns . hunreify) $
194205 gconstructADT
195− @(TTable Expr)
206+ @(TTable (Reify Expr))
196207 @TColumns
197− @Id
198− @Expr
199− @(Eval (rep Expr))
200− (const toColumns)
201− (\Spec {info} -> snull info)
202− (\Spec {nullity} -> case nullity of
208+ @TUnreify
209+ @(Col (Reify Expr))
210+ @(Eval (rep (Reify Expr)))
211+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
212+ (\SSpec {info} -> Reify (E (snull info)))
213+ (\SSpec {nullity} -> case nullity of
203214 Null -> id
204− NotNull -> nullify)
205− (HIdentity . litExpr)
215+ NotNull -> \(Reify (E a)) -> Reify (E (nullify a)))
216+ (HType . Reify . E . litExpr)
206217
207218
208219type GGDeconstruct :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Type -> Type -> Type
209220type family GGDeconstruct algebra rep a r where
210221 GGDeconstruct 'K.Product rep a r =
211− GConstruct Id (Eval (rep Expr)) r -> a -> r
222+ GConstruct TUnreify (Eval (rep (Reify Expr))) r -> a -> r
212223 GGDeconstruct 'K.Sum rep a r =
213− GConstructADT Id (Eval (rep Expr)) r (a -> r)
224+ GConstructADT TUnreify (Eval (rep (Reify Expr))) r (a -> r)
214225
215226
216227ggdeconstruct :: forall algebra rep a r. (GGConstructable algebra rep, Table Expr r)
217− => (a -> Eval (GGColumns algebra TColumns (Eval (rep Expr))) Expr)
228+ => (a -> Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))) (Col Expr))
218229 -> GGDeconstruct algebra rep a r
219230ggdeconstruct gtoColumns = case algebraSing @algebra of
220231 SProduct -> \build ->
221− gindex @Id @(Eval (rep Expr)) @r build .
232+ gindex @TUnreify @(Eval (rep (Reify Expr))) @r build .
222233 gdeconstruct
223− @(TTable Expr)
234+ @(TTable (Reify Expr))
224235 @TColumns
225− @Id
226− @Expr
227− @(Eval (rep Expr))
228− (const fromColumns) .
236+ @TUnreify
237+ @(Col (Reify Expr))
238+ @(Eval (rep (Reify Expr)))
239+ (\(_ :: proxy x) -> unreify @_ @x Refl . fromColumns) .
240+ hreify .
229241 gtoColumns
230242 SSum ->
231− gctabulate @Id @(Eval (rep Expr)) @r @(a -> r) $ \constructors as ->
243+ gctabulate @TUnreify @(Eval (rep (Reify Expr))) @r @(a -> r) $ \constructors as ->
232244 let
233− (HIdentity tag, cases) =
245+ (HType (Reify (E tag)), cases) =
234246 gdeconstructADT
235− @(TTable Expr)
247+ @(TTable (Reify Expr))
236248 @TColumns
237− @Id
238− @Expr
239− @(Eval (rep Expr))
240− (const fromColumns)
241− (\Spec {nullity} -> case nullity of
249+ @TUnreify
250+ @(Col (Reify Expr))
251+ @(Eval (rep (Reify Expr)))
252+ (\(_ :: proxy x) -> unreify @_ @x Refl . fromColumns)
253+ (\SSpec {nullity} -> case nullity of
242254 Null -> id
243− NotNull -> unsafeUnnullify)
255+ NotNull -> \(Reify (E a)) -> Reify (E (unsafeUnnullify a)))
244256 constructors $
257+ hreify $
245258 gtoColumns as
246259 in
247260 case cases of
@@ -251,102 +264,108 @@ ggdeconstruct gtoColumns = case algebraSing @algebra of
251264
252265type GGName :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Type -> Type
253266type family GGName algebra rep a where
254− GGName 'K.Product rep a = GConstruct Id (Eval (rep Name)) a
255− GGName 'K.Sum rep a = Name Tag -> GBuildADT Id (Eval (rep Name)) a
267+ GGName 'K.Product rep a = GConstruct TUnreify (Eval (rep (Reify Name))) a
268+ GGName 'K.Sum rep a = Name Tag -> GBuildADT TUnreify (Eval (rep (Reify Name))) a
256269
257270
258271ggname :: forall algebra rep a. GGConstructable algebra rep
259− => (Eval (GGColumns algebra TColumns (Eval (rep Expr))) Name -> a)
272+ => (Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))) (Col Name) -> a)
260273 -> GGName algebra rep a
261274ggname gfromColumns = case algebraSing @algebra of
262275 SProduct ->
263− gtabulate @Id @(Eval (rep Name)) @a $
276+ gtabulate @TUnreify @(Eval (rep (Reify Name))) @a $
264277 gfromColumns .
278+ hunreify .
265279 gconstruct
266− @(TTable Name)
280+ @(TTable (Reify Name))
267281 @TColumns
268− @Id
269− @Name
270− @(Eval (rep Name))
271− (const toColumns)
282+ @TUnreify
283+ @(Col (Reify Name))
284+ @(Eval (rep (Reify Name)))
285+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
272286 SSum -> \tag ->
273− gftabulate @Id @(Eval (rep Name)) @a $
287+ gftabulate @TUnreify @(Eval (rep (Reify Name))) @a $
274288 gfromColumns .
289+ hunreify .
275290 gbuildADT
276− @(TTable Name)
291+ @(TTable (Reify Name))
277292 @TColumns
278− @Id
279− @Name
280− @(Eval (rep Name))
281− (const toColumns)
282− (\_ _ (Name a) -> Name a)
283− (HIdentity tag)
293+ @TUnreify
294+ @(Col (Reify Name))
295+ @(Eval (rep (Reify Name)))
296+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
297+ (\_ _ (Reify (N (Name a))) -> Reify (N (Name a)))
298+ (HType (Reify (N tag)))
284299
285300
286301type GGAggregate :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Type -> Type
287302type family GGAggregate algebra rep r where
288303 GGAggregate 'K.Product rep r =
289− GConstruct Id (Eval (rep Aggregate)) r ->
290− GConstruct Id (Eval (rep Expr)) r
304+ GConstruct TUnreify (Eval (rep (Reify Aggregate))) r ->
305+ GConstruct TUnreify (Eval (rep (Reify Expr))) r
291306 GGAggregate 'K.Sum rep r =
292− GBuildADT Id (Eval (rep Aggregate)) r ->
293− GBuildADT Id (Eval (rep Expr)) r
307+ GBuildADT TUnreify (Eval (rep (Reify Aggregate))) r ->
308+ GBuildADT TUnreify (Eval (rep (Reify Expr))) r
294309
295310
296311ggaggregate :: forall algebra rep exprs agg. GGConstructable algebra rep
297− => (Eval (GGColumns algebra TColumns (Eval (rep Expr))) Aggregate -> agg)
298− -> (exprs -> Eval (GGColumns algebra TColumns (Eval (rep Expr))) Expr)
312+ => (Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))) (Col Aggregate) -> agg)
313+ -> (exprs -> Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))) (Col Expr))
299314 -> GGAggregate algebra rep agg -> exprs -> agg
300315ggaggregate gfromColumns gtoColumns agg es = case algebraSing @algebra of
301316 SProduct -> flip f exprs $
302317 gfromColumns .
318+ hunreify .
303319 gconstruct
304− @(TTable Aggregate)
320+ @(TTable (Reify Aggregate))
305321 @TColumns
306− @Id
307− @Aggregate
308− @(Eval (rep Aggregate))
309− (const toColumns)
322+ @TUnreify
323+ @(Col (Reify Aggregate))
324+ @(Eval (rep (Reify Aggregate)))
325+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
310326 where
311327 f =
312− gindex @Id @(Eval (rep Expr)) @agg .
328+ gindex @TUnreify @(Eval (rep (Reify Expr))) @agg .
313329 agg .
314− gtabulate @Id @(Eval (rep Aggregate)) @agg
330+ gtabulate @TUnreify @(Eval (rep (Reify Aggregate))) @agg
315331 exprs =
316332 gdeconstruct
317− @(TTable Expr)
333+ @(TTable (Reify Expr))
318334 @TColumns
319− @Id
320− @Expr
321− @(Eval (rep Expr))
322− (const fromColumns) $
335+ @TUnreify
336+ @(Col (Reify Expr))
337+ @(Eval (rep (Reify Expr)))
338+ (\(_ :: proxy x) -> unreify @_ @x Refl . fromColumns) $
339+ hreify $
323340 gtoColumns es
324341 SSum -> flip f exprs $
325342 gfromColumns .
343+ hunreify .
326344 gbuildADT
327− @(TTable Aggregate)
345+ @(TTable (Reify Aggregate))
328346 @TColumns
329− @Id
330− @Aggregate
331− @(Eval (rep Aggregate))
332− (const toColumns)
333− (\tag' Spec {nullity} (Aggregate a) ->
334− Aggregate $ sguard (tag ==. litExpr tag') . snullify nullity <$> a)
335− (HIdentity (groupByExpr tag))
347+ @TUnreify
348+ @(Col (Reify Aggregate))
349+ @(Eval (rep (Reify Aggregate)))
350+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
351+ (\tag' SSpec {nullity} (Reify (A (Aggregate a))) ->
352+ Reify $ A $ Aggregate $ runTag nullity (tag ==. litExpr tag') <$> a)
353+ (HType (Reify (A (groupByExpr tag))))
336354 where
337355 f =
338− gfindex @Id @(Eval (rep Expr)) @agg .
356+ gfindex @TUnreify @(Eval (rep (Reify Expr))) @agg .
339357 agg .
340− gftabulate @Id @(Eval (rep Aggregate)) @agg
341− (HIdentity tag, exprs) =
358+ gftabulate @TUnreify @(Eval (rep (Reify Aggregate))) @agg
359+ (HType (Reify (E tag)), exprs) =
342360 gunbuildADT
343− @(TTable Expr)
361+ @(TTable (Reify Expr))
344362 @TColumns
345− @Id
346− @Expr
347− @(Eval (rep Expr))
348− (const fromColumns)
349− (\Spec {nullity} -> case nullity of
363+ @TUnreify
364+ @(Col (Reify Expr))
365+ @(Eval (rep (Reify Expr)))
366+ (\(_ :: proxy x) -> unreify @_ @x Refl . fromColumns)
367+ (\SSpec {nullity} -> case nullity of
350368 Null -> id
351− NotNull -> unsafeUnnullify) $
369+ NotNull -> \(Reify (E a)) -> Reify (E (unsafeUnnullify a))) $
370+ hreify $
352371 gtoColumns es
src/Rel8/Generic/Construction/ADT.hs+47−32
@@ -1,7 +1,6 @@
11{-# language AllowAmbiguousTypes #-}
22{-# language BlockArguments #-}
33{-# language DataKinds #-}
4−{-# language FlexibleContexts #-}
54{-# language FlexibleInstances #-}
65{-# language MultiParamTypeClasses #-}
76{-# language RankNTypes #-}
@@ -49,13 +48,14 @@ import Rel8.Generic.Construction.Record
4948 )
5049import Rel8.Generic.Table.ADT ( GColumnsADT, GColumnsADT' )
5150import Rel8.Generic.Table.Record ( GColumns )
51+import Rel8.Schema.Context.Label ( HLabelable, hlabeler, hunlabeler )
5252import Rel8.Schema.HTable ( HTable )
53−import Rel8.Schema.HTable.Identity ( HIdentity )
53+import Rel8.Schema.HTable.Identity ( HType )
5454import Rel8.Schema.HTable.Label ( HLabel, hlabel, hunlabel )
5555import Rel8.Schema.HTable.Nullify ( HNullify, hnulls, hnullify, hunnullify )
5656import Rel8.Schema.HTable.Product ( HProduct( HProduct ) )
5757import Rel8.Schema.Null ( Nullify )
58−import Rel8.Schema.Spec ( Spec )
58+import Rel8.Schema.Spec ( Spec( Spec ), SSpec )
5959import qualified Rel8.Schema.Kind as K
6060import Rel8.Type.Tag ( Tag( Tag ) )
6161
@@ -63,16 +63,24 @@ import Rel8.Type.Tag ( Tag( Tag ) )
6363import Data.Text ( pack )
6464
6565
66−type Null :: K.Context -> Type
67−type Null context = forall a. Spec a -> context (Nullify a)
66+type Null :: K.HContext -> Type
67+type Null context = forall labels a. ()
68+ => SSpec ('Spec labels a)
69+ -> context ('Spec labels (Nullify a))
6870
6971
70−type Nullifier :: K.Context -> Type
71−type Nullifier context = forall a. Spec a -> context a -> context (Nullify a)
72+type Nullifier :: K.HContext -> Type
73+type Nullifier context = forall labels a. ()
74+ => SSpec ('Spec labels a)
75+ -> context ('Spec labels a)
76+ -> context ('Spec labels (Nullify a))
7277
7378
74−type Unnullifier :: K.Context -> Type
75−type Unnullifier context = forall a. Spec a -> context (Nullify a) -> context a
79+type Unnullifier :: K.HContext -> Type
80+type Unnullifier context = forall labels a. ()
81+ => SSpec ('Spec labels a)
82+ -> context ('Spec labels (Nullify a))
83+ -> context ('Spec labels a)
7684
7785
7886type NoConstructor :: Symbol -> Symbol -> ErrorMessage
@@ -181,12 +189,12 @@ type GConstructableADT
181189 :: (Type -> Exp Constraint)
182190 -> (Type -> Exp K.HTable)
183191 -> (Type -> Exp Type)
184− -> K.Context -> (Type -> Type) -> Constraint
192+ -> K.HContext -> (Type -> Type) -> Constraint
185193class GConstructableADT _Table _Columns f context rep where
186194 gbuildADT :: ()
187195 => ToColumns _Table _Columns f context
188196 -> (Tag -> Nullifier context)
189− -> HIdentity Tag context
197+ -> HType Tag context
190198 -> GFieldsADT f rep
191199 -> GColumnsADT _Columns rep context
192200
@@ -194,13 +202,13 @@ class GConstructableADT _Table _Columns f context rep where
194202 => FromColumns _Table _Columns f context
195203 -> Unnullifier context
196204 -> GColumnsADT _Columns rep context
197− -> (HIdentity Tag context, GFieldsADT f rep)
205+ -> (HType Tag context, GFieldsADT f rep)
198206
199207 gconstructADT :: ()
200208 => ToColumns _Table _Columns f context
201209 -> Null context
202210 -> Nullifier context
203− -> (Tag -> HIdentity Tag context)
211+ -> (Tag -> HType Tag context)
204212 -> GConstructors f rep (GColumnsADT _Columns rep context)
205213
206214 gdeconstructADT :: ()
@@ -208,29 +216,30 @@ class GConstructableADT _Table _Columns f context rep where
208216 -> Unnullifier context
209217 -> GConstructors f rep r
210218 -> GColumnsADT _Columns rep context
211− -> (HIdentity Tag context, NonEmpty (Tag, r))
219+ -> (HType Tag context, NonEmpty (Tag, r))
212220
213221
214222instance
215− ( htable ~ HLabel "tag" (HIdentity Tag)
223+ ( htable ~ HLabel "tag" (HType Tag)
216224 , GConstructableADT' _Table _Columns f context htable rep
225+ , HLabelable context
217226 )
218227 => GConstructableADT _Table _Columns f context (M1 D meta rep)
219228 where
220229 gbuildADT toColumns nullifier =
221230 gbuildADT' @_Table @_Columns @f @context @htable @rep toColumns nullifier .
222− hlabel
231+ hlabel hlabeler
223232
224233 gunbuildADT fromColumns unnullifier =
225− first hunlabel .
234+ first (hunlabel hunlabeler) .
226235 gunbuildADT' @_Table @_Columns @f @context @htable @rep fromColumns unnullifier
227236
228237 gconstructADT toColumns null nullifier mk =
229238 gconstructADT' @_Table @_Columns @f @context @htable @rep toColumns null nullifier
230− (hlabel . mk)
239+ (hlabel hlabeler . mk)
231240
232241 gdeconstructADT fromColumns unnullifier cases =
233− first hunlabel .
242+ first (hunlabel hunlabeler) .
234243 gdeconstructADT' @_Table @_Columns @f @context @htable @rep fromColumns unnullifier cases
235244
236245
@@ -238,7 +247,7 @@ type GConstructableADT'
238247 :: (Type -> Exp Constraint)
239248 -> (Type -> Exp K.HTable)
240249 -> (Type -> Exp Type)
241− -> K.Context -> K.HTable -> (Type -> Type) -> Constraint
250+ -> K.HContext -> K.HTable -> (Type -> Type) -> Constraint
242251class GConstructableADT' _Table _Columns f context htable rep where
243252 gbuildADT' :: ()
244253 => ToColumns _Table _Columns f context
@@ -325,6 +334,7 @@ instance {-# OVERLAPPABLE #-}
325334 ( HTable (GColumns _Columns rep)
326335 , KnownSymbol label
327336 , meta ~ 'MetaCons label _fixity _isRecord
337+ , HLabelable context
328338 , GConstructable _Table _Columns f context rep
329339 , GColumnsADT' _Columns htable (M1 C meta rep) ~
330340 HProduct htable (HLabel label (HNullify (GColumns _Columns rep)))
@@ -333,7 +343,7 @@ instance {-# OVERLAPPABLE #-}
333343 where
334344 gbuildADT' toColumns nullifier htable =
335345 HProduct htable .
336− hlabel .
346+ hlabel hlabeler .
337347 hnullify (nullifier tag) .
338348 gconstruct @_Table @_Columns @f @context @rep toColumns
339349 where
@@ -344,13 +354,13 @@ instance {-# OVERLAPPABLE #-}
344354 , gdeconstruct @_Table @_Columns @f @context @rep fromColumns $
345355 runIdentity $
346356 hunnullify (\spec -> pure . unnullifier spec) $
347− hunlabel
357+ hunlabel hunlabeler
348358 a
349359 )
350360
351361 gconstructADT' toColumns _ nullifier mk =
352362 HProduct htable .
353− hlabel .
363+ hlabel hlabeler .
354364 hnullify nullifier .
355365 gconstruct @_Table @_Columns @f @context @rep toColumns
356366 where
@@ -365,34 +375,35 @@ instance {-# OVERLAPPABLE #-}
365375 a = gdeconstruct @_Table @_Columns @f @context @rep fromColumns $
366376 runIdentity $
367377 hunnullify (\spec -> pure . unnullifier spec) $
368− hunlabel
378+ hunlabel hunlabeler
369379 columns
370380 tag = Tag $ pack $ symbolVal (Proxy @label)
371381
372− gfill null htable = HProduct htable (hlabel (hnulls null))
382+ gfill null htable = HProduct htable (hlabel hlabeler (hnulls null))
373383
374384
375385type GMakeableADT
376386 :: (Type -> Exp Constraint)
377387 -> (Type -> Exp K.HTable)
378388 -> (Type -> Exp Type)
379− -> K.Context -> Symbol -> (Type -> Type) -> Constraint
389+ -> K.HContext -> Symbol -> (Type -> Type) -> Constraint
380390class GMakeableADT _Table _Columns f context name rep where
381391 gmakeADT :: ()
382392 => ToColumns _Table _Columns f context
383393 -> Null context
384394 -> Nullifier context
385− -> (Tag -> HIdentity Tag context)
395+ -> (Tag -> HType Tag context)
386396 -> GFields f (GConstructorADT name rep)
387397 -> GColumnsADT _Columns rep context
388398
389399
390400instance
391− ( htable ~ HLabel "tag" (HIdentity Tag)
401+ ( htable ~ HLabel "tag" (HType Tag)
392402 , meta ~ 'MetaData datatype _module _package _newtype
393403 , fallback ~ TypeError (NoConstructor datatype name)
394404 , fields ~ GFields f (GConstructorADT' name rep fallback)
395405 , GMakeableADT' _Table _Columns f context htable name rep fields
406+ , HLabelable context
396407 , KnownSymbol name
397408 )
398409 => GMakeableADT _Table _Columns f context name (M1 D meta rep)
@@ -403,14 +414,14 @@ instance
403414 toColumns null nullifier htable
404415 where
405416 tag = Tag $ pack $ symbolVal (Proxy @name)
406− htable = hlabel (wrap tag)
417+ htable = hlabel hlabeler (wrap tag)
407418
408419
409420type GMakeableADT'
410421 :: (Type -> Exp Constraint)
411422 -> (Type -> Exp K.HTable)
412423 -> (Type -> Exp Type)
413− -> K.Context -> K.HTable -> Symbol -> (Type -> Type) -> Type -> Constraint
424+ -> K.HContext -> K.HTable -> Symbol -> (Type -> Type) -> Type -> Constraint
414425class GMakeableADT' _Table _Columns f context htable name rep fields where
415426 gmakeADT' :: ()
416427 => ToColumns _Table _Columns f context
@@ -451,6 +462,8 @@ instance {-# OVERLAPS #-}
451462
452463instance {-# OVERLAPS #-}
453464 ( HTable (GColumns _Columns rep)
465+ , KnownSymbol name
466+ , HLabelable context
454467 , GConstructable _Table _Columns f context rep
455468 , fields ~ GFields f rep
456469 , GColumnsADT' _Columns htable (M1 C ('MetaCons name _fixity _isRecord) rep) ~
@@ -460,13 +473,15 @@ instance {-# OVERLAPS #-}
460473 where
461474 gmakeADT' toColumns _ nullifier htable =
462475 HProduct htable .
463− hlabel .
476+ hlabel hlabeler .
464477 hnullify nullifier .
465478 gconstruct @_Table @_Columns @f @context @rep toColumns
466479
467480
468481instance {-# OVERLAPPABLE #-}
469482 ( HTable (GColumns _Columns rep)
483+ , KnownSymbol label
484+ , HLabelable context
470485 , GColumnsADT' _Columns htable (M1 C ('MetaCons label _fixity _isRecord) rep) ~
471486 HProduct htable (HLabel label (HNullify (GColumns _Columns rep)))
472487 )
@@ -474,5 +489,5 @@ instance {-# OVERLAPPABLE #-}
474489 where
475490 gmakeADT' _ null _ htable _ =
476491 HProduct htable $
477− hlabel $
492+ hlabel hlabeler $
478493 hnulls null
src/Rel8/Generic/Construction/Record.hs+11−6
@@ -27,13 +27,15 @@ import GHC.Generics
2727 )
2828import GHC.TypeLits
2929 ( ErrorMessage( (:<>:), Text ), TypeError
30− , Symbol
30+ , Symbol, KnownSymbol
3131 )
3232import Prelude
3333
3434-- rel8
3535import Rel8.FCF ( Eval, Exp )
3636import Rel8.Generic.Table.Record ( GColumns )
37+import Rel8.Schema.Context.Label ( HLabelable, hlabeler, hunlabeler )
38+import Rel8.Schema.HTable ( HTable )
3739import Rel8.Schema.HTable.Label ( hlabel, hunlabel )
3840import Rel8.Schema.HTable.Product ( HProduct( HProduct ) )
3941import qualified Rel8.Schema.Kind as K
@@ -43,7 +45,7 @@ type FromColumns
4345 :: (Type -> Exp Constraint)
4446 -> (Type -> Exp K.HTable)
4547 -> (Type -> Exp Type)
46− -> K.Context
48+ -> K.HContext
4749 -> Type
4850type FromColumns _Table _Columns f context = forall proxy x.
4951 Eval (_Table x) => proxy x -> Eval (_Columns x) context -> Eval (f x)
@@ -53,7 +55,7 @@ type ToColumns
5355 :: (Type -> Exp Constraint)
5456 -> (Type -> Exp K.HTable)
5557 -> (Type -> Exp Type)
56− -> K.Context
58+ -> K.HContext
5759 -> Type
5860type ToColumns _Table _Columns f context = forall proxy x.
5961 Eval (_Table x) => proxy x -> Eval (f x) -> Eval (_Columns x) context
@@ -117,7 +119,7 @@ type GConstructable
117119 :: (Type -> Exp Constraint)
118120 -> (Type -> Exp K.HTable)
119121 -> (Type -> Exp Type)
120− -> K.Context -> (Type -> Type) -> Constraint
122+ -> K.HContext -> (Type -> Type) -> Constraint
121123class GConstructable _Table _Columns f context rep where
122124 gconstruct :: ()
123125 => ToColumns _Table _Columns f context
@@ -160,9 +162,12 @@ instance
160162
161163instance
162164 ( Eval (_Table a)
165+ , HTable (Eval (_Columns a))
166+ , HLabelable context
167+ , KnownSymbol label
163168 , meta ~ 'MetaSel ('Just label) _su _ss _ds
164169 )
165170 => GConstructable _Table _Columns f context (M1 S meta (K1 i a))
166171 where
167− gconstruct toColumns = hlabel . toColumns (Proxy @a)
168− gdeconstruct fromColumns = fromColumns (Proxy @a) . hunlabel
172+ gconstruct toColumns = hlabel hlabeler . toColumns (Proxy @a)
173+ gdeconstruct fromColumns = fromColumns (Proxy @a) . hunlabel hunlabeler
src/Rel8/Generic/Map.hs+64−20
@@ -1,19 +1,26 @@
1+{-# language AllowAmbiguousTypes #-}
12{-# language DataKinds #-}
3+{-# language EmptyCase #-}
4+{-# language FlexibleInstances #-}
5+{-# language LambdaCase #-}
6+{-# language MultiParamTypeClasses #-}
7+{-# language RankNTypes #-}
8+{-# language ScopedTypeVariables #-}
29{-# language StandaloneKindSignatures #-}
10+{-# language TypeApplications #-}
311{-# language TypeFamilies #-}
412{-# language TypeOperators #-}
513{-# language UndecidableInstances #-}
614
715module Rel8.Generic.Map
8− ( GMap
9− , Map
16+ ( GMap, GMappable (gmap, gunmap)
1017 )
1118where
1219
1320-- base
14−import Data.Kind ( Type )
21+import Data.Kind ( Constraint, Type )
1522import GHC.Generics
16− ( (:+:), (:*:), K1, M1, U1, V1
23+ ( (:+:)( L1, R1 ), (:*:)( (:*:) ), K1( K1 ), M1( M1 ), U1( U1 ), V1
1724 )
1825import Prelude ()
1926
@@ -31,19 +38,56 @@ type family GMap f rep where
3138 GMap f (K1 i a) = K1 i (Eval (f a))
3239
3340
34−-- | Map a @Type -> Type@ function over the @Type@-kinded type variables in
35−-- of a type constructor.
36−type Map :: (Type -> Exp Type) -> Type -> Type
37−type family Map f a where
38− Map p (t a b c d e f g) =
39− t (Eval (p a)) (Eval (p b)) (Eval (p c)) (Eval (p d)) (Eval (p e))
40− (Eval (p f)) (Eval (p g))
41− Map p (t a b c d e f) =
42− t (Eval (p a)) (Eval (p b)) (Eval (p c)) (Eval (p d)) (Eval (p e))
43− (Eval (p f))
44− Map p (t a b c d e) =
45− t (Eval (p a)) (Eval (p b)) (Eval (p c)) (Eval (p d)) (Eval (p e))
46− Map p (t a b c d) = t (Eval (p a)) (Eval (p b)) (Eval (p c)) (Eval (p d))
47− Map p (t a b c) = t (Eval (p a)) (Eval (p b)) (Eval (p c))
48− Map p (t a b) = t (Eval (p a)) (Eval (p b))
49− Map p (t a) = t (Eval (p a))
41+type GMappable :: (Type -> Exp Constraint) -> (Type -> Type) -> Constraint
42+class GMappable constraint rep where
43+ gmap :: ()
44+ => proxy f
45+ -> (forall a. Eval (constraint a) => a -> Eval (f a))
46+ -> rep x
47+ -> GMap f rep x
48+
49+ gunmap :: ()
50+ => proxy f
51+ -> (forall a. Eval (constraint a) => Eval (f a) -> a)
52+ -> GMap f rep x
53+ -> rep x
54+
55+
56+instance GMappable constraint rep => GMappable constraint (M1 i c rep) where
57+ gmap proxy f (M1 a) = M1 (gmap @constraint proxy f a)
58+ gunmap proxy f (M1 a) = M1 (gunmap @constraint proxy f a)
59+
60+
61+instance GMappable constraint V1 where
62+ gmap _ _ = \case
63+ gunmap _ _ = \case
64+
65+
66+instance (GMappable constraint rep1, GMappable constraint rep2) =>
67+ GMappable constraint (rep1 :+: rep2)
68+ where
69+ gmap proxy f = \case
70+ L1 a -> L1 (gmap @constraint proxy f a)
71+ R1 a -> R1 (gmap @constraint proxy f a)
72+ gunmap proxy f = \case
73+ L1 a -> L1 (gunmap @constraint proxy f a)
74+ R1 a -> R1 (gunmap @constraint proxy f a)
75+
76+
77+instance GMappable constraint U1 where
78+ gmap _ _ U1 = U1
79+ gunmap _ _ U1 = U1
80+
81+
82+instance (GMappable constraint rep1, GMappable constraint rep2) =>
83+ GMappable constraint (rep1 :*: rep2)
84+ where
85+ gmap proxy f (a :*: b) =
86+ gmap @constraint proxy f a :*: gmap @constraint proxy f b
87+ gunmap proxy f (a :*: b) =
88+ gunmap @constraint proxy f a :*: gunmap @constraint proxy f b
89+
90+
91+instance Eval (constraint a) => GMappable constraint (K1 i a) where
92+ gmap _ f (K1 a) = K1 (f a)
93+ gunmap _ f (K1 a) = K1 (f a)
src/Rel8/Generic/Record.hs−2
@@ -1,6 +1,5 @@
11{-# language AllowAmbiguousTypes #-}
22{-# language DataKinds #-}
3−{-# language FlexibleContexts #-}
43{-# language FlexibleInstances #-}
54{-# language MultiParamTypeClasses #-}
65{-# language PolyKinds #-}
@@ -142,7 +141,6 @@ instance {-# OVERLAPPABLE #-} Snd (Count n rep) ~ rep => Countable n rep where
142141 uncount = id
143142
144143
145−type Record :: Type -> Type
146144newtype Record a = Record
147145 { unrecord :: a
148146 }
src/Rel8/Generic/Reify.hsnew file+24
@@ -0,0 +1,24 @@
1+{-# language StandaloneKindSignatures #-}
2+{-# language TypeFamilies #-}
3+
4+module Rel8.Generic.Reify
5+ ( ARep
6+ )
7+where
8+
9+-- base
10+import Data.Functor.Contravariant ( Contravariant, contramap )
11+import Data.Kind ( Type )
12+import Data.Void ( Void, absurd )
13+import GHC.Generics ( Generic, Rep, from, to )
14+import Prelude
15+
16+
17+type ARep :: (Type -> Type) -> Type
18+newtype ARep rep = ARep (rep Void)
19+
20+
21+instance (Contravariant rep, Functor rep) => Generic (ARep rep) where
22+ type Rep (ARep rep) = rep
23+ from (ARep a) = fmap absurd a
24+ to = ARep . contramap absurd
src/Rel8/Generic/Rel8able.hs+72−178
@@ -1,18 +1,13 @@
11{-# language AllowAmbiguousTypes #-}
2−{-# language ConstraintKinds #-}
32{-# language DataKinds #-}
43{-# language DefaultSignatures #-}
54{-# language FlexibleContexts #-}
65{-# language FlexibleInstances #-}
7−{-# language LambdaCase #-}
86{-# language MultiParamTypeClasses #-}
9−{-# language PolyKinds #-}
10−{-# language QuantifiedConstraints #-}
117{-# language ScopedTypeVariables #-}
128{-# language StandaloneKindSignatures #-}
139{-# language TypeApplications #-}
14−{-# language TypeFamilyDependencies #-}
15−{-# language TypeOperators #-}
10+{-# language TypeFamilies #-}
1611{-# language UndecidableInstances #-}
1712
1813module Rel8.Generic.Rel8able
@@ -20,39 +15,36 @@ module Rel8.Generic.Rel8able
2015 , Algebra
2116 , GRep
2217 , GColumns, gfromColumns, gtoColumns
23− , GFromExprs, gfromResult, gtoResult
24− , TSerialize, serialize, deserialize
18+ , greify, gunreify
19+ , TUnreifyContext
2520 )
2621where
2722
2823-- base
29−import Data.Functor.Identity ( Identity )
3024import Data.Kind ( Constraint, Type )
31−import Data.Type.Bool ( type (&&) )
25+import Data.Proxy ( Proxy( Proxy ) )
26+import Data.Type.Equality ( (:~:)( Refl ) )
3227import GHC.Generics ( Generic, Rep, from, to )
3328import Prelude
3429
3530-- rel8
36−import Rel8.Aggregate ( Aggregate )
37−import Rel8.Expr ( Expr )
38−import Rel8.FCF ( Exp, Eval )
31+import Rel8.Kind.Context ( Reifiable )
32+import Rel8.FCF ( Eval, Exp )
33+import Rel8.Generic.Map ( GMap, GMappable, gmap, gunmap )
3934import Rel8.Generic.Record ( Record(..) )
4035import Rel8.Generic.Table ( GAlgebra )
4136import qualified Rel8.Generic.Table.Record as G
4237import qualified Rel8.Kind.Algebra as K ( Algebra(..) )
43−import Rel8.Kind.Context ( SContext(..) )
44−import Rel8.Schema.Field ( Field )
38+import Rel8.Schema.Context ( Col )
39+import Rel8.Schema.Context.Label ( Labelable )
4540import Rel8.Schema.HTable ( HTable )
4641import qualified Rel8.Schema.Kind as K
47−import Rel8.Schema.Name ( Name )
42+import Rel8.Schema.Reify ( Reify, UnwrapReify )
4843import Rel8.Schema.Result ( Result )
4944import Rel8.Table
50− ( Table, Columns, Context, fromColumns, toColumns
51− , FromExprs, fromResult, toResult
52− , Transpose
53− , TTable, TColumns
45+ ( fromColumns, toColumns, reify, unreify
46+ , TTable, TColumns, TContext, TUnreify
5447 )
55−import Rel8.Table.Transpose ( Transposes )
5648
5749
5850-- | The kind of 'Rel8able' types
@@ -60,59 +52,6 @@ type KRel8able :: Type
6052type KRel8able = K.Rel8able
6153
6254
63−-- This is almost 'Data.Type.Equality.==', but we add an extra case.
64−type (==) :: k -> k -> Bool
65−type family a == b where
66− -- This extra case is needed to solve the equation "a == a" (where a is a
67− -- KRel8able), which occurs when we have polymorphic Rel8ables
68− -- (e.g., newtype T t f = T { x :: t a })
69− (a :: KRel8able) == (a :: KRel8able) = 'True
70−
71− -- This extra case is needed to solve the equation "a == Identity a",
72− -- which occurs when we have polymorphic Rel8ables
73− -- (e.g., newtype T a f = T { x :: Column f a })
74− a == Identity a = 'False
75−
76− -- These cases are exactly the same as those in 'Data.Type.Equality.==.
77− f a == g b = f == g && a == b
78− a == a = 'True
79− _ == _ = 'False
80−
81−
82−type Serialize :: Bool -> Type -> Type -> Constraint
83−class transposition ~ (a == Transpose Result expr) =>
84− Serialize transposition expr a
85− where
86− serialize :: a -> Columns expr Result
87− deserialize :: Columns expr Result -> a
88−
89−
90−instance
91− ( (a == Transpose Result expr) ~ 'True
92− , Transposes Expr Result expr a
93− )
94− => Serialize 'True expr a
95− where
96− serialize = toColumns
97− deserialize = fromColumns
98−
99−
100−instance
101− ( (a == Transpose Result expr) ~ 'False
102− , Table (Context expr) expr
103− , FromExprs expr ~ a
104− )
105− => Serialize 'False expr a
106− where
107− serialize = toResult @_ @expr
108− deserialize = fromResult @_ @expr
109−
110−
111−data TSerialize :: Type -> Type -> Exp Constraint
112−type instance Eval (TSerialize expr a) =
113− Serialize (a == Transpose Result expr) expr a
114−
115−
11655-- | This type class allows you to define custom 'Table's using higher-kinded
11756-- data types. Higher-kinded data types are data types of the pattern:
11857--
@@ -146,7 +85,7 @@ type instance Eval (TSerialize expr a) =
14685-- [Supplying @Rel8able@ instances]
14786--
14887-- This type class should be derived generically for all table types in your
149−-- project. To do this, enable the @DeriveAnyClass@ and @DeriveGeneric@ language
88+-- project. To do this, enable the @DeriveAnyType@ and @DeriveGeneric@ language
15089-- extensions:
15190--
15291-- @
@@ -158,124 +97,79 @@ type instance Eval (TSerialize expr a) =
15897type Rel8able :: K.Rel8able -> Constraint
15998class HTable (GColumns t) => Rel8able t where
16099 type GColumns t :: K.HTable
161− type GFromExprs t :: Type
162100
163− gfromColumns :: SContext context -> GColumns t context -> t context
164− gtoColumns :: SContext context -> t context -> GColumns t context
101+ gfromColumns :: (Labelable context, Reifiable context)
102+ => GColumns t (Col (Reify context)) -> t (Reify context)
103+
104+ gtoColumns :: (Labelable context, Reifiable context)
105+ => t (Reify context) -> GColumns t (Col (Reify context))
165106
166− gfromResult :: GColumns t Result -> GFromExprs t
167− gtoResult :: GFromExprs t -> GColumns t Result
107+ greify :: (Labelable context, Reifiable context)
108+ => t context -> t (Reify context)
168109
169− type GColumns t = G.GColumns TColumns (GRep t Expr)
170− type GFromExprs t = t Result
110+ gunreify :: (Labelable context, Reifiable context)
111+ => t (Reify context) -> t context
112+
113+ type GColumns t = G.GColumns TColumns (GRep t (Reify Result))
171114
172115 default gfromColumns :: forall context.
173− ( SRel8able t Aggregate
174− , SRel8able t Expr
175− , forall table. SRel8able t (Field table)
176− , SRel8able t Name
177− , SSerialize t
116+ ( Generic (Record (t (Reify context)))
117+ , G.GTable (TTable (Reify context)) TColumns (Col (Reify context)) (GRep t (Reify context))
118+ , G.GColumns TColumns (GRep t (Reify context)) ~ GColumns t
178119 )
179− => SContext context -> GColumns t context -> t context
180− gfromColumns = \case
181− SAggregate -> sfromColumns
182− SExpr -> sfromColumns
183− SField -> sfromColumns
184− SName -> sfromColumns
185− SResult -> sfromResult
120+ => GColumns t (Col (Reify context)) -> t (Reify context)
121+ gfromColumns =
122+ unrecord .
123+ to .
124+ G.gfromColumns @(TTable (Reify context)) @TColumns fromColumns
186125
187126 default gtoColumns :: forall context.
188− ( SRel8able t Aggregate
189− , SRel8able t Expr
190− , forall table. SRel8able t (Field table)
191− , SRel8able t Name
192− , SSerialize t
127+ ( Generic (Record (t (Reify context)))
128+ , G.GTable (TTable (Reify context)) TColumns (Col (Reify context)) (GRep t (Reify context))
129+ , G.GColumns TColumns (GRep t (Reify context)) ~ GColumns t
193130 )
194− => SContext context -> t context -> GColumns t context
195− gtoColumns = \case
196− SAggregate -> stoColumns
197− SExpr -> stoColumns
198− SField -> stoColumns
199− SName -> stoColumns
200− SResult -> stoResult
201−
202− default gfromResult :: (SSerialize t, GFromExprs t ~ t Result)
203− => GColumns t Result -> GFromExprs t
204− gfromResult = sfromResult
205−
206− default gtoResult :: (SSerialize t, GFromExprs t ~ t Result)
207− => GFromExprs t -> GColumns t Result
208− gtoResult = stoResult
131+ => t (Reify context) -> GColumns t (Col (Reify context))
132+ gtoColumns =
133+ G.gtoColumns @(TTable (Reify context)) @TColumns toColumns .
134+ from .
135+ Record
136+
137+ default greify :: forall context.
138+ ( Generic (Record (t context))
139+ , Generic (Record (t (Reify context)))
140+ , GMappable (TTable (Reify context)) (GRep t (Reify context))
141+ , GRep t context ~ GMap TUnreify (GRep t (Reify context))
142+ )
143+ => t context -> t (Reify context)
144+ greify =
145+ unrecord .
146+ to .
147+ gunmap @(TTable (Reify context)) (Proxy @TUnreify) (reify Refl) .
148+ from .
149+ Record
150+
151+ default gunreify :: forall context.
152+ ( Generic (Record (t context))
153+ , Generic (Record (t (Reify context)))
154+ , GMappable (TTable (Reify context)) (GRep t (Reify context))
155+ , GRep t context ~ GMap TUnreify (GRep t (Reify context))
156+ )
157+ => t (Reify context) -> t context
158+ gunreify =
159+ unrecord .
160+ to .
161+ gmap @(TTable (Reify context)) (Proxy @TUnreify) (unreify Refl) .
162+ from .
163+ Record
209164
210165
211166type Algebra :: K.Rel8able -> K.Algebra
212−type Algebra t = GAlgebra (GRep t Expr)
167+type Algebra t = GAlgebra (GRep t (Reify Result))
213168
214169
215170type GRep :: K.Rel8able -> K.Context -> Type -> Type
216171type GRep t context = Rep (Record (t context))
217172
218173
219−type SRel8able :: K.Rel8able -> K.Context -> Constraint
220−class
221− ( Generic (Record (t context))
222− , G.GTable (TTable context) TColumns (GRep t context)
223− , G.GColumns TColumns (GRep t context) ~ GColumns t
224− )
225− => SRel8able t context
226−instance
227− ( Generic (Record (t context))
228− , G.GTable (TTable context) TColumns (GRep t context)
229− , G.GColumns TColumns (GRep t context) ~ GColumns t
230− )
231− => SRel8able t context
232−
233−
234−type SSerialize :: K.Rel8able -> Constraint
235−type SSerialize t =
236− ( Generic (Record (t Result))
237− , G.GSerialize TSerialize TColumns (GRep t Expr) (GRep t Result)
238− , G.GColumns TColumns (GRep t Expr) ~ GColumns t
239− )
240−
241−
242−sfromColumns :: forall t context. SRel8able t context
243− => GColumns t context -> t context
244−sfromColumns =
245− unrecord .
246− to .
247− G.gfromColumns @(TTable context) @TColumns fromColumns
248−
249−
250−stoColumns :: forall t context. SRel8able t context
251− => t context -> GColumns t context
252−stoColumns =
253− G.gtoColumns @(TTable context) @TColumns toColumns .
254− from .
255− Record
256−
257−
258−sfromResult :: forall t. SSerialize t
259− => GColumns t Result -> t Result
260−sfromResult =
261− unrecord .
262− to .
263− G.gfromResult
264− @TSerialize
265− @TColumns
266− @(GRep t Expr)
267− @(GRep t Result)
268− (\(_ :: proxy x) -> deserialize @_ @x)
269−
270−
271−stoResult :: forall t. SSerialize t
272− => t Result -> GColumns t Result
273−stoResult =
274− G.gtoResult
275− @TSerialize
276− @TColumns
277− @(GRep t Expr)
278− @(GRep t Result)
279− (\(_ :: proxy x) -> serialize @_ @x) .
280− from .
281− Record
174+data TUnreifyContext :: Type -> Exp K.Context
175+type instance Eval (TUnreifyContext a) = UnwrapReify (Eval (TContext a))
tests/Rel8/Generic/Rel8able/Test.hs → src/Rel8/Generic/Rel8able/Test.hsrenamed+16−47
@@ -1,11 +1,11 @@
11{-# language DataKinds #-}
22{-# language DeriveAnyClass #-}
33{-# language DeriveGeneric #-}
4−{-# language DerivingStrategies #-}
4+{-# language DerivingVia #-}
55{-# language DuplicateRecordFields #-}
66{-# language FlexibleInstances #-}
77{-# language MultiParamTypeClasses #-}
8−{-# language StandaloneKindSignatures #-}
8+{-# language StandaloneDeriving #-}
99{-# language TypeFamilies #-}
1010{-# language UndecidableInstances #-}
1111
@@ -98,20 +98,30 @@ data S3Object = S3Object
9898 deriving stock Generic
9999
100100
101−instance x ~ HKD S3Object Expr => ToExprs x S3Object
101+deriving via HKDT S3Object
102+ instance Table Result S3Object
103+
104+
105+deriving via HKDT S3Object
106+ instance x ~ HKD S3Object Expr => ToExprs x S3Object
102107
103108
104109data HKDSum = HKDSumA Text | HKDSumB Bool Char | HKDSumC
105110 deriving stock Generic
106111
107112
108−instance x ~ HKD HKDSum Expr => ToExprs x HKDSum
113+deriving via HKDT HKDSum
114+ instance Table Result HKDSum
115+
116+
117+deriving via HKDT HKDSum
118+ instance x ~ HKD HKDSum Expr => ToExprs x HKDSum
109119
110120
111121data HKDTest f = HKDTest
112122 { s3Object :: Lift f S3Object
113123 , hkdSum :: Lift f HKDSum
114− }
124+ }
115125 deriving stock Generic
116126 deriving anyclass Rel8able
117127
@@ -138,51 +148,10 @@ data TableSum f
138148 deriving stock Generic
139149
140150
141−data BarbieSum f
142− = BarbieSumA (f Bool) (f Text)
143− | BarbieSumB
144− | BarbieSumC (f Text)
145− deriving stock Generic
146−
147−
148151data TableProduct f = TableProduct
149− { sum :: HADT f BarbieSum
152+ { sum :: HADT f TableSum
150153 , list :: TableList f
151154 , foos :: HList f (HADT f TableSum, Lift f HKDSum, HKDTest f)
152155 }
153156 deriving stock Generic
154157 deriving anyclass Rel8able
155−
156−
157−data TableTestB f = TableTestB
158− { foo :: f Bool
159− , bar :: f (Maybe Bool)
160− }
161− deriving stock Generic
162− deriving anyclass Rel8able
163−
164−
165−data NestedTableTestB f = NestedTableTestB
166− { foo :: f Bool
167− , bar :: f (Maybe Bool)
168− , baz :: Column f Char
169− , nest :: TableTestB f
170− }
171− deriving stock Generic
172− deriving anyclass Rel8able
173−
174−
175−newtype IdRecord a f = IdRecord { recordId :: Column f a }
176− deriving stock Generic
177−
178−
179−instance DBType a => Rel8able (IdRecord a)
180−
181−
182−type Nest :: KRel8able -> KRel8able -> KRel8able
183−data Nest t u f = Nest
184− { foo :: t f
185− , bar :: u f
186− }
187− deriving stock Generic
188− deriving anyclass Rel8able
src/Rel8/Generic/Table.hs+109−24
@@ -12,7 +12,8 @@
1212{-# language UndecidableInstances #-}
1313
1414module Rel8.Generic.Table
15− ( GGSerialize, GGColumns, ggfromResult, ggtoResult
15+ ( GGTable, GGColumns, GGContext, ggfromColumns, ggtoColumns, ggtable
16+ , GGToExprs, ggfromResult, ggtoResult
1617 , GAlgebra
1718 )
1819where
@@ -25,19 +26,43 @@ import Prelude ()
2526-- rel8
2627import Rel8.FCF ( Eval, Exp )
2728import Rel8.Generic.Table.ADT
28− ( GSerializeADT, GColumnsADT, gtoResultADT, gfromResultADT
29+ ( GTableADT, GColumnsADT, gfromColumnsADT, gtoColumnsADT, gtableADT
30+ , GToExprsADT, gtoResultADT, gfromResultADT
31+ )
32+import Rel8.Generic.Table.Record
33+ ( GTable, GColumns, GContext, gfromColumns, gtoColumns, gtable
34+ , GToExprs, gtoResult, gfromResult
2935 )
30−import Rel8.Generic.Table.Record ( GSerialize, GColumns, gtoResult, gfromResult )
3136import Rel8.Kind.Algebra
3237 ( Algebra( Product, Sum )
3338 , SAlgebra( SProduct, SSum )
3439 , KnownAlgebra, algebraSing
3540 )
41+import Rel8.Schema.Context ( Col )
3642import qualified Rel8.Schema.Kind as K
43+import Rel8.Schema.Null ( Nullify )
44+import Rel8.Schema.Spec ( Spec( Spec ), SSpec )
3745import Rel8.Schema.Result ( Result )
3846
3947
40−data GGSerialize
48+data GGTable
49+ :: Algebra
50+ -> (Type -> Exp Constraint)
51+ -> (Type -> Exp K.HTable)
52+ -> K.HContext
53+ -> (Type -> Type)
54+ -> Exp Constraint
55+
56+
57+type instance Eval (GGTable 'Product _Table _Columns context rep) =
58+ GTable _Table _Columns context rep
59+
60+
61+type instance Eval (GGTable 'Sum _Table _Columns context rep) =
62+ GTableADT _Table _Columns context rep
63+
64+
65+data GGToExprs
4166 :: Algebra
4267 -> (Type -> Type -> Exp Constraint)
4368 -> (Type -> Exp K.HTable)
@@ -46,12 +71,12 @@ data GGSerialize
4671 -> Exp Constraint
4772
4873
49−type instance Eval (GGSerialize 'Product _Serialize _Columns exprs rep) =
50− GSerialize _Serialize _Columns exprs rep
74+type instance Eval (GGToExprs 'Product _ToExprs _Columns exprs rep) =
75+ GToExprs _ToExprs _Columns exprs rep
5176
5277
53−type instance Eval (GGSerialize 'Sum _Serialize _Columns exprs rep) =
54− GSerializeADT _Serialize _Columns exprs rep
78+type instance Eval (GGToExprs 'Sum _ToExprs _Columns exprs rep) =
79+ GToExprsADT _ToExprs _Columns exprs rep
5580
5681
5782data GGColumns
@@ -67,6 +92,19 @@ type instance Eval (GGColumns 'Product _Columns rep) = GColumns _Columns rep
6792type instance Eval (GGColumns 'Sum _Columns rep) = GColumnsADT _Columns rep
6893
6994
95+data GGContext
96+ :: Algebra
97+ -> (Type -> Exp K.Context)
98+ -> (Type -> Type)
99+ -> Exp K.Context
100+
101+
102+type instance Eval (GGContext 'Product _Context rep) = GContext _Context rep
103+
104+
105+type instance Eval (GGContext 'Sum _Context _rep) = Result
106+
107+
70108type GAlgebra :: (Type -> Type) -> Algebra
71109type family GAlgebra rep where
72110 GAlgebra (M1 _ _ rep) = GAlgebra rep
@@ -77,27 +115,74 @@ type family GAlgebra rep where
77115 GAlgebra (K1 _ _) = 'Product
78116
79117
80−ggfromResult :: forall algebra _Serialize _Columns exprs rep x.
118+ggfromColumns :: forall algebra _Table _Columns rep context x.
119+ ( KnownAlgebra algebra
120+ , Eval (GGTable algebra _Table _Columns context rep)
121+ )
122+ => (forall spec. algebra ~ 'Sum => context spec -> Col Result spec)
123+ -> (forall spec. algebra ~ 'Sum => Col Result spec -> context spec)
124+ -> (forall a. Eval (_Table a) => Eval (_Columns a) context -> a)
125+ -> Eval (GGColumns algebra _Columns rep) context
126+ -> rep x
127+ggfromColumns = case algebraSing @algebra of
128+ SProduct -> \_ _ -> gfromColumns @_Table @_Columns
129+ SSum -> gfromColumnsADT @_Table @_Columns
130+
131+
132+ggtoColumns :: forall algebra _Table _Columns rep context x.
133+ ( KnownAlgebra algebra
134+ , Eval (GGTable algebra _Table _Columns context rep)
135+ )
136+ => (forall spec. algebra ~ 'Sum => context spec -> Col Result spec)
137+ -> (forall spec. algebra ~ 'Sum => Col Result spec -> context spec)
138+ -> (forall a. Eval (_Table a) => a -> Eval (_Columns a) context)
139+ -> rep x
140+ -> Eval (GGColumns algebra _Columns rep) context
141+ggtoColumns = case algebraSing @algebra of
142+ SProduct -> \_ _ -> gtoColumns @_Table @_Columns
143+ SSum -> gtoColumnsADT @_Table @_Columns
144+
145+
146+ggtable :: forall algebra _Table _Columns rep context.
147+ ( KnownAlgebra algebra
148+ , Eval (GGTable algebra _Table _Columns context rep)
149+ )
150+ => (forall a proxy. Eval (_Table a) => proxy a -> Eval (_Columns a) context)
151+ -> (forall a labels. ()
152+ => SSpec ('Spec labels a)
153+ -> context ('Spec labels a)
154+ -> context ('Spec labels (Nullify a)))
155+ -> Eval (GGColumns algebra _Columns rep) context
156+ggtable = case algebraSing @algebra of
157+ SProduct -> \table _ -> gtable @_Table @_Columns @_ @rep table
158+ SSum -> gtableADT @_Table @_Columns @_ @rep
159+
160+
161+ggfromResult :: forall algebra _ToExprs _Columns exprs rep x.
81162 ( KnownAlgebra algebra
82− , Eval (GGSerialize algebra _Serialize _Columns exprs rep)
163+ , Eval (GGToExprs algebra _ToExprs _Columns exprs rep)
83164 )
84− => (forall expr a proxy. Eval (_Serialize expr a)
85− => proxy expr -> Eval (_Columns expr) Result -> a)
86− -> Eval (GGColumns algebra _Columns exprs) Result
165+ => (forall expr a proxy. Eval (_ToExprs expr a)
166+ => proxy expr
167+ -> Eval (_Columns expr) (Col Result)
168+ -> a)
169+ -> Eval (GGColumns algebra _Columns exprs) (Col Result)
87170 -> rep x
88−ggfromResult f x = case algebraSing @algebra of
89− SProduct -> gfromResult @_Serialize @_Columns @exprs @rep f x
90− SSum -> gfromResultADT @_Serialize @_Columns @exprs @rep f x
171+ggfromResult = case algebraSing @algebra of
172+ SProduct -> gfromResult @_ToExprs @_Columns @exprs
173+ SSum -> gfromResultADT @_ToExprs @_Columns @exprs
91174
92175
93−ggtoResult :: forall algebra _Serialize _Columns exprs rep x.
176+ggtoResult :: forall algebra _ToExprs _Columns exprs rep x.
94177 ( KnownAlgebra algebra
95− , Eval (GGSerialize algebra _Serialize _Columns exprs rep)
178+ , Eval (GGToExprs algebra _ToExprs _Columns exprs rep)
96179 )
97− => (forall expr a proxy. Eval (_Serialize expr a)
98− => proxy expr -> a -> Eval (_Columns expr) Result)
180+ => (forall expr a proxy. Eval (_ToExprs expr a)
181+ => proxy expr
182+ -> a
183+ -> Eval (_Columns expr) (Col Result))
99184 -> rep x
100− -> Eval (GGColumns algebra _Columns exprs) Result
101−ggtoResult f x = case algebraSing @algebra of
102− SProduct -> gtoResult @_Serialize @_Columns @exprs @rep f x
103− SSum -> gtoResultADT @_Serialize @_Columns @exprs @rep f x
185+ -> Eval (GGColumns algebra _Columns exprs) (Col Result)
186+ggtoResult = case algebraSing @algebra of
187+ SProduct -> gtoResult @_ToExprs @_Columns @exprs
188+ SSum -> gtoResultADT @_ToExprs @_Columns @exprs
src/Rel8/Generic/Table/ADT.hs+203−61
@@ -4,6 +4,7 @@
44{-# language FlexibleInstances #-}
55{-# language LambdaCase #-}
66{-# language MultiParamTypeClasses #-}
7+{-# language PolyKinds #-}
78{-# language RankNTypes #-}
89{-# language ScopedTypeVariables #-}
910{-# language StandaloneKindSignatures #-}
@@ -13,13 +14,13 @@
1314{-# language UndecidableInstances #-}
1415
1516module Rel8.Generic.Table.ADT
16− ( GSerializeADT, GColumnsADT, gfromResultADT, gtoResultADT
17− , GSerializeADT', GColumnsADT'
17+ ( GTableADT, GColumnsADT, gfromColumnsADT, gtoColumnsADT, gtableADT
18+ , GTableADT', GColumnsADT'
19+ , GToExprsADT, gfromResultADT, gtoResultADT
1820 )
1921where
2022
2123-- base
22−import Data.Functor.Identity ( Identity( Identity ) )
2324import Data.Kind ( Constraint, Type )
2425import Data.Proxy ( Proxy( Proxy ) )
2526import GHC.Generics
@@ -32,14 +33,23 @@ import Prelude hiding ( null )
3233
3334-- rel8
3435import Rel8.FCF ( Eval, Exp )
35−import Rel8.Generic.Table.Record ( GSerialize, GColumns, gfromResult, gtoResult )
36−import Rel8.Schema.HTable ( HTable )
37−import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) )
36+import Rel8.Generic.Table.Record
37+ ( GTable, GColumns, gtable
38+ , GToExprs, gfromResult, gtoResult
39+ )
40+import Rel8.Schema.Context.Label ( HLabelable, hlabeler, labeler, unlabeler )
41+import Rel8.Schema.HTable ( HTable, hmap )
42+import Rel8.Schema.HTable.Identity ( HIdentity( HType ), HType )
3843import Rel8.Schema.HTable.Label ( HLabel, hlabel, hunlabel )
3944import Rel8.Schema.HTable.Nullify ( HNullify, hnulls, hnullify, hunnullify )
4045import Rel8.Schema.HTable.Product ( HProduct( HProduct ) )
4146import qualified Rel8.Schema.Kind as K
42−import Rel8.Schema.Result ( Result, null, nullifier, unnullifier )
47+import Rel8.Schema.Null ( Nullify )
48+import Rel8.Schema.Spec ( Spec( Spec ), SSpec )
49+import Rel8.Schema.Result
50+ ( Col( R ), Result
51+ , null, nullifier, unnullifier
52+ )
4353import Rel8.Type.Tag ( Tag( Tag ) )
4454
4555-- text
@@ -51,7 +61,7 @@ type GColumnsADT
5161 -> (Type -> Type) -> K.HTable
5262type family GColumnsADT _Columns rep where
5363 GColumnsADT _Columns (M1 D _ rep) =
54− GColumnsADT' _Columns (HLabel "tag" (HIdentity Tag)) rep
64+ GColumnsADT' _Columns (HLabel "tag" (HType Tag)) rep
5565
5666
5767type GColumnsADT'
@@ -65,118 +75,246 @@ type family GColumnsADT' _Columns htable rep where
6575 HProduct htable (HLabel label (HNullify (GColumns _Columns rep)))
6676
6777
68−type GSerializeADT
78+type GTableADT
79+ :: (Type -> Exp Constraint)
80+ -> (Type -> Exp K.HTable)
81+ -> K.HContext -> (Type -> Type) -> Constraint
82+class GTableADT _Table _Columns context rep where
83+ gfromColumnsADT :: ()
84+ => (forall spec. context spec -> Col Result spec)
85+ -> (forall spec. Col Result spec -> context spec)
86+ -> (forall a. Eval (_Table a) => Eval (_Columns a) context -> a)
87+ -> GColumnsADT _Columns rep context
88+ -> rep x
89+
90+ gtoColumnsADT :: ()
91+ => (forall spec. context spec -> Col Result spec)
92+ -> (forall spec. Col Result spec -> context spec)
93+ -> (forall a. Eval (_Table a) => a -> Eval (_Columns a) context)
94+ -> rep x
95+ -> GColumnsADT _Columns rep context
96+
97+ gtableADT :: ()
98+ => (forall a proxy. Eval (_Table a) => proxy a -> Eval (_Columns a) context)
99+ -> (forall a labels. ()
100+ => SSpec ('Spec labels a)
101+ -> context ('Spec labels a)
102+ -> context ('Spec labels (Nullify a)))
103+ -> GColumnsADT _Columns rep context
104+
105+
106+instance
107+ ( htable ~ HLabel "tag" (HType Tag)
108+ , HTable (GColumnsADT _Columns (M1 D meta rep))
109+ , Eval (_Table (htable context))
110+ , Eval (_Columns (htable context)) ~ htable
111+ , GTableADT' _Table _Columns htable context rep
112+ , GToExprsADT' (Const _Table) _Columns htable rep rep
113+ )
114+ => GTableADT _Table _Columns context (M1 D meta rep)
115+ where
116+ gfromColumnsADT fromContext toContext fromColumns =
117+ gfromResultADT @(Const _Table) @_Columns @(M1 D meta rep) @(M1 D meta rep)
118+ (const (fromColumns . hmap toContext)) .
119+ hmap fromContext
120+
121+ gtoColumnsADT fromContext toContext toColumns =
122+ hmap toContext .
123+ gtoResultADT @(Const _Table) @_Columns @(M1 D meta rep) @(M1 D meta rep)
124+ (const (hmap fromContext . toColumns))
125+
126+ gtableADT table hnullifier =
127+ gtableADT' @_Table @_Columns @htable @context @rep table hnullifier htable
128+ where
129+ htable = table (Proxy @(htable context))
130+
131+
132+type GTableADT'
133+ :: (Type -> Exp Constraint)
134+ -> (Type -> Exp K.HTable)
135+ -> K.HTable -> K.HContext -> (Type -> Type) -> Constraint
136+class GTableADT' _Table _Columns htable context rep where
137+ gtableADT' :: ()
138+ => (forall a proxy. Eval (_Table a) => proxy a -> Eval (_Columns a) context)
139+ -> (forall a labels. ()
140+ => SSpec ('Spec labels a)
141+ -> context ('Spec labels a)
142+ -> context ('Spec labels (Nullify a)))
143+ -> htable context
144+ -> GColumnsADT' _Columns htable rep context
145+
146+
147+instance
148+ ( htable' ~ GColumnsADT' _Columns htable a
149+ , GTableADT' _Table _Columns htable context a
150+ , GTableADT' _Table _Columns htable' context b
151+ )
152+ => GTableADT' _Table _Columns htable context (a :+: b)
153+ where
154+ gtableADT' table hnullifier =
155+ gtableADT' @_Table @_Columns @_ @_ @b table hnullifier .
156+ gtableADT' @_Table @_Columns @_ @_ @a table hnullifier
157+
158+
159+instance meta ~ 'MetaCons label _fixity _isRecord =>
160+ GTableADT' _Table _Columns htable context (M1 C meta U1)
161+ where
162+ gtableADT' _ _ = id
163+
164+
165+instance {-# OVERLAPPABLE #-}
166+ ( HTable (GColumns _Columns rep)
167+ , GTable _Table _Columns context rep
168+ , HLabelable context
169+ , meta ~ 'MetaCons label _fixity _isRecord
170+ , KnownSymbol label
171+ , GColumnsADT' _Columns htable (M1 C ('MetaCons label _fixity _isRecord) rep) ~
172+ HProduct htable (HLabel label (HNullify (GColumns _Columns rep)))
173+ )
174+ => GTableADT' _Table _Columns htable context (M1 C meta rep)
175+ where
176+ gtableADT' table hnullifier htable =
177+ HProduct htable $
178+ hlabel hlabeler $
179+ hnullify hnullifier $
180+ gtable @_Table @_Columns @_ @rep table
181+
182+
183+type GToExprsADT
69184 :: (Type -> Type -> Exp Constraint)
70185 -> (Type -> Exp K.HTable)
71186 -> (Type -> Type) -> (Type -> Type) -> Constraint
72−class GSerializeADT _Serialize _Columns exprs rep where
187+class GToExprsADT _ToExprs _Columns exprs rep where
73188 gfromResultADT :: ()
74− => (forall expr a proxy. Eval (_Serialize expr a)
75− => proxy expr -> Eval (_Columns expr) Result -> a)
76− -> GColumnsADT _Columns exprs Result
189+ => (forall expr a proxy.
190+ ( Eval (_ToExprs expr a)
191+ , HTable (Eval (_Columns expr))
192+ )
193+ => proxy expr
194+ -> Eval (_Columns expr) (Col Result)
195+ -> a)
196+ -> GColumnsADT _Columns exprs (Col Result)
77197 -> rep x
78198
79199 gtoResultADT :: ()
80− => (forall expr a proxy. Eval (_Serialize expr a)
81− => proxy expr -> a -> Eval (_Columns expr) Result)
200+ => (forall expr a proxy.
201+ ( Eval (_ToExprs expr a)
202+ , HTable (Eval (_Columns expr))
203+ )
204+ => proxy expr
205+ -> a
206+ -> Eval (_Columns expr) (Col Result))
82207 -> rep x
83− -> GColumnsADT _Columns exprs Result
208+ -> GColumnsADT _Columns exprs (Col Result)
84209
85210
86211instance
87− ( htable ~ HLabel "tag" (HIdentity Tag)
88− , GSerializeADT' _Serialize _Columns htable exprs rep
212+ ( htable ~ HLabel "tag" (HType Tag)
213+ , GToExprsADT' _ToExprs _Columns htable exprs rep
89214 )
90− => GSerializeADT _Serialize _Columns (M1 D meta exprs) (M1 D meta rep)
215+ => GToExprsADT _ToExprs _Columns (M1 D meta exprs) (M1 D meta rep)
91216 where
92217 gfromResultADT fromResult columns =
93− case gfromResultADT' @_Serialize @_Columns @htable @exprs @rep fromResult tag columns of
218+ case gfromResultADT' @_ToExprs @_Columns @_ @exprs fromResult tag columns of
94219 Just rep -> M1 rep
95220 _ -> error "ADT.fromColumns: mismatch between tag and data"
96221 where
97− tag = (\(HIdentity (Identity a)) -> a) . hunlabel @"tag"
222+ tag = (\(HType (R a)) -> a) . hunlabel @_ @"tag" unlabeler
98223
99224 gtoResultADT toResult (M1 rep) =
100− gtoResultADT' @_Serialize @_Columns @htable @exprs @rep toResult tag (Just rep)
225+ gtoResultADT' @_ToExprs @_Columns @_ @exprs toResult tag (Just rep)
101226 where
102− tag = hlabel @"tag" . HIdentity . Identity
227+ tag = hlabel @_ @"tag" labeler . HType . R
103228
104229
105−type GSerializeADT'
230+type GToExprsADT'
106231 :: (Type -> Type -> Exp Constraint)
107232 -> (Type -> Exp K.HTable)
108233 -> K.HTable -> (Type -> Type) -> (Type -> Type) -> Constraint
109−class GSerializeADT' _Serialize _Columns htable exprs rep where
110− gfromResultADT' :: context ~ Result
111− => (forall expr a proxy. Eval (_Serialize expr a)
112− => proxy expr -> Eval (_Columns expr) context -> a)
113− -> (htable Result -> Tag)
114− -> GColumnsADT' _Columns htable exprs context
234+class GToExprsADT' _ToExprs _Columns htable exprs rep where
235+ gfromResultADT' :: ()
236+ => (forall expr a proxy.
237+ ( Eval (_ToExprs expr a)
238+ , HTable (Eval (_Columns expr))
239+ )
240+ => proxy expr
241+ -> Eval (_Columns expr) (Col Result)
242+ -> a)
243+ -> (htable (Col Result) -> Tag)
244+ -> GColumnsADT' _Columns htable exprs (Col Result)
115245 -> Maybe (rep x)
116246
117− gtoResultADT' :: context ~ Result
118− => (forall expr a proxy. Eval (_Serialize expr a)
119− => proxy expr -> a -> Eval (_Columns expr) context)
120− -> (Tag -> htable Result)
247+ gtoResultADT' :: ()
248+ => (forall expr a proxy.
249+ ( Eval (_ToExprs expr a)
250+ , HTable (Eval (_Columns expr))
251+ )
252+ => proxy expr
253+ -> a
254+ -> Eval (_Columns expr) (Col Result))
255+ -> (Tag -> htable (Col Result))
121256 -> Maybe (rep x)
122− -> GColumnsADT' _Columns htable exprs context
257+ -> GColumnsADT' _Columns htable exprs (Col Result)
123258
124259 extract :: GColumnsADT' _Columns htable exprs context -> htable context
125260
126261
127262instance
128− ( htable' ~ GColumnsADT' _Columns htable exprs1
129− , GSerializeADT' _Serialize _Columns htable exprs1 a
130− , GSerializeADT' _Serialize _Columns htable' exprs2 b
263+ ( htable' ~ GColumnsADT' _Columns htable a
264+ , GToExprsADT' _ToExprs _Columns htable a rep1
265+ , GToExprsADT' _ToExprs _Columns htable' b rep2
131266 )
132− => GSerializeADT' _Serialize _Columns htable (exprs1 :+: exprs2) (a :+: b)
267+ => GToExprsADT' _ToExprs _Columns htable (a :+: b) (rep1 :+: rep2)
133268 where
134269 gfromResultADT' fromResult f columns =
135270 case ma of
136271 Just a -> Just (L1 a)
137272 Nothing -> R1 <$>
138− gfromResultADT' @_Serialize @_Columns @_ @exprs2 @b
273+ gfromResultADT' @_ToExprs @_Columns @_ @b @rep2
139274 fromResult
140− (f . extract @_Serialize @_Columns @_ @exprs1 @a)
275+ (f . extract @_ToExprs @_Columns @_ @a @rep1)
141276 columns
142277 where
143278 ma =
144− gfromResultADT' @_Serialize @_Columns @_ @exprs1 @a
279+ gfromResultADT' @_ToExprs @_Columns @_ @a @rep1
145280 fromResult
146281 f
147− (extract @_Serialize @_Columns @_ @exprs2 @b columns)
282+ (extract @_ToExprs @_Columns @_ @b @rep2 columns)
148283
149284 gtoResultADT' toResult tag = \case
150285 Just (L1 a) ->
151− gtoResultADT' @_Serialize @_Columns @_ @exprs2 @b
286+ gtoResultADT' @_ToExprs @_Columns @_ @b @rep2
152287 toResult
153− (\_ -> gtoResultADT' @_Serialize @_Columns @_ @exprs1 @a
288+ (\_ -> gtoResultADT' @_ToExprs @_Columns @_ @a @rep1
154289 toResult
155290 tag
156291 (Just a))
157292 Nothing
158293 Just (R1 b) ->
159− gtoResultADT' @_Serialize @_Columns @_ @exprs2 @b
294+ gtoResultADT' @_ToExprs @_Columns @_ @b @rep2
160295 toResult
161296 (\tag' ->
162− gtoResultADT' @_Serialize @_Columns @_ @exprs1 @a
297+ gtoResultADT' @_ToExprs @_Columns @_ @a @rep1
163298 toResult
164299 (\_ -> tag tag')
165300 Nothing)
166301 (Just b)
167302 Nothing ->
168− gtoResultADT' @_Serialize @_Columns @_ @exprs2 @b
303+ gtoResultADT' @_ToExprs @_Columns @_ @b @rep2
169304 toResult
170− (\_ -> gtoResultADT' @_Serialize @_Columns @_ @exprs1 @a toResult tag Nothing)
305+ (\_ -> gtoResultADT' @_ToExprs @_Columns @_ @a @rep1 toResult tag Nothing)
171306 Nothing
172307
173308 extract =
174− extract @_Serialize @_Columns @_ @exprs1 @a .
175− extract @_Serialize @_Columns @_ @exprs2 @b
309+ extract @_ToExprs @_Columns @_ @a @rep1 .
310+ extract @_ToExprs @_Columns @_ @b @rep2
176311
177312
178−instance (meta ~ 'MetaCons label _fixity _isRecord, KnownSymbol label) =>
179− GSerializeADT' _Serialize _Columns _htable (M1 C meta U1) (M1 C meta U1)
313+instance
314+ ( meta ~ 'MetaCons label _fixity _isRecord
315+ , KnownSymbol label
316+ )
317+ => GToExprsADT' _ToExprs _Columns htable (M1 C meta U1) (M1 C meta U1)
180318 where
181319 gfromResultADT' _ tag columns
182320 | tag columns == tag' = Just (M1 U1)
@@ -193,29 +331,33 @@ instance (meta ~ 'MetaCons label _fixity _isRecord, KnownSymbol label) =>
193331
194332instance {-# OVERLAPPABLE #-}
195333 ( HTable (GColumns _Columns exprs)
196− , GSerialize _Serialize _Columns exprs rep
334+ , GToExprs _ToExprs _Columns exprs rep
197335 , meta ~ 'MetaCons label _fixity _isRecord
198336 , KnownSymbol label
199− , GColumnsADT' _Columns htable (M1 C ('MetaCons label _fixity _isRecord) exprs) ~
337+ , GColumnsADT' _Columns htable (M1 C meta exprs) ~
200338 HProduct htable (HLabel label (HNullify (GColumns _Columns exprs)))
201339 )
202− => GSerializeADT' _Serialize _Columns htable (M1 C meta exprs) (M1 C meta rep)
340+ => GToExprsADT' _ToExprs _Columns htable (M1 C meta exprs) (M1 C meta rep)
203341 where
204342 gfromResultADT' fromResult tag (HProduct a b)
205343 | tag a == tag' =
206− M1 . gfromResult @_Serialize @_Columns @exprs @rep fromResult <$>
207− hunnullify unnullifier (hunlabel b)
344+ M1 . gfromResult @_ToExprs @_Columns @exprs fromResult <$>
345+ hunnullify unnullifier (hunlabel unlabeler b)
208346 | otherwise = Nothing
209347 where
210348 tag' = Tag $ pack $ symbolVal (Proxy @label)
211349
212350 gtoResultADT' toResult tag = \case
213− Nothing -> HProduct (tag tag') (hlabel (hnulls (const null)))
351+ Nothing -> HProduct (tag tag') (hlabel labeler (hnulls (const null)))
214352 Just (M1 rep) -> HProduct (tag tag') $
215− hlabel $
353+ hlabel labeler $
216354 hnullify nullifier $
217− gtoResult @_Serialize @_Columns @exprs @rep toResult rep
355+ gtoResult @_ToExprs @_Columns @exprs toResult rep
218356 where
219357 tag' = Tag $ pack $ symbolVal (Proxy @label)
220358
221359 extract (HProduct a _) = a
360+
361+
362+data Const :: (a -> Exp Constraint) -> a -> a -> Exp Constraint
363+type instance Eval (Const f x a) = (Eval (f a), x ~ a)
src/Rel8/Generic/Table/Record.hs+99−74
@@ -14,7 +14,7 @@
1414
1515module Rel8.Generic.Table.Record
1616 ( GTable, GColumns, GContext, gfromColumns, gtoColumns, gtable
17− , GSerialize, gfromResult, gtoResult
17+ , GToExprs, gfromResult, gtoResult
1818 )
1919where
2020
@@ -26,13 +26,18 @@ import GHC.Generics
2626 , C, D, S
2727 , Meta( MetaSel )
2828 )
29+import GHC.TypeLits ( KnownSymbol )
2930import Prelude hiding ( null )
3031
3132-- rel8
3233import Rel8.FCF ( Eval, Exp )
34+import Rel8.Schema.Context ( Col )
35+import Rel8.Schema.Context.Label ( HLabelable, hlabeler, hunlabeler )
36+import Rel8.Schema.HTable ( HTable )
3337import Rel8.Schema.HTable.Label ( HLabel, hlabel, hunlabel )
3438import Rel8.Schema.HTable.Product ( HProduct(..) )
3539import qualified Rel8.Schema.Kind as K
40+import Rel8.Schema.Result ( Result )
3641
3742
3843type GColumns :: (Type -> Exp K.HTable) -> (Type -> Type) -> K.HTable
@@ -55,8 +60,8 @@ type family GContext _Context rep where
5560type GTable
5661 :: (Type -> Exp Constraint)
5762 -> (Type -> Exp K.HTable)
58− -> (Type -> Type) -> Constraint
59−class GTable _Table _Columns rep
63+ -> K.HContext -> (Type -> Type) -> Constraint
64+class GTable _Table _Columns context rep
6065 where
6166 gfromColumns :: ()
6267 => (forall a. Eval (_Table a) => Eval (_Columns a) context -> a)
@@ -73,109 +78,129 @@ class GTable _Table _Columns rep
7378 -> GColumns _Columns rep context
7479
7580
76−type GSerialize
77− :: (Type -> Type -> Exp Constraint)
78− -> (Type -> Exp K.HTable)
79− -> (Type -> Type) -> (Type -> Type) -> Constraint
80−class GSerialize _Serialize _Columns exprs rep
81+instance GTable _Table _Columns context rep =>
82+ GTable _Table _Columns context (M1 D c rep)
8183 where
82− gfromResult :: ()
83− => (forall expr a proxy. Eval (_Serialize expr a)
84− => proxy expr -> Eval (_Columns expr) context -> a)
85− -> GColumns _Columns exprs context
86− -> rep x
87−
88− gtoResult :: ()
89− => (forall expr a proxy. Eval (_Serialize expr a)
90− => proxy expr -> a -> Eval (_Columns expr) context)
91− -> rep x
92− -> GColumns _Columns exprs context
84+ gfromColumns fromColumns =
85+ M1 . gfromColumns @_Table @_Columns @context @rep fromColumns
86+ gtoColumns toColumns (M1 a) =
87+ gtoColumns @_Table @_Columns @context @rep toColumns a
88+ gtable = gtable @_Table @_Columns @context @rep
9389
9490
95−instance GTable _Table _Columns rep => GTable _Table _Columns (M1 D c rep)
91+instance GTable _Table _Columns context rep =>
92+ GTable _Table _Columns context (M1 C c rep)
9693 where
9794 gfromColumns fromColumns =
98− M1 . gfromColumns @_Table @_Columns @rep fromColumns
95+ M1 . gfromColumns @_Table @_Columns @context @rep fromColumns
9996 gtoColumns toColumns (M1 a) =
100− gtoColumns @_Table @_Columns @rep toColumns a
101− gtable = gtable @_Table @_Columns @rep
97+ gtoColumns @_Table @_Columns @context @rep toColumns a
98+ gtable = gtable @_Table @_Columns @context @rep
10299
103100
104−instance GSerialize _Serialize _Columns exprs rep =>
105− GSerialize _Serialize _Columns (M1 D c exprs) (M1 D c rep)
101+instance
102+ ( GTable _Table _Columns context rep1
103+ , GTable _Table _Columns context rep2
104+ )
105+ => GTable _Table _Columns context (rep1 :*: rep2)
106106 where
107− gfromResult fromResult =
108− M1 . gfromResult @_Serialize @_Columns @exprs @rep fromResult
109− gtoResult toResult (M1 a) =
110− gtoResult @_Serialize @_Columns @exprs @rep toResult a
107+ gfromColumns fromColumns (HProduct a b) =
108+ gfromColumns @_Table @_Columns @context @rep1 fromColumns a :*:
109+ gfromColumns @_Table @_Columns @context @rep2 fromColumns b
110+ gtoColumns toColumns (a :*: b) = HProduct
111+ (gtoColumns @_Table @_Columns @context @rep1 toColumns a)
112+ (gtoColumns @_Table @_Columns @context @rep2 toColumns b)
113+ gtable table = HProduct
114+ (gtable @_Table @_Columns @context @rep1 table)
115+ (gtable @_Table @_Columns @context @rep2 table)
111116
112117
113−instance GTable _Table _Columns rep => GTable _Table _Columns (M1 C c rep)
118+instance
119+ ( HTable (Eval (_Columns a))
120+ , Eval (_Table a)
121+ , HLabelable context
122+ , KnownSymbol label
123+ , meta ~ 'MetaSel ('Just label) _su _ss _ds
124+ , k1 ~ K1 i a
125+ )
126+ => GTable _Table _Columns context (M1 S meta k1)
114127 where
115− gfromColumns fromColumns =
116− M1 . gfromColumns @_Table @_Columns @rep fromColumns
117− gtoColumns toColumns (M1 a) =
118− gtoColumns @_Table @_Columns @rep toColumns a
119− gtable = gtable @_Table @_Columns @rep
128+ gfromColumns fromColumns = M1 . K1 . fromColumns . hunlabel hunlabeler
129+ gtoColumns toColumns (M1 (K1 a)) = hlabel hlabeler (toColumns a)
130+ gtable table = hlabel hlabeler (table (Proxy @a))
120131
121132
122−instance GSerialize _Serialize _Columns exprs rep =>
123− GSerialize _Serialize _Columns (M1 C c exprs) (M1 C c rep)
133+type GToExprs
134+ :: (Type -> Type -> Exp Constraint)
135+ -> (Type -> Exp K.HTable)
136+ -> (Type -> Type) -> (Type -> Type) -> Constraint
137+class GToExprs _ToExprs _Columns exprs rep where
138+ gfromResult :: ()
139+ => (forall expr a proxy.
140+ ( Eval (_ToExprs expr a)
141+ , HTable (Eval (_Columns expr))
142+ )
143+ => proxy expr
144+ -> Eval (_Columns expr) (Col Result)
145+ -> a)
146+ -> GColumns _Columns exprs (Col Result)
147+ -> rep x
148+ gtoResult :: ()
149+ => (forall expr a proxy.
150+ ( Eval (_ToExprs expr a)
151+ , HTable (Eval (_Columns expr))
152+ )
153+ => proxy expr
154+ -> a
155+ -> Eval (_Columns expr) (Col Result))
156+ -> rep x
157+ -> GColumns _Columns exprs (Col Result)
158+
159+
160+instance GToExprs _ToExprs _Columns exprs rep =>
161+ GToExprs _ToExprs _Columns (M1 D c exprs) (M1 D c rep)
124162 where
125163 gfromResult fromResult =
126− M1 . gfromResult @_Serialize @_Columns @exprs @rep fromResult
164+ M1 . gfromResult @_ToExprs @_Columns @exprs fromResult
127165 gtoResult toResult (M1 a) =
128− gtoResult @_Serialize @_Columns @exprs @rep toResult a
166+ gtoResult @_ToExprs @_Columns @exprs toResult a
129167
130168
131−instance (GTable _Table _Columns rep1, GTable _Table _Columns rep2) =>
132− GTable _Table _Columns (rep1 :*: rep2)
169+instance GToExprs _ToExprs _Columns exprs rep =>
170+ GToExprs _ToExprs _Columns (M1 C c exprs) (M1 C c rep)
133171 where
134− gfromColumns fromColumns (HProduct a b) =
135− gfromColumns @_Table @_Columns @rep1 fromColumns a :*:
136− gfromColumns @_Table @_Columns @rep2 fromColumns b
137− gtoColumns toColumns (a :*: b) = HProduct
138− (gtoColumns @_Table @_Columns @rep1 toColumns a)
139− (gtoColumns @_Table @_Columns @rep2 toColumns b)
140− gtable table = HProduct
141− (gtable @_Table @_Columns @rep1 table)
142− (gtable @_Table @_Columns @rep2 table)
172+ gfromResult fromResult =
173+ M1 . gfromResult @_ToExprs @_Columns @exprs fromResult
174+ gtoResult toResult (M1 a) =
175+ gtoResult @_ToExprs @_Columns @exprs toResult a
143176
144177
145178instance
146− ( GSerialize _Serialize _Columns expr1 rep1
147− , GSerialize _Serialize _Columns expr2 rep2
179+ ( GToExprs _ToExprs _Columns exprs1 rep1
180+ , GToExprs _ToExprs _Columns exprs2 rep2
148181 )
149− => GSerialize _Serialize _Columns (expr1 :*: expr2) (rep1 :*: rep2)
182+ => GToExprs _ToExprs _Columns (exprs1 :*: exprs2) (rep1 :*: rep2)
150183 where
151184 gfromResult fromResult (HProduct a b) =
152− gfromResult @_Serialize @_Columns @expr1 @rep1 fromResult a :*:
153− gfromResult @_Serialize @_Columns @expr2 @rep2 fromResult b
185+ gfromResult @_ToExprs @_Columns @exprs1 fromResult a :*:
186+ gfromResult @_ToExprs @_Columns @exprs2 fromResult b
154187 gtoResult toResult (a :*: b) =
155188 HProduct
156− (gtoResult @_Serialize @_Columns @expr1 @rep1 toResult a)
157− (gtoResult @_Serialize @_Columns @expr2 @rep2 toResult b)
189+ (gtoResult @_ToExprs @_Columns @exprs1 toResult a)
190+ (gtoResult @_ToExprs @_Columns @exprs2 toResult b)
158191
159192
160193instance
161− ( Eval (_Table a)
194+ ( Eval (_ToExprs exprs a)
195+ , HTable (Eval (_Columns exprs))
196+ , KnownSymbol label
162197 , meta ~ 'MetaSel ('Just label) _su _ss _ds
163− , k1 ~ K1 i a
164− )
165− => GTable _Table _Columns (M1 S meta k1)
166− where
167− gfromColumns fromColumns = M1 . K1 . fromColumns . hunlabel
168− gtoColumns toColumns (M1 (K1 a)) = hlabel (toColumns a)
169− gtable table = hlabel (table (Proxy @a))
170−
171−
172−instance
173− ( Eval (_Serialize expr a)
174− , meta ~ 'MetaSel ('Just label) _su _ss _ds
175− , k1 ~ K1 i expr
198+ , k1 ~ K1 i exprs
176199 , k1' ~ K1 i a
177200 )
178− => GSerialize _Serialize _Columns (M1 S meta k1) (M1 S meta k1')
201+ => GToExprs _ToExprs _Columns (M1 S meta k1) (M1 S meta k1')
179202 where
180− gfromResult fromResult = M1 . K1 . fromResult (Proxy @expr) . hunlabel
181− gtoResult toResult (M1 (K1 a)) = hlabel (toResult (Proxy @expr) a)
203+ gfromResult fromResult =
204+ M1 . K1 . fromResult (Proxy @exprs) . hunlabel hunlabeler
205+ gtoResult toResult (M1 (K1 a)) =
206+ hlabel hlabeler (toResult (Proxy @exprs) a)
src/Rel8/Kind/Context.hs+33−8
@@ -1,11 +1,13 @@
11{-# language DataKinds #-}
22{-# language GADTs #-}
3+{-# language LambdaCase #-}
34{-# language StandaloneKindSignatures #-}
4−{-# language TypeSynonymInstances #-}
55
66module Rel8.Kind.Context
77 ( Reifiable( contextSing )
88 , SContext(..)
9+ , sReifiable
10+ , sLabelable
911 )
1012where
1113
@@ -16,9 +18,12 @@ import Prelude ()
1618-- rel8
1719import Rel8.Aggregate ( Aggregate )
1820import Rel8.Expr ( Expr )
19−import Rel8.Schema.Field ( Field )
21+import Rel8.Schema.Dict ( Dict( Dict ) )
22+import Rel8.Schema.Context ( Interpretation )
23+import Rel8.Schema.Context.Label ( Labelable )
2024import Rel8.Schema.Kind ( Context )
2125import Rel8.Schema.Name ( Name )
26+import Rel8.Schema.Reify ( Reify )
2227import Rel8.Schema.Result ( Result )
2328
2429
@@ -26,13 +31,13 @@ type SContext :: Context -> Type
2631data SContext context where
2732 SAggregate :: SContext Aggregate
2833 SExpr :: SContext Expr
29− SField :: SContext (Field table)
3034 SName :: SContext Name
3135 SResult :: SContext Result
36+ SReify :: SContext context -> SContext (Reify context)
3237
3338
3439type Reifiable :: Context -> Constraint
35−class Reifiable context where
40+class Interpretation context => Reifiable context where
3641 contextSing :: SContext context
3742
3843
@@ -44,13 +49,33 @@ instance Reifiable Expr where
4449 contextSing = SExpr
4550
4651
47−instance Reifiable (Field table) where
48− contextSing = SField
49−
50−
5152instance Reifiable Result where
5253 contextSing = SResult
5354
5455
5556instance Reifiable Name where
5657 contextSing = SName
58+
59+
60+instance Reifiable context => Reifiable (Reify context) where
61+ contextSing = SReify contextSing
62+
63+
64+sReifiable :: SContext context -> Dict Reifiable context
65+sReifiable = \case
66+ SAggregate -> Dict
67+ SExpr -> Dict
68+ SName -> Dict
69+ SResult -> Dict
70+ SReify context -> case sReifiable context of
71+ Dict -> Dict
72+
73+
74+sLabelable :: SContext context -> Dict Labelable context
75+sLabelable = \case
76+ SAggregate -> Dict
77+ SExpr -> Dict
78+ SName -> Dict
79+ SResult -> Dict
80+ SReify context -> case sLabelable context of
81+ Dict -> Dict
src/Rel8/Kind/Labels.hsnew file+57
@@ -0,0 +1,57 @@
1+{-# language DataKinds #-}
2+{-# language FlexibleContexts #-}
3+{-# language FlexibleInstances #-}
4+{-# language GADTs #-}
5+{-# language LambdaCase #-}
6+{-# language StandaloneKindSignatures #-}
7+{-# language TypeOperators #-}
8+
9+module Rel8.Kind.Labels
10+ ( Labels
11+ , SLabels( SNil, SCons )
12+ , KnownLabels( labelsSing )
13+ , renderLabels
14+ )
15+where
16+
17+-- base
18+import Data.Kind ( Constraint, Type )
19+import Data.List.NonEmpty ( NonEmpty, nonEmpty )
20+import Data.Maybe ( fromMaybe )
21+import Data.Proxy ( Proxy( Proxy ) )
22+import GHC.TypeLits ( KnownSymbol, Symbol, symbolVal )
23+import Prelude
24+
25+
26+type Labels :: Type
27+type Labels = [Symbol]
28+
29+
30+type SLabels :: Labels -> Type
31+data SLabels labels where
32+ SNil :: SLabels '[]
33+ SCons :: KnownSymbol label => Proxy label -> SLabels labels -> SLabels (label ': labels)
34+
35+
36+type KnownLabels :: Labels -> Constraint
37+class KnownLabels labels where
38+ labelsSing :: SLabels labels
39+
40+
41+instance KnownLabels '[] where
42+ labelsSing = SNil
43+
44+
45+instance (KnownSymbol label, KnownLabels labels) =>
46+ KnownLabels (label ': labels)
47+ where
48+ labelsSing = SCons Proxy labelsSing
49+
50+
51+renderLabels :: SLabels labels -> NonEmpty String
52+renderLabels = fromMaybe (pure "anon") . nonEmpty . go
53+ where
54+ go :: SLabels labels -> [String]
55+ go = \case
56+ SNil -> []
57+ SCons label labels -> symbolVal label : go labels
src/Rel8/Order.hs+9−2
@@ -4,6 +4,7 @@
44
55module Rel8.Order
66 ( Order(..)
7+ , toOrderExprs
78 )
89where
910
@@ -16,7 +17,8 @@ import Prelude
1617import Data.Functor.Contravariant.Divisible ( Decidable, Divisible )
1718
1819-- opaleye
19−import qualified Opaleye.Order as Opaleye
20+import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
21+import qualified Opaleye.Internal.Order as Opaleye
2022
2123
2224-- | An ordering expression for @a@. Primitive orderings are defined with
@@ -24,7 +26,12 @@ import qualified Opaleye.Order as Opaleye
2426-- instances.
2527--
2628-- A common pattern is to use '<>' to combine multiple orderings in sequence,
27−-- and 'Data.Functor.Contravariant.>$<' to select individual columns.
29+-- and '>$<' (from 'Contravariant') to select individual columns.
2830type Order :: Type -> Type
2931newtype Order a = Order (Opaleye.Order a)
3032 deriving newtype (Contravariant, Divisible, Decidable, Semigroup, Monoid)
33+
34+
35+toOrderExprs :: Order a -> a -> [Opaleye.OrderExpr]
36+toOrderExprs (Order (Opaleye.Order order)) a =
37+ uncurry Opaleye.OrderExpr <$> order a
src/Rel8/Query.hs+7−181
@@ -1,6 +1,6 @@
1−{-# language FlexibleContexts #-}
1+{-# language DerivingVia #-}
2+{-# language GeneralizedNewtypeDeriving #-}
23{-# language StandaloneKindSignatures #-}
3−{-# language UndecidableInstances #-}
44
55module Rel8.Query
66 ( Query( Query )
@@ -8,201 +8,37 @@ module Rel8.Query
88where
99
1010-- base
11−import Control.Applicative ( liftA2 )
12−import Control.Monad ( liftM2 )
1311import Data.Kind ( Type )
14−import Data.Monoid ( Any( Any ) )
1512import Prelude
1613
1714-- opaleye
18−import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
19−import qualified Opaleye.Internal.PackMap as Opaleye
20−import qualified Opaleye.Internal.PrimQuery as Opaleye hiding (lateral)
21−import qualified Opaleye.Internal.QueryArr as Opaleye
22−import qualified Opaleye.Internal.Tag as Opaleye
15+import qualified Opaleye.Select as Opaleye
2316
2417-- rel8
25−import Rel8.Expr ( Expr )
2618import Rel8.Query.Set ( unionAll )
27−import Rel8.Query.Opaleye ( fromOpaleye )
2819import Rel8.Query.Values ( values )
29−import Rel8.Table ( Table, fromColumns, toColumns )
3020import Rel8.Table.Alternative
3121 ( AltTable, (<|>:)
3222 , AlternativeTable, emptyTable
3323 )
34−import Rel8.Table.Projection ( Projectable, apply, project )
3524
3625-- semigroupoids
37−import Data.Functor.Apply ( Apply, (<.>) )
26+import Data.Functor.Apply ( Apply, WrappedApplicative(..) )
3827import Data.Functor.Bind ( Bind, (>>-) )
3928
4029
4130-- | The @Query@ monad allows you to compose a @SELECT@ query. This monad has
4231-- semantics similar to the list (@[]@) monad.
4332type Query :: Type -> Type
44−newtype Query a =
45− Query (
46− -- This is based on Opaleye's Select monad, but with two addtions. We
47− -- maintain a stack of PrimExprs from parent previous subselects. In
48− -- practice, these are always the results of dummy calls to random().
49− --
50− -- We also return a Bool that indicates to the parent subselect whether
51− -- or not that stack of PrimExprs were used at any point. If they weren't,
52− -- then the call to random() is never added to the query.
53− --
54− -- This is all needed to implement evaluate. Consider the following code:
55− --
56− -- do
57− -- x <- values [lit 'a', lit 'b', lit 'c']
58− -- y <- evaluate $ nextval "user_id_seq"
59− -- pure (x, y)
60− --
61− -- If we just used Opaleye's Select monad directly, the SQL would come out
62− -- like this:
63− --
64− -- SELECT
65− -- a, b
66− -- FROM
67− -- (VALUES ('a'), ('b'), ('c')) Q1(a),
68− -- LATERAL (SELECT nextval('user_id_seq')) Q2(b);
69− --
70− -- From the Haskell code, you would intuitively expect to get back the
71− -- results of three different calls to nextval(), but from Postgres' point
72− -- of view, because the Q2 subquery doesn't reference anything from the Q1
73− -- query, it thinks it only needs to call nextval() once. This is actually
74− -- exactly the same problem you get with the deprecated ListT IO monad from
75− -- the transformers package — *> behaves differently to >>=, so
76− -- using ApplicativeDo can change the results of a program. ApplicativeDo
77− -- is exactly the optimisation Postgres does on a "LATERAL" query that
78− -- doesn't make any references to previous subselects.
79− --
80− -- Rel8's solution is generate the following SQL instead:
81− --
82− -- SELECT
83− -- a, b
84− -- FROM
85− -- (SELECT
86− -- random() AS dummy,
87− -- *
88− -- FROM
89− -- (VALUES ('a'), ('b'), ('c')) Q1(a)) Q1,
90− -- LATERAL (SELECT
91− -- CASE
92− -- WHEN dummy IS NOT NULL
93− -- THEN nextval('user_id_seq')
94− -- END) Q2(b);
95− --
96− -- We use random() here as the dummy value (and not some constant) because
97− -- Postgres will again optimize if it sees that a value is constant
98− -- (and thus only call nextval() once), but because random() is marked as
99− -- VOLATILE, this inhibits Postgres from doing that optimisation.
100− --
101− -- Why not just reference the a column from the previous query directly
102− -- instead of adding a dummy value? Basically, even if we extract out all
103− -- the bindings introduced in a PrimQuery, we can't always be sure which
104− -- ones refer to constant values, so if we end up laterally referencing a
105− -- constant value, then all of this would be for nothing.
106− --
107− -- Why not just add the call to the previous subselect directly, like so:
108− --
109− -- SELECT
110− -- a, b
111− -- FROM
112− -- (SELECT
113− -- nextval('user_id_seq') AS eval,
114− -- *
115− -- FROM
116− -- (VALUES ('a'), ('b'), ('c')) Q1(a)) Q1,
117− -- LATERAL (SELECT eval) Q2(b);
118− --
119− -- That would work in this case. But consider the following Rel8 code:
120− --
121− -- do
122− -- x <- values [lit 'a', lit 'b', lit 'c']
123− -- y <- values [lit 'd', lit 'e', lit 'f']
124− -- z <- evaluate $ nextval "user_id_seq"
125− -- pure (x, y, z)
126− --
127− -- How many calls to nextval should there be? Our Haskell intuition says
128− -- nine. But that's not what you would get if you used the above
129− -- technique. The problem is, which VALUES query should the nextval be
130− -- added to? You can choose one or the other to get three calls to
131− -- nextval, but you still need to make a superfluous LATERAL references to
132− -- the other if you want nine calls. So for the above Rel8 code we generate
133− -- the following SQL:
134− --
135− -- SELECT
136− -- a, b, c
137− -- FROM
138− -- (SELECT
139− -- random() AS dummy,
140− -- *
141− -- FROM
142− -- (VALUES ('a'), ('b'), ('c')) Q1(a)) Q1,
143− -- (SELECT
144− -- random() AS dummy,
145− -- *
146− -- FROM
147− -- (VALUES ('d'), ('e'), ('f')) Q2(b)) Q2,
148− -- LATERAL (SELECT
149− -- CASE
150− -- WHEN Q1.dummy IS NOT NULL AND Q2.dummy IS NOT NULL
151− -- THEN nextval('user_id_seq')
152− -- END) Q3(c);
153− --
154− -- This gives nine calls to nextval() as we would expect.
155− [Opaleye.PrimExpr] -> Opaleye.Select (Any, a)
156− )
157−
158−
159−instance Projectable Query where
160− project f = fmap (fromColumns . apply f . toColumns)
161−
162−
163−instance Functor Query where
164− fmap f (Query a) = Query (fmap (fmap (fmap f)) a)
165−
166−
167−instance Apply Query where
168− (<.>) = (<*>)
169−
170−
171−instance Applicative Query where
172− pure = fromOpaleye . pure
173− liftA2 = liftM2
33+newtype Query a = Query (Opaleye.Select a)
34+ deriving newtype (Functor, Applicative, Monad)
35+ deriving Apply via (WrappedApplicative Opaleye.Select)
17436
17537
17638instance Bind Query where
17739 (>>-) = (>>=)
17840
17941
180−instance Monad Query where
181− Query q >>= f = Query $ \dummies -> Opaleye.stateQueryArr $ \_ tag ->
182− let
183− qa = q dummies
184− ((m, a), query, tag') = Opaleye.runStateQueryArr qa () tag
185− Query q' = f a
186− (dummies', query', tag'') =
187− ( dummy : dummies
188− , query <> Opaleye.aRebind bindings
189− , Opaleye.next tag'
190− )
191− where
192− (dummy, bindings) = Opaleye.run $ name random
193− where
194− random = Opaleye.FunExpr "random" []
195− name = Opaleye.extractAttr "dummy" tag'
196− qa' = Opaleye.lateral $ \_ -> q' dummies'
197− ((m'@(Any needsDummies), b), query'', tag''') = Opaleye.runStateQueryArr qa' () tag''
198− query'''
199− | needsDummies = query' <> query''
200− | otherwise = query <> query''
201− m'' = m <> m'
202− in
203− ((m'', b), query''', tag''')
204−
205−
20642-- | '<|>:' = 'unionAll'.
20743instance AltTable Query where
20844 (<|>:) = unionAll
@@ -211,13 +47,3 @@ instance AltTable Query where
21147-- | 'emptyTable' = 'values' @[]@.
21248instance AlternativeTable Query where
21349 emptyTable = values []
214−
215−
216−-- | '<>' = 'unionAll'.
217−instance Table Expr a => Semigroup (Query a) where
218− (<>) = (<|>:)
219−
220−
221−-- | 'mempty' = @'values' []@.
222−instance Table Expr a => Monoid (Query a) where
223− mempty = emptyTable
src/Rel8/Query.hs-boot+1−3
@@ -7,13 +7,11 @@ where
77
88-- base
99import Data.Kind ( Type )
10−import Data.Monoid ( Any )
1110import Prelude ()
1211
1312-- opaleye
14−import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
1513import qualified Opaleye.Select as Opaleye
1614
1715
1816type Query :: Type -> Type
19−newtype Query a = Query ([Opaleye.PrimExpr] -> Opaleye.Select (Any, a))
17+newtype Query a = Query (Opaleye.Select a)
src/Rel8/Query/Aggregate.hs−19
@@ -1,17 +1,13 @@
11{-# language FlexibleContexts #-}
22{-# language MonoLocalBinds #-}
3−{-# language ScopedTypeVariables #-}
4−{-# language TypeApplications #-}
53
64module Rel8.Query.Aggregate
75 ( aggregate
86 , countRows
9− , mode
107 )
118where
129
1310-- base
14−import Data.Functor.Contravariant ( (>$<) )
1511import Data.Int ( Int64 )
1612import Prelude
1713
@@ -22,16 +18,9 @@ import qualified Opaleye.Aggregate as Opaleye
2218import Rel8.Aggregate ( Aggregates )
2319import Rel8.Expr ( Expr )
2420import Rel8.Expr.Aggregate ( countStar )
25−import Rel8.Expr.Order ( desc )
2621import Rel8.Query ( Query )
27−import Rel8.Query.Limit ( limit )
2822import Rel8.Query.Maybe ( optional )
2923import Rel8.Query.Opaleye ( mapOpaleye )
30−import Rel8.Query.Order ( orderBy )
31−import Rel8.Table ( toColumns )
32−import Rel8.Table.Aggregate ( hgroupBy )
33−import Rel8.Table.Cols ( Cols( Cols ), fromCols )
34−import Rel8.Table.Eq ( EqTable, eqTable )
3524import Rel8.Table.Opaleye ( aggregator )
3625import Rel8.Table.Maybe ( maybeTable )
3726
@@ -46,11 +35,3 @@ aggregate = mapOpaleye (Opaleye.aggregate aggregator)
4635-- will return @0@.
4736countRows :: Query a -> Query (Expr Int64)
4837countRows = fmap (maybeTable 0 id) . optional . aggregate . fmap (const countStar)
49−
50−
51−-- | Return the most common row in a query.
52−mode :: forall a. EqTable a => Query a -> Query a
53−mode rows = limit 1 $ fmap (fromCols . snd) $ orderBy (fst >$< desc) $ do
54− aggregate $ do
55− row <- toColumns <$> rows
56− pure (countStar, Cols $ hgroupBy (eqTable @a) row)
src/Rel8/Query/Distinct.hs+9−4
@@ -1,3 +1,5 @@
1+{-# options_ghc -fno-warn-redundant-constraints #-}
2+
13module Rel8.Query.Distinct
24 ( distinct
35 , distinctOn
@@ -6,11 +8,12 @@ module Rel8.Query.Distinct
68where
79
810-- base
9−import Prelude ()
11+import Prelude
1012
1113-- opaleye
1214import qualified Opaleye.Distinct as Opaleye
13−import qualified Opaleye.Order as Opaleye
15+import qualified Opaleye.Internal.Order as Opaleye
16+import qualified Opaleye.Internal.QueryArr as Opaleye
1417
1518-- rel8
1619import Rel8.Order ( Order( Order ) )
@@ -30,11 +33,13 @@ distinct = mapOpaleye (Opaleye.distinctExplicit distinctspec)
3033-- to a projection. If multiple rows have the same projection, it is
3134-- unspecified which row will be returned. If this matters, use 'distinctOnBy'.
3235distinctOn :: EqTable b => (a -> b) -> Query a -> Query a
33−distinctOn proj = mapOpaleye (Opaleye.distinctOnExplicit unpackspec proj)
36+distinctOn proj =
37+ mapOpaleye (\q -> Opaleye.productQueryArr (Opaleye.distinctOn unpackspec proj . Opaleye.runSimpleQueryArr q))
3438
3539
3640-- | Select all distinct rows from a query, where rows are equivalent according
3741-- to a projection. If there are multiple rows with the same projection, the
3842-- first row according to the specified 'Order' will be returned.
3943distinctOnBy :: EqTable b => (a -> b) -> Order a -> Query a -> Query a
40−distinctOnBy proj (Order order) = mapOpaleye (Opaleye.distinctOnByExplicit unpackspec proj order)
44+distinctOnBy proj (Order order) =
45+ mapOpaleye (\q -> Opaleye.productQueryArr (Opaleye.distinctOnBy unpackspec proj order . Opaleye.runSimpleQueryArr q))
src/Rel8/Query/Each.hs+3−3
@@ -17,7 +17,7 @@ import Rel8.Query ( Query )
1717import Rel8.Query.Opaleye ( fromOpaleye )
1818import Rel8.Schema.Name ( Selects )
1919import Rel8.Schema.Table ( TableSchema )
20−import Rel8.Table.Cols ( fromCols, toCols )
20+import Rel8.Table ( fromColumns, toColumns )
2121import Rel8.Table.Opaleye ( table, unpackspec )
2222
2323
@@ -25,8 +25,8 @@ import Rel8.Table.Opaleye ( table, unpackspec )
2525-- table@.
2626each :: Selects names exprs => TableSchema names -> Query exprs
2727each =
28− fmap fromCols .
28+ fmap fromColumns .
2929 fromOpaleye .
3030 Opaleye.selectTableExplicit unpackspec .
3131 table .
32− fmap toCols
32+ fmap toColumns
src/Rel8/Query/Either.hs+6−10
@@ -10,11 +10,7 @@ where
1010-- base
1111import Prelude
1212
13−-- comonad
14−import Control.Comonad ( extract )
15−
1613-- rel8
17−import Rel8.Expr ( Expr )
1814import Rel8.Expr.Eq ( (==.) )
1915import Rel8.Query ( Query )
2016import Rel8.Query.Filter ( where_ )
@@ -27,17 +23,17 @@ import Rel8.Table.Maybe ( MaybeTable( MaybeTable ), isJustTable )
2723
2824
2925-- | Filter 'EitherTable's, keeping only 'leftTable's.
30−keepLeftTable :: EitherTable Expr a b -> Query a
26+keepLeftTable :: EitherTable a b -> Query a
3127keepLeftTable e@(EitherTable _ a _) = do
3228 where_ $ isLeftTable e
33− pure (extract a)
29+ pure a
3430
3531
3632-- | Filter 'EitherTable's, keeping only 'rightTable's.
37−keepRightTable :: EitherTable Expr a b -> Query b
33+keepRightTable :: EitherTable a b -> Query b
3834keepRightTable e@(EitherTable _ _ b) = do
3935 where_ $ isRightTable e
40− pure (extract b)
36+ pure b
4137
4238
4339-- | @bitraverseEitherTable f g x@ will pass all @leftTable@s through @f@ and
@@ -59,8 +55,8 @@ keepRightTable e@(EitherTable _ _ b) = do
5955bitraverseEitherTable :: ()
6056 => (a -> Query c)
6157 -> (b -> Query d)
62− -> EitherTable Expr a b
63− -> Query (EitherTable Expr c d)
58+ -> EitherTable a b
59+ -> Query (EitherTable c d)
6460bitraverseEitherTable f g e@(EitherTable tag _ _) = do
6561 mc@(MaybeTable _ c) <- optional (f =<< keepLeftTable e)
6662 md@(MaybeTable _ d) <- optional (g =<< keepRightTable e)
src/Rel8/Query/Evaluate.hs+61−28
@@ -1,49 +1,82 @@
1+{-# language DerivingStrategies #-}
12{-# language FlexibleContexts #-}
2−{-# language TupleSections #-}
3+{-# language GeneralizedNewtypeDeriving #-}
4+{-# language NamedFieldPuns #-}
5+{-# language StandaloneKindSignatures #-}
36
47module Rel8.Query.Evaluate
5− ( evaluate
8+ ( Evaluate
9+ , eval
10+ , evaluate
611 )
712where
813
914-- base
10−import Control.Monad ( (>=>) )
11−import Data.Foldable ( foldl' )
12−import Data.List.NonEmpty ( NonEmpty( (:|) ), nonEmpty )
13−import Data.Monoid ( Any( Any ) )
14−import Prelude hiding ( undefined )
15+import Data.Kind ( Type )
16+import Data.Monoid ( Endo ( Endo ), appEndo )
17+import Prelude
1518
1619-- opaleye
1720import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
21+import qualified Opaleye.Internal.PackMap as Opaleye
22+import qualified Opaleye.Internal.PrimQuery as Opaleye
23+import qualified Opaleye.Internal.QueryArr as Opaleye
24+import qualified Opaleye.Internal.Tag as Opaleye
25+import qualified Opaleye.Internal.Unpackspec as Opaleye
1826
1927-- rel8
2028import Rel8.Expr ( Expr )
21−import Rel8.Expr.Bool ( (&&.) )
22−import Rel8.Expr.Opaleye ( fromPrimExpr )
2329import Rel8.Query ( Query( Query ) )
24−import Rel8.Query.Rebind ( rebind )
2530import Rel8.Table ( Table )
26−import Rel8.Table.Bool ( case_ )
27−import Rel8.Table.Undefined ( undefined )
31+import Rel8.Table.Opaleye ( unpackspec )
2832
33+-- semigroupoids
34+import Data.Functor.Apply ( Apply )
35+import Data.Functor.Bind ( Bind, (>>-) )
2936
30−-- | 'evaluate' takes expressions that could potentially have side effects and
31−-- \"runs\" them in the 'Query' monad. The returned expressions have no side
32−-- effects and can safely be reused.
33−evaluate :: Table Expr a => a -> Query a
34−evaluate = laterally >=> rebind "eval"
37+-- transformers
38+import Control.Monad.Trans.State.Strict ( State, get, put, runState )
3539
3640
37−laterally :: Table Expr a => a -> Query a
38−laterally a = Query $ \bindings -> pure $ (Any True,) $
39− case nonEmpty bindings of
40− Nothing -> a
41− Just bindings' -> case_ [(condition, a)] undefined
42− where
43− condition = foldl1' (&&.) (fmap go bindings')
44− where
45− go = fromPrimExpr . Opaleye.UnExpr Opaleye.OpIsNotNull
41+type Evaluations :: Type
42+data Evaluations = Evaluations
43+ { tag :: !Opaleye.Tag
44+ , bindings :: !(Endo (Opaleye.Bindings Opaleye.PrimExpr))
45+ }
4646
4747
48−foldl1' :: (a -> a -> a) -> NonEmpty a -> a
49−foldl1' f (a :| as) = foldl' f a as
48+-- | Some PostgreSQL functions, such as 'Rel8.nextval', have side effects,
49+-- breaking the referential transparency we would otherwise enjoy.
50+--
51+-- To try to recover our ability to reason about such expressions, 'Evaluate'
52+-- allows us to control the evaluation order of side-effects by sequencing
53+-- them monadically.
54+type Evaluate :: Type -> Type
55+newtype Evaluate a = Evaluate (State Evaluations a)
56+ deriving newtype (Functor, Apply, Applicative, Monad)
57+
58+
59+instance Bind Evaluate where
60+ (>>-) = (>>=)
61+
62+
63+-- | 'eval' takes expressions that could potentially have side effects and
64+-- \"runs\" them in the 'Evaluate' monad. The returned expressions have no
65+-- side effetcs and can safely be reused.
66+eval :: Table Expr a => a -> Evaluate a
67+eval a = Evaluate $ do
68+ Evaluations {tag, bindings} <- get
69+ let
70+ tag' = Opaleye.next tag
71+ (a', bindings') = Opaleye.run $
72+ Opaleye.runUnpackspec unpackspec (Opaleye.extractAttr "eval" tag') a
73+ put Evaluations {tag = tag', bindings = bindings <> Endo (bindings' ++)}
74+ pure a'
75+
76+
77+-- | 'evaluate' runs an 'Evaluate' inside the 'Query' monad.
78+evaluate :: Evaluate a -> Query a
79+evaluate (Evaluate m) = Query $ Opaleye.QueryArr $ \(_, query, tag) ->
80+ case runState m (Evaluations tag mempty) of
81+ (a, Evaluations {tag = tag', bindings}) ->
82+ (a, Opaleye.Rebind True (appEndo bindings mempty) query, tag')
src/Rel8/Query/Exists.hs+16−14
@@ -2,8 +2,8 @@
22
33module Rel8.Query.Exists
44 ( exists, inQuery
5− , present, with, withBy
6− , absent, without, withoutBy
5+ , whereExists, with, withBy
6+ , whereNotExists, without, withoutBy
77 )
88where
99
@@ -11,44 +11,46 @@ where
1111import Prelude hiding ( filter )
1212
1313-- opaleye
14−import qualified Opaleye.Exists as Opaleye
1514import qualified Opaleye.Operators as Opaleye
1615
1716-- rel8
1817import Rel8.Expr ( Expr )
19−import Rel8.Expr.Opaleye ( fromColumn, fromPrimExpr )
2018import Rel8.Query ( Query )
2119import Rel8.Query.Filter ( filter )
20+import Rel8.Query.Maybe ( optional )
2221import Rel8.Query.Opaleye ( mapOpaleye )
2322import Rel8.Table.Eq ( EqTable, (==:) )
23+import Rel8.Table.Maybe ( isJustTable )
2424
2525
2626-- | Checks if a query returns at least one row.
2727exists :: Query a -> Query (Expr Bool)
28−exists = fmap (fromPrimExpr . fromColumn) . mapOpaleye Opaleye.exists
28+exists = fmap isJustTable . optional . whereExists
29+-- FIXME: change this when b7aacc07c6392654cae439fc3b997620c3aa7a87 makes it
30+-- into a release of Opaleye
2931
3032
3133inQuery :: EqTable a => a -> Query a -> Query (Expr Bool)
3234inQuery a = exists . (>>= filter (a ==:))
3335
3436
35−-- | Produce the empty query if the given query returns no rows. @present@
37+-- | Produce the empty query if the given query returns no rows. @whereExists@
3638-- is equivalent to @WHERE EXISTS@ in SQL.
37−present :: Query a -> Query ()
38−present = mapOpaleye Opaleye.restrictExists
39+whereExists :: Query a -> Query ()
40+whereExists = mapOpaleye Opaleye.restrictExists
3941
4042
41−-- | Produce the empty query if the given query returns rows. @absent@
43+-- | Produce the empty query if the given query returns rows. @whereNotExists@
4244-- is equivalent to @WHERE NOT EXISTS@ in SQL.
43−absent :: Query a -> Query ()
44−absent = mapOpaleye Opaleye.restrictNotExists
45+whereNotExists :: Query a -> Query ()
46+whereNotExists = mapOpaleye Opaleye.restrictNotExists
4547
4648
4749-- | @with@ is similar to 'filter', but allows the predicate to be a full query.
4850--
49−-- @with f a = a <$ present (f a)@, but this form matches 'filter'.
51+-- @with f a = a <$ whereExists (f a)@, but this form matches 'filter'.
5052with :: (a -> Query b) -> a -> Query a
51−with f a = a <$ present (f a)
53+with f a = a <$ whereExists (f a)
5254
5355
5456-- | Like @with@, but with a custom membership test.
@@ -58,7 +60,7 @@ withBy predicate bs = with $ \a -> bs >>= filter (predicate a)
5860
5961-- | Filter rows where @a -> Query b@ yields no rows.
6062without :: (a -> Query b) -> a -> Query a
61−without f a = a <$ absent (f a)
63+without f a = a <$ whereNotExists (f a)
6264
6365
6466-- | Like @without@, but with a custom membership test.
src/Rel8/Query/Indexed.hsdeleted−21
@@ -1,21 +0,0 @@
1−module Rel8.Query.Indexed
2− ( indexed
3− )
4−where
5−
6−-- base
7−import Control.Applicative ( liftA2 )
8−import Data.Int ( Int64 )
9−import Prelude
10−
11−-- rel8
12−import Rel8.Expr ( Expr )
13−import Rel8.Expr.Window ( rowNumber )
14−import Rel8.Query ( Query )
15−import Rel8.Query.Window ( window )
16−import Rel8.Table.Window ( currentRow )
17−
18−
19−-- | Pair each row of a query with its index within the query.
20−indexed :: Query a -> Query (Expr Int64, a)
21−indexed = window (liftA2 (,) (subtract 1 <$> rowNumber) currentRow)
src/Rel8/Query/List.hs+15−19
@@ -19,18 +19,16 @@ import Prelude
1919import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
2020
2121-- rel8
22−import Rel8.Expr ( Expr )
22+import Rel8.Expr ( Col( E, unE ), Expr )
2323import Rel8.Expr.Aggregate ( listAggExpr, nonEmptyAggExpr )
2424import Rel8.Expr.Opaleye ( mapPrimExpr )
2525import Rel8.Query ( Query )
2626import Rel8.Query.Aggregate ( aggregate )
2727import Rel8.Query.Maybe ( optional )
28−import Rel8.Query.Rebind ( rebind )
2928import Rel8.Schema.HTable.Vectorize ( hunvectorize )
3029import Rel8.Schema.Null ( Sql, Unnullify )
31−import Rel8.Schema.Spec ( Spec( Spec, info ) )
32−import Rel8.Table ( Table, fromColumns )
33−import Rel8.Table.Cols ( toCols )
30+import Rel8.Schema.Spec ( SSpec( SSpec, info ) )
31+import Rel8.Table ( Table, fromColumns, toColumns )
3432import Rel8.Table.Aggregate ( listAgg, nonEmptyAgg )
3533import Rel8.Table.List ( ListTable( ListTable ) )
3634import Rel8.Table.Maybe ( maybeTable )
@@ -47,12 +45,12 @@ import Rel8.Type.Information ( TypeInformation )
4745--
4846-- @many@ is analogous to 'Control.Applicative.many' from
4947-- @Control.Applicative@.
50−many :: Table Expr a => Query a -> Query (ListTable Expr a)
48+many :: Table Expr a => Query a -> Query (ListTable a)
5149many =
5250 fmap (maybeTable mempty (\(ListTable a) -> ListTable a)) .
5351 optional .
5452 aggregate .
55− fmap (listAgg . toCols)
53+ fmap (listAgg . toColumns)
5654
5755
5856-- | Aggregate a 'Query' into a 'NonEmptyTable'. If the supplied query returns
@@ -63,11 +61,11 @@ many =
6361--
6462-- @some@ is analogous to 'Control.Applicative.some' from
6563-- @Control.Applicative@.
66−some :: Table Expr a => Query a -> Query (NonEmptyTable Expr a)
64+some :: Table Expr a => Query a -> Query (NonEmptyTable a)
6765some =
6866 fmap (\(NonEmptyTable a) -> NonEmptyTable a) .
6967 aggregate .
70− fmap (nonEmptyAgg . toCols)
68+ fmap (nonEmptyAgg . toColumns)
7169
7270
7371-- | A version of 'many' specialised to single expressions.
@@ -84,20 +82,18 @@ someExpr = aggregate . fmap nonEmptyAggExpr
8482-- element of the given @ListTable@.
8583--
8684-- @catListTable@ is an inverse to 'many'.
87−catListTable :: Table Expr a => ListTable Expr a -> Query a
88−catListTable (ListTable as) =
89− rebind "unnest" $ fromColumns $ runIdentity $
90− hunvectorize (\Spec {info} -> pure . sunnest info) as
85+catListTable :: Table Expr a => ListTable a -> Query a
86+catListTable (ListTable as) = pure $ fromColumns $ runIdentity $
87+ hunvectorize (\SSpec {info} -> pure . E . sunnest info . unE) as
9188
9289
9390-- | Expand a 'NonEmptyTable' into a 'Query', where each row in the query is an
9491-- element of the given @NonEmptyTable@.
9592--
9693-- @catNonEmptyTable@ is an inverse to 'some'.
97−catNonEmptyTable :: Table Expr a => NonEmptyTable Expr a -> Query a
98−catNonEmptyTable (NonEmptyTable as) =
99− rebind "unnest" $ fromColumns $ runIdentity $
100− hunvectorize (\Spec {info} -> pure . sunnest info) as
94+catNonEmptyTable :: Table Expr a => NonEmptyTable a -> Query a
95+catNonEmptyTable (NonEmptyTable as) = pure $ fromColumns $ runIdentity $
96+ hunvectorize (\SSpec {info} -> pure . E . sunnest info . unE) as
10197
10298
10399-- | Expand an expression that contains a list into a 'Query', where each row
@@ -105,7 +101,7 @@ catNonEmptyTable (NonEmptyTable as) =
105101--
106102-- @catList@ is an inverse to 'manyExpr'.
107103catList :: Sql DBType a => Expr [a] -> Query (Expr a)
108−catList = rebind "unnest" . sunnest typeInformation
104+catList = pure . sunnest typeInformation
109105
110106
111107-- | Expand an expression that contains a non-empty list into a 'Query', where
@@ -113,7 +109,7 @@ catList = rebind "unnest" . sunnest typeInformation
113109--
114110-- @catNonEmpty@ is an inverse to 'someExpr'.
115111catNonEmpty :: Sql DBType a => Expr (NonEmpty a) -> Query (Expr a)
116−catNonEmpty = rebind "unnest" . sunnest typeInformation
112+catNonEmpty = pure . sunnest typeInformation
117113
118114
119115sunnest :: TypeInformation (Unnullify a) -> Expr (list a) -> Expr a
src/Rel8/Query/Maybe.hs+29−24
@@ -1,7 +1,3 @@
1−{-# language GADTs #-}
2−{-# language LambdaCase #-}
3−{-# language NamedFieldPuns #-}
4−
51module Rel8.Query.Maybe
62 ( optional
73 , catMaybeTable
@@ -12,20 +8,23 @@ where
128-- base
139import Prelude
1410
15−-- comonad
16−import Control.Comonad ( extract )
17−
1811-- opaleye
19−import qualified Opaleye.Internal.MaybeFields as Opaleye
12+import qualified Opaleye.Internal.PackMap as Opaleye
13+import qualified Opaleye.Internal.PrimQuery as Opaleye
14+import qualified Opaleye.Internal.QueryArr as Opaleye
15+import qualified Opaleye.Internal.Tag as Opaleye
16+import qualified Opaleye.Internal.Unpackspec as Opaleye
2017
2118-- rel8
22−import Rel8.Expr ( Expr )
19+import Rel8.Expr.Bool ( true )
2320import Rel8.Expr.Eq ( (==.) )
24−import Rel8.Expr.Opaleye ( fromColumn, fromPrimExpr )
21+import Rel8.Expr.Opaleye ( toPrimExpr )
2522import Rel8.Query ( Query )
2623import Rel8.Query.Filter ( where_ )
2724import Rel8.Query.Opaleye ( mapOpaleye )
28−import Rel8.Table.Maybe ( MaybeTable(..), isJustTable )
25+import Rel8.Table.Maybe ( MaybeTable( MaybeTable ), isJustTable )
26+import Rel8.Table.Opaleye ( unpackspec )
27+import Rel8.Table.Tag ( Tag(..), fromExpr )
2928
3029
3130-- | Convert a query that might return zero rows to a query that always returns
@@ -33,11 +32,18 @@ import Rel8.Table.Maybe ( MaybeTable(..), isJustTable )
3332--
3433-- To speak in more concrete terms, 'optional' is most useful to write @LEFT
3534-- JOIN@s.
36−optional :: Query a -> Query (MaybeTable Expr a)
37−optional = mapOpaleye $ Opaleye.optionalInternal $ \tag a -> MaybeTable
38− { tag = fromPrimExpr $ fromColumn tag
39− , just = pure a
40− }
35+optional :: Query a -> Query (MaybeTable a)
36+optional = mapOpaleye $ Opaleye.QueryArr . go
37+ where
38+ go query (i, left, tag) =
39+ (MaybeTable (fromExpr t') a, join, Opaleye.next tag')
40+ where
41+ (MaybeTable Tag {expr = t} a, right, tag') =
42+ Opaleye.runSimpleQueryArr (pure <$> query) (i, tag)
43+ (t', bindings) = Opaleye.run $
44+ Opaleye.runUnpackspec unpackspec (Opaleye.extractAttr "maybe" tag') t
45+ join = Opaleye.Join Opaleye.LeftJoin condition [] bindings left right
46+ condition = toPrimExpr true
4147
4248
4349-- | Filter out 'MaybeTable's, returning only the tables that are not-null.
@@ -45,24 +51,23 @@ optional = mapOpaleye $ Opaleye.optionalInternal $ \tag a -> MaybeTable
4551-- This operation can be used to "undo" the effect of 'optional', which
4652-- operationally is like turning a @LEFT JOIN@ back into a full @JOIN@. You
4753-- can think of this as analogous to 'Data.Maybe.catMaybes'.
48−catMaybeTable :: MaybeTable Expr a -> Query a
54+catMaybeTable :: MaybeTable a -> Query a
4955catMaybeTable ma@(MaybeTable _ a) = do
5056 where_ $ isJustTable ma
51− pure (extract a)
57+ pure a
5258
5359
5460-- | Extend an optional query with another query. This is useful if you want
5561-- to step through multiple @LEFT JOINs@.
5662--
5763-- Note that @traverseMaybeTable@ takes a @a -> Query b@ function, which means
58−-- you also have the ability to "expand" one row into multiple rows. If the
64+-- you also have the ability to "expand" one row into multiple rows. If the
5965-- @a -> Query b@ function returns no rows, then the resulting query will also
6066-- have no rows. However, regardless of the given @a -> Query b@ function, if
6167-- the input is @nothingTable@, you will always get exactly one @nothingTable@
6268-- back.
63−traverseMaybeTable :: (a -> Query b) -> MaybeTable Expr a -> Query (MaybeTable Expr b)
69+traverseMaybeTable :: (a -> Query b) -> MaybeTable a -> Query (MaybeTable b)
6470traverseMaybeTable query ma@(MaybeTable input _) = do
65− optional (query =<< catMaybeTable ma) >>= \case
66− MaybeTable output b -> do
67− where_ $ output ==. input
68− pure $ MaybeTable input b
71+ MaybeTable output b <- optional (query =<< catMaybeTable ma)
72+ where_ $ expr output ==. expr input
73+ pure $ MaybeTable input b
src/Rel8/Query/Null.hs−15
@@ -1,8 +1,5 @@
1−{-# language FlexibleContexts #-}
2−
31module Rel8.Query.Null
42 ( catNull
5− , catNullTable
63 )
74where
85
@@ -12,8 +9,6 @@ import Prelude
129-- rel8
1310import Rel8.Expr ( Expr )
1411import Rel8.Expr.Null ( isNonNull, unsafeUnnullify )
15−import Rel8.Table ( Table )
16−import Rel8.Table.Null ( NullTable, isNonNullTable, unsafeUnnullifyTable )
1712import Rel8.Query ( Query )
1813import Rel8.Query.Filter ( where_ )
1914
@@ -26,13 +21,3 @@ catNull :: Expr (Maybe a) -> Query (Expr a)
2621catNull a = do
2722 where_ $ isNonNull a
2823 pure $ unsafeUnnullify a
29−
30−
31−-- | Filter a 'Query' that might return @nullTable@ to a 'Query' without any
32−-- @nullTable@s.
33−--
34−-- Corresponds to 'Data.Maybe.catMaybes'.
35−catNullTable :: Table Expr a => NullTable Expr a -> Query a
36−catNullTable a = do
37− where_ $ isNonNullTable a
38− pure $ unsafeUnnullifyTable a
src/Rel8/Query/Opaleye.hs+5−41
@@ -1,70 +1,34 @@
1−{-# language TupleSections #-}
2−
31module Rel8.Query.Opaleye
42 ( fromOpaleye
53 , toOpaleye
64 , mapOpaleye
75 , zipOpaleyeWith
8− , unsafePeekQuery
96 )
107where
118
129-- base
13−import Control.Applicative ( liftA2 )
1410import Prelude
1511
1612-- opaleye
17−import qualified Opaleye.Internal.QueryArr as Opaleye
18−import qualified Opaleye.Internal.Tag as Opaleye
13+import qualified Opaleye.Select as Opaleye
1914
2015-- rel8
2116import {-# SOURCE #-} Rel8.Query ( Query( Query ) )
2217
2318
2419fromOpaleye :: Opaleye.Select a -> Query a
25−fromOpaleye = Query . pure . fmap pure
20+fromOpaleye = Query
2621
2722
2823toOpaleye :: Query a -> Opaleye.Select a
29−toOpaleye (Query a) = snd <$> a mempty
24+toOpaleye (Query a) = a
3025
3126
3227mapOpaleye :: (Opaleye.Select a -> Opaleye.Select b) -> Query a -> Query b
33−mapOpaleye f (Query a) = Query (fmap (mapping f) a)
28+mapOpaleye f = fromOpaleye . f . toOpaleye
3429
3530
3631zipOpaleyeWith :: ()
3732 => (Opaleye.Select a -> Opaleye.Select b -> Opaleye.Select c)
3833 -> Query a -> Query b -> Query c
39−zipOpaleyeWith f (Query a) (Query b) = Query $ liftA2 (zipping f) a b
40−
41−
42−unsafePeekQuery :: Query a -> a
43−unsafePeekQuery (Query q) = case q mempty of
44− f -> case Opaleye.runStateQueryArr f () Opaleye.start of
45− ((_, a), _, _) -> a
46−
47−
48−mapping :: ()
49− => (Opaleye.Select a -> Opaleye.Select b)
50− -> Opaleye.Select (m, a) -> Opaleye.Select (m, b)
51−mapping f q = Opaleye.stateQueryArr $ \_ tag ->
52− let
53− ((m, _), _, _) = Opaleye.runStateQueryArr q () tag
54− q' = (m,) <$> f (snd <$> q)
55− in
56− Opaleye.runStateQueryArr q' () tag
57−
58−
59−zipping :: Semigroup m
60− => (Opaleye.Select a -> Opaleye.Select b -> Opaleye.Select c)
61− -> Opaleye.Select (m, a) -> Opaleye.Select (m, b) -> Opaleye.Select (m, c)
62−zipping f q q' =
63− Opaleye.stateQueryArr $ \_ tag ->
64− let
65− ((m, _), _, _) = Opaleye.runStateQueryArr q () tag
66− ((m', _), _, _) = Opaleye.runStateQueryArr q' () tag
67− m'' = m <> m'
68− q'' = (m'',) <$> f (snd <$> q) (snd <$> q')
69− in
70− Opaleye.runStateQueryArr q'' () tag
34+zipOpaleyeWith f a b = fromOpaleye $ f (toOpaleye a) (toOpaleye b)
src/Rel8/Query/Rebind.hsdeleted−27
@@ -1,27 +0,0 @@
1−{-# language FlexibleContexts #-}
2−
3−module Rel8.Query.Rebind
4− ( rebind
5− )
6−where
7−
8−-- base
9−import Prelude
10−import Control.Arrow ((<<<))
11−
12−-- opaleye
13−import qualified Opaleye.Internal.Rebind as Opaleye
14−
15−-- rel8
16−import Rel8.Expr ( Expr )
17−import Rel8.Query ( Query )
18−import Rel8.Table ( Table )
19−import Rel8.Table.Opaleye ( unpackspec )
20−import Rel8.Query.Opaleye (fromOpaleye)
21−
22−
23−-- | 'rebind' takes a variable name, some expressions, and binds each of them
24−-- to a new variable in the SQL. The @a@ returned consists only of these
25−-- variables. It's essentially a @let@ binding for Postgres expressions.
26−rebind :: Table Expr a => String -> a -> Query a
27−rebind prefix a = fromOpaleye (Opaleye.rebindExplicitPrefix prefix unpackspec <<< pure a)
src/Rel8/Query/SQL.hs+59−6
@@ -1,21 +1,74 @@
11{-# language FlexibleContexts #-}
2−{-# language MonoLocalBinds #-}
2+{-# language TypeFamilies #-}
3+{-# language ViewPatterns #-}
34
45module Rel8.Query.SQL
56 ( showQuery
7+ , sqlForQuery, sqlForQueryWithNames
68 )
79where
810
911-- base
12+import Data.Foldable ( fold )
13+import Data.Functor.Const ( Const( Const ), getConst )
14+import Data.Void ( Void )
1015import Prelude
1116
17+-- opaleye
18+import qualified Opaleye.Internal.HaskellDB.Sql as Opaleye
19+import qualified Opaleye.Internal.PrimQuery as Opaleye
20+import qualified Opaleye.Internal.Print as Opaleye
21+import qualified Opaleye.Internal.Optimize as Opaleye
22+import qualified Opaleye.Internal.QueryArr as Opaleye hiding ( Select )
23+import qualified Opaleye.Internal.Sql as Opaleye
24+
1225-- rel8
13−import Rel8.Expr ( Expr )
26+import Rel8.Expr ( Expr, Col( E ) )
27+import Rel8.Expr.Opaleye ( toPrimExpr )
1428import Rel8.Query ( Query )
15−import Rel8.Statement.Select ( ppSelect )
16−import Rel8.Table ( Table )
29+import Rel8.Query.Opaleye ( toOpaleye )
30+import Rel8.Schema.Name ( Name( Name ), Selects, Col( N ) )
31+import Rel8.Schema.HTable ( htabulateA, hfield )
32+import Rel8.Table ( Table, toColumns )
33+import Rel8.Table.Name ( namesFromLabels )
34+import Rel8.Table.Opaleye ( castTable )
1735
1836
19−-- | Convert a 'Query' to a 'String' containing a @SELECT@ statement.
37+-- | Convert a query to a 'String' containing the query as a @SELECT@
38+-- statement.
2039showQuery :: Table Expr a => Query a -> String
21−showQuery = show . ppSelect
40+showQuery = fold . sqlForQuery
41+
42+
43+sqlForQuery :: Table Expr a
44+ => Query a -> Maybe String
45+sqlForQuery = sqlForQueryWithNames namesFromLabels . fmap toColumns
46+
47+
48+sqlForQueryWithNames :: Selects names exprs
49+ => names -> Query exprs -> Maybe String
50+sqlForQueryWithNames names query =
51+ show . Opaleye.ppSql . selectFrom names exprs <$> optimize primQuery
52+ where
53+ (exprs, primQuery, _) =
54+ Opaleye.runSimpleQueryArrStart (toOpaleye query) ()
55+
56+
57+optimize :: Opaleye.PrimQuery' a -> Maybe (Opaleye.PrimQuery' Void)
58+optimize = Opaleye.removeEmpty . Opaleye.optimize
59+
60+
61+selectFrom :: Selects names exprs
62+ => names -> exprs -> Opaleye.PrimQuery' Void -> Opaleye.Select
63+selectFrom (toColumns -> names) (toColumns . castTable -> exprs) query =
64+ Opaleye.SelectFrom $ Opaleye.newSelect
65+ { Opaleye.attrs = Opaleye.SelectAttrs attributes
66+ , Opaleye.tables = Opaleye.oneTable select
67+ }
68+ where
69+ select = Opaleye.foldPrimQuery Opaleye.sqlQueryGenerator query
70+ attributes = getConst $ htabulateA $ \field -> case hfield names field of
71+ N (Name name) -> case hfield exprs field of
72+ E (toPrimExpr -> expr) -> Const (pure (makeAttr name expr))
73+ makeAttr label expr =
74+ (Opaleye.sqlExpr expr, Just (Opaleye.SqlColumn label))
src/Rel8/Query/Set.hs+6−6
@@ -23,36 +23,36 @@ import Rel8.Table.Opaleye ( binaryspec )
2323
2424
2525-- | Combine the results of two queries of the same type, collapsing
26−-- duplicates. @union a b@ is the same as the SQL statement @a UNION b@.
26+-- duplicates. @union a b@ is the same as the SQL statement @x UNION b@.
2727union :: EqTable a => Query a -> Query a -> Query a
2828union = zipOpaleyeWith (Opaleye.unionExplicit binaryspec)
2929
3030
3131-- | Combine the results of two queries of the same type, retaining duplicates.
32−-- @unionAll a b@ is the same as the SQL statement @a UNION ALL b@.
32+-- @unionAll a b@ is the same as the SQL statement @x UNION ALL b@.
3333unionAll :: Table Expr a => Query a -> Query a -> Query a
3434unionAll = zipOpaleyeWith (Opaleye.unionAllExplicit binaryspec)
3535
3636
3737-- | Find the intersection of two queries, collapsing duplicates. @intersect a
38−-- b@ is the same as the SQL statement @a INTERSECT b@.
38+-- b@ is the same as the SQL statement @x INTERSECT b@.
3939intersect :: EqTable a => Query a -> Query a -> Query a
4040intersect = zipOpaleyeWith (Opaleye.intersectExplicit binaryspec)
4141
4242
4343-- | Find the intersection of two queries, retaining duplicates. @intersectAll
44−-- a b@ is the same as the SQL statement @a INTERSECT ALL b@.
44+-- a b@ is the same as the SQL statement @x INTERSECT ALL b@.
4545intersectAll :: EqTable a => Query a -> Query a -> Query a
4646intersectAll = zipOpaleyeWith (Opaleye.intersectAllExplicit binaryspec)
4747
4848
4949-- | Find the difference of two queries, collapsing duplicates @except a b@ is
50−-- the same as the SQL statement @a EXCEPT b@.
50+-- the same as the SQL statement @x INTERSECT b@.
5151except :: EqTable a => Query a -> Query a -> Query a
5252except = zipOpaleyeWith (Opaleye.exceptExplicit binaryspec)
5353
5454
5555-- | Find the difference of two queries, retaining duplicates. @exceptAll a b@
56−-- is the same as the SQL statement @a EXCEPT ALL b@.
56+-- is the same as the SQL statement @x EXCEPT ALL b@.
5757exceptAll :: EqTable a => Query a -> Query a -> Query a
5858exceptAll = zipOpaleyeWith (Opaleye.exceptAllExplicit binaryspec)
src/Rel8/Query/These.hs+39−52
@@ -1,5 +1,4 @@
11{-# language FlexibleContexts #-}
2−{-# language GADTs #-}
32
43module Rel8.Query.These
54 ( alignBy
@@ -15,29 +14,27 @@ where
1514-- base
1615import Prelude
1716
18−-- comonad
19−import Control.Comonad ( extract )
20−
2117-- opaleye
22−import qualified Opaleye.Internal.PackMap as Opaleye
18+import qualified Opaleye.Internal.Join as Opaleye
2319import qualified Opaleye.Internal.PrimQuery as Opaleye
24−import qualified Opaleye.Internal.QueryArr as Opaleye
25−import qualified Opaleye.Internal.Tag as Opaleye
2620
2721-- rel8
2822import Rel8.Expr ( Expr )
2923import Rel8.Expr.Bool ( boolExpr, not_ )
3024import Rel8.Expr.Eq ( (==.) )
31−import Rel8.Expr.Opaleye ( toPrimExpr, traversePrimExpr )
25+import Rel8.Expr.Opaleye ( toColumn, toPrimExpr )
3226import Rel8.Expr.Serialize ( litExpr )
3327import Rel8.Query ( Query )
3428import Rel8.Query.Filter ( where_ )
3529import Rel8.Query.Maybe ( optional )
3630import Rel8.Query.Opaleye ( zipOpaleyeWith )
31+import Rel8.Table ( Table )
3732import Rel8.Table.Either ( EitherTable( EitherTable ) )
3833import Rel8.Table.Maybe ( MaybeTable( MaybeTable ), isJustTable )
34+import Rel8.Table.Opaleye ( unpackspec )
35+import Rel8.Table.Tag ( Tag(..) )
3936import Rel8.Table.These
40− ( TheseTable( TheseTable, here, there )
37+ ( TheseTable( TheseTable )
4138 , hasHereTable, hasThereTable
4239 , isThisTable, isThatTable, isThoseTable
4340 )
@@ -45,91 +42,81 @@ import Rel8.Type.Tag ( EitherTag( IsLeft, IsRight ) )
4542
4643
4744-- | Corresponds to a @FULL OUTER JOIN@ between two queries.
48−alignBy :: ()
45+alignBy :: (Table Expr a, Table Expr b)
4946 => (a -> b -> Expr Bool)
50− -> Query a -> Query b -> Query (TheseTable Expr a b)
51−alignBy condition = zipOpaleyeWith $ \left right -> Opaleye.stateQueryArr $ \_ t -> case t of
52− tag -> (tab, join', tag''')
53− where
54− (ma, left', tag') = Opaleye.runStateQueryArr (pure <$> left) () tag
55− (mb, right', tag'') = Opaleye.runStateQueryArr (pure <$> right) () tag'
56− MaybeTable hasHere a = ma
57− MaybeTable hasThere b = mb
58− (hasHere', lbindings) = Opaleye.run $ do
59− traversePrimExpr (Opaleye.extractAttr "hasHere" tag'') hasHere
60− (hasThere', rbindings) = Opaleye.run $ do
61− traversePrimExpr (Opaleye.extractAttr "hasThere" tag'') hasThere
62− tag''' = Opaleye.next tag''
63− join = Opaleye.Join Opaleye.FullJoin on left'' right''
64− where
65− on = toPrimExpr $ condition (extract a) (extract b)
66− left'' = (Opaleye.NonLateral, Opaleye.toPrimQuery (left' <> Opaleye.aRebind lbindings))
67− right'' = (Opaleye.NonLateral, Opaleye.toPrimQuery (right' <> Opaleye.aRebind rbindings))
68− ma' = MaybeTable hasHere' a
69− mb' = MaybeTable hasThere' b
70− tab = TheseTable {here = ma', there = mb'}
71− join' = Opaleye.aProduct join
72−
73−
74−keepHereTable :: TheseTable Expr a b -> Query (a, MaybeTable Expr b)
47+ -> Query a -> Query b -> Query (TheseTable a b)
48+alignBy condition as bs =
49+ uncurry TheseTable <$> zipOpaleyeWith fullOuterJoin as bs
50+ where
51+ fullOuterJoin a b =
52+ Opaleye.joinExplicit unpackspec unpackspec pure pure full a b on
53+ where
54+ full = Opaleye.FullJoin
55+ on = toColumn . toPrimExpr . uncurry condition
56+
57+
58+-- | Filter 'TheseTable's, keeping only 'thisTable's and 'thoseTable's.
59+keepHereTable :: TheseTable a b -> Query (a, MaybeTable b)
7560keepHereTable = loseThatTable
7661
7762
78−loseHereTable :: TheseTable Expr a b -> Query b
63+-- | Filter 'TheseTable's, keeping on
64+loseHereTable :: TheseTable a b -> Query b
7965loseHereTable = keepThatTable
8066
8167
82−keepThereTable :: TheseTable Expr a b -> Query (MaybeTable Expr a, b)
68+keepThereTable :: TheseTable a b -> Query (MaybeTable a, b)
8369keepThereTable = loseThisTable
8470
8571
86−loseThereTable :: TheseTable Expr a b -> Query a
72+loseThereTable :: TheseTable a b -> Query a
8773loseThereTable = keepThisTable
8874
8975
90−keepThisTable :: TheseTable Expr a b -> Query a
76+keepThisTable :: TheseTable a b -> Query a
9177keepThisTable t@(TheseTable (MaybeTable _ a) _) = do
9278 where_ $ isThisTable t
93− pure (extract a)
79+ pure a
9480
9581
96−loseThisTable :: TheseTable Expr a b -> Query (MaybeTable Expr a, b)
82+loseThisTable :: TheseTable a b -> Query (MaybeTable a, b)
9783loseThisTable t@(TheseTable ma (MaybeTable _ b)) = do
9884 where_ $ not_ $ isThisTable t
99− pure (ma, extract b)
85+ pure (ma, b)
10086
10187
102−keepThatTable :: TheseTable Expr a b -> Query b
88+keepThatTable :: TheseTable a b -> Query b
10389keepThatTable t@(TheseTable _ (MaybeTable _ b)) = do
10490 where_ $ isThatTable t
105− pure (extract b)
91+ pure b
10692
10793
108−loseThatTable :: TheseTable Expr a b -> Query (a, MaybeTable Expr b)
94+loseThatTable :: TheseTable a b -> Query (a, MaybeTable b)
10995loseThatTable t@(TheseTable (MaybeTable _ a) mb) = do
11096 where_ $ not_ $ isThatTable t
111− pure (extract a, mb)
97+ pure (a, mb)
11298
11399
114−keepThoseTable :: TheseTable Expr a b -> Query (a, b)
100+keepThoseTable :: TheseTable a b -> Query (a, b)
115101keepThoseTable t@(TheseTable (MaybeTable _ a) (MaybeTable _ b)) = do
116102 where_ $ isThoseTable t
117− pure (extract a, extract b)
103+ pure (a, b)
118104
119105
120−loseThoseTable :: TheseTable Expr a b -> Query (EitherTable Expr a b)
106+loseThoseTable :: TheseTable a b -> Query (EitherTable a b)
121107loseThoseTable t@(TheseTable (MaybeTable _ a) (MaybeTable _ b)) = do
122108 where_ $ not_ $ isThoseTable t
123− pure $ EitherTable tag a b
109+ pure $ EitherTable result a b
124110 where
125111 tag = boolExpr (litExpr IsLeft) (litExpr IsRight) (isThatTable t)
112+ result = (mempty `asTypeOf` result) {expr = tag}
126113
127114
128115bitraverseTheseTable :: ()
129116 => (a -> Query c)
130117 -> (b -> Query d)
131− -> TheseTable Expr a b
132− -> Query (TheseTable Expr c d)
118+ -> TheseTable a b
119+ -> Query (TheseTable c d)
133120bitraverseTheseTable f g t = do
134121 mc <- optional (f . fst =<< keepHereTable t)
135122 md <- optional (g . snd =<< keepThereTable t)
src/Rel8/Query/Window.hsdeleted−26
@@ -1,26 +0,0 @@
1−module Rel8.Query.Window
2− ( window
3− )
4−where
5−
6−-- base
7−import Prelude ()
8−
9−-- opaleye
10−import qualified Opaleye.Window as Opaleye
11−
12−-- rel8
13−import Rel8.Query ( Query )
14−import Rel8.Query.Opaleye ( mapOpaleye )
15−import Rel8.Window ( Window( Window ) )
16−
17−
18−-- | 'window' runs a query composed of expressions containing
19−-- [window functions](https://www.postgresql.org/docs/current/tutorial-window.html).
20−-- 'window' is similar to 'Rel8.aggregate', with the main difference being
21−-- that in a window query, each input row corresponds to one output row,
22−-- whereas aggregation queries fold the entire input query down into a single
23−-- row. To put this into a Haskell context, 'Rel8.aggregate' is to 'foldl' as
24−-- 'window' is to 'scanl'.
25−window :: Window a b -> Query a -> Query b
26−window (Window a) = mapOpaleye (Opaleye.runWindows a)
src/Rel8/Schema/Context.hsnew file+20
@@ -0,0 +1,20 @@
1+{-# language DataKinds #-}
2+{-# language StandaloneKindSignatures #-}
3+{-# language TypeFamilies #-}
4+
5+module Rel8.Schema.Context
6+ ( Interpretation( Col )
7+ )
8+where
9+
10+-- base
11+import Data.Kind ( Constraint )
12+import Prelude ()
13+
14+-- rel8
15+import Rel8.Schema.Kind ( Context, HContext )
16+
17+
18+type Interpretation :: Context -> Constraint
19+class Interpretation context where
20+ data Col context :: HContext
src/Rel8/Schema/Context/Label.hsnew file+62
@@ -0,0 +1,62 @@
1+{-# language DataKinds #-}
2+{-# language FlexibleInstances #-}
3+{-# language StandaloneKindSignatures #-}
4+{-# language TypeFamilies #-}
5+{-# language TypeOperators #-}
6+
7+module Rel8.Schema.Context.Label
8+ ( Labelable( labeler, unlabeler )
9+ , HLabelable( hlabeler, hunlabeler )
10+ )
11+where
12+
13+-- base
14+import Data.Kind ( Constraint )
15+import Prelude hiding ( null )
16+
17+-- rel8
18+import Rel8.Schema.Context ( Interpretation )
19+import Rel8.Schema.Dict ( Dict( Dict ) )
20+import Rel8.Schema.Kind ( Context, HContext )
21+import Rel8.Schema.Spec ( Spec( Spec ) )
22+import Rel8.Schema.Result ( Col( R ), Result )
23+import Rel8.Schema.Spec.ConstrainDBType ( ConstrainDBType )
24+
25+
26+-- | The @Labelable@ class is an internal implementation detail of Rel8, and
27+-- indicates that we can successfully "name" all columns in a type.
28+type Labelable :: Context -> Constraint
29+class Interpretation context => Labelable context where
30+ labeler :: ()
31+ => Col context ('Spec labels a)
32+ -> Col context ('Spec (label ': labels) a)
33+
34+ unlabeler :: ()
35+ => Col context ('Spec (label ': labels) a)
36+ -> Col context ('Spec labels a)
37+
38+
39+instance Labelable Result where
40+ labeler (R a) = R a
41+ unlabeler (R a) = R a
42+
43+
44+type HLabelable :: HContext -> Constraint
45+class HLabelable context where
46+ hlabeler :: ()
47+ => context ('Spec labels a)
48+ -> context ('Spec (label ': labels) a)
49+
50+ hunlabeler :: ()
51+ => context ('Spec (label ': labels) a)
52+ -> context ('Spec labels a)
53+
54+
55+instance Labelable context => HLabelable (Col context) where
56+ hlabeler = labeler
57+ hunlabeler = unlabeler
58+
59+
60+instance HLabelable (Dict (ConstrainDBType constraint)) where
61+ hlabeler Dict = Dict
62+ hunlabeler Dict = Dict
src/Rel8/Schema/Context/Nullify.hs+149−105
@@ -1,144 +1,188 @@
11{-# language DataKinds #-}
2−{-# language EmptyCase #-}
32{-# language FlexibleContexts #-}
4−{-# language GADTs #-}
5−{-# language LambdaCase #-}
3+{-# language FlexibleInstances #-}
4+{-# language InstanceSigs #-}
5+{-# language MultiParamTypeClasses #-}
66{-# language NamedFieldPuns #-}
7+{-# language ScopedTypeVariables #-}
78{-# language StandaloneKindSignatures #-}
9+{-# language TypeFamilies #-}
810
911module Rel8.Schema.Context.Nullify
10− ( Nullifiability(..), NonNullifiability(..), nullifiableOrNot, absurd
11− , Nullifiable, nullifiability
12− , guarder, nullifier, unnullifier
13− , sguard, snullify
12+ ( Nullifiable( ConstrainTag, encodeTag, decodeTag, nullifier, unnullifier )
13+ , HNullifiable( HConstrainTag, hencodeTag, hdecodeTag, hnullifier, hunnullifier )
14+ , runTag, unnull
1415 )
1516where
1617
1718-- base
18−import Data.Bool ( bool )
19−import Data.Functor.Identity ( Identity( Identity ) )
2019import Data.Kind ( Constraint, Type )
20+import GHC.TypeLits ( KnownSymbol )
2121import Prelude hiding ( null )
2222
2323-- opaleye
2424import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
2525
2626-- rel8
27−import Rel8.Aggregate ( Aggregate(..), zipOutputs )
28−import Rel8.Expr ( Expr )
27+import Rel8.Aggregate
28+ ( Aggregate( Aggregate ), Col( A )
29+ , mapInputs
30+ , unsafeMakeAggregate
31+ )
32+import Rel8.Expr ( Expr, Col( E ) )
2933import Rel8.Expr.Bool ( boolExpr )
3034import Rel8.Expr.Null ( nullify, unsafeUnnullify )
31−import Rel8.Expr.Opaleye ( fromPrimExpr )
32−import Rel8.Kind.Context ( SContext(..) )
33−import Rel8.Schema.Field ( Field )
35+import Rel8.Expr.Opaleye ( fromPrimExpr, toPrimExpr )
36+import Rel8.Schema.Context ( Interpretation )
3437import qualified Rel8.Schema.Kind as K
35−import Rel8.Schema.Name ( Name( Name ) )
36−import Rel8.Schema.Null ( Nullify, Nullity( Null, NotNull ) )
37−import Rel8.Schema.Result ( Result )
38−import Rel8.Schema.Spec ( Spec(..) )
39−
40−
41−type Nullifiability :: K.Context -> Type
42−data Nullifiability context where
43− NAggregate :: Nullifiability Aggregate
44− NExpr :: Nullifiability Expr
45− NName :: Nullifiability Name
38+import Rel8.Schema.Name ( Name( Name ), Col( N ) )
39+import Rel8.Schema.Null ( Nullify, Nullity( Null, NotNull ), Sql )
40+import Rel8.Schema.Dict ( Dict( Dict ) )
41+import Rel8.Schema.Spec ( Spec( Spec ), SSpec(..) )
42+import Rel8.Schema.Spec.ConstrainDBType ( ConstrainDBType )
43+import qualified Rel8.Schema.Spec.ConstrainDBType as ConstrainDBType
44+import Rel8.Table.Tag ( Tag(..), Taggable, fromAggregate, fromExpr, fromName )
4645
4746
4847type Nullifiable :: K.Context -> Constraint
49−class Nullifiable context where
50− nullifiability :: Nullifiability context
48+class Interpretation context => Nullifiable context where
49+ type ConstrainTag context :: Type -> Constraint
50+ type ConstrainTag _context = DefaultConstrainTag
51+
52+ encodeTag ::
53+ ( Sql (ConstrainTag context) a
54+ , KnownSymbol label
55+ , Taggable a
56+ )
57+ => Tag label a
58+ -> Col context ('Spec labels a)
59+
60+ decodeTag ::
61+ ( Sql (ConstrainTag context) a
62+ , KnownSymbol label
63+ , Taggable a
64+ )
65+ => Col context ('Spec labels a)
66+ -> Tag label a
67+
68+ nullifier :: ()
69+ => Tag label a
70+ -> (Expr a -> Expr Bool)
71+ -> SSpec ('Spec labels x)
72+ -> Col context ('Spec labels x)
73+ -> Col context ('Spec labels (Nullify x))
74+
75+ unnullifier :: ()
76+ => SSpec ('Spec labels x)
77+ -> Col context ('Spec labels (Nullify x))
78+ -> Col context ('Spec labels x)
5179
5280
5381instance Nullifiable Aggregate where
54− nullifiability = NAggregate
82+ encodeTag Tag {aggregator, expr} =
83+ A $ unsafeMakeAggregate toPrimExpr fromPrimExpr aggregator expr
84+
85+ decodeTag (A aggregate) = fromAggregate aggregate
86+
87+ nullifier Tag {expr} test SSpec {nullity} (A (Aggregate a)) =
88+ A $
89+ mapInputs (toPrimExpr . runTag nullity condition . fromPrimExpr) $
90+ Aggregate $
91+ runTag nullity condition <$> a
92+ where
93+ condition = test expr
94+
95+ unnullifier SSpec {nullity} (A (Aggregate a)) =
96+ A (Aggregate (unnull nullity <$> a))
97+
98+ {-# INLINABLE encodeTag #-}
99+ {-# INLINABLE decodeTag #-}
100+ {-# INLINABLE nullifier #-}
101+ {-# INLINABLE unnullifier #-}
55102
56103
57104instance Nullifiable Expr where
58− nullifiability = NExpr
105+ encodeTag Tag {expr} = E expr
106+ decodeTag (E a) = fromExpr a
107+ nullifier Tag {expr} test SSpec {nullity} (E a) =
108+ E $ runTag nullity (test expr) a
109+ unnullifier SSpec {nullity} (E a) = E $ unnull nullity a
110+
111+ {-# INLINABLE encodeTag #-}
112+ {-# INLINABLE decodeTag #-}
113+ {-# INLINABLE nullifier #-}
114+ {-# INLINABLE unnullifier #-}
59115
60116
61117instance Nullifiable Name where
62− nullifiability = NName
63−
64−
65−type NonNullifiability :: K.Context -> Type
66−data NonNullifiability context where
67− NField :: NonNullifiability (Field table)
68− NResult :: NonNullifiability Result
69−
70−
71−nullifiableOrNot :: ()
72− => SContext context
73− -> Either (NonNullifiability context) (Nullifiability context)
74−nullifiableOrNot = \case
75− SAggregate -> Right NAggregate
76− SExpr -> Right NExpr
77− SField -> Left NField
78− SName -> Right NName
79− SResult -> Left NResult
80−
81−
82−absurd :: Nullifiability context -> NonNullifiability context -> a
83−absurd = \case
84− NAggregate -> \case
85− NExpr -> \case
86− NName -> \case
87−
88−
89−guarder :: ()
90− => SContext context
91− -> context tag
92− -> (tag -> Bool)
93− -> (Expr tag -> Expr Bool)
94− -> context (Maybe a)
95− -> context (Maybe a)
96−guarder = \case
97− SAggregate -> \tag _ isNonNull -> zipOutputs (sguard . isNonNull) tag
98− SExpr -> \tag _ isNonNull -> sguard (isNonNull tag)
99− SField -> \_ _ _ -> id
100− SName -> \_ _ _ -> id
101− SResult -> \(Identity tag) isNonNull _ (Identity a) ->
102− Identity (bool Nothing a (isNonNull tag))
103−
104−
105−nullifier :: ()
106− => Nullifiability context
107− -> Spec a
108− -> context a
109− -> context (Nullify a)
110−nullifier = \case
111− NAggregate -> \Spec {nullity} (Aggregate a) ->
112− Aggregate $ snullify nullity <$> a
113− NExpr -> \Spec {nullity} a -> snullify nullity a
114− NName -> \_ (Name a) -> Name a
115−
116−
117−unnullifier :: ()
118− => Nullifiability context
119− -> Spec a
120− -> context (Nullify a)
121− -> context a
122−unnullifier = \case
123− NAggregate -> \Spec {nullity} (Aggregate a) ->
124− Aggregate $ sunnullify nullity <$> a
125− NExpr -> \Spec {nullity} a -> sunnullify nullity a
126− NName -> \_ (Name a) -> Name a
127−
128−
129−sguard :: Expr Bool -> Expr (Maybe a) -> Expr (Maybe a)
130−sguard condition a = boolExpr null a condition
118+ encodeTag Tag {name} = N name
119+ decodeTag (N name) = fromName name
120+ nullifier _ _ _ (N (Name name)) = N (Name name)
121+ unnullifier _ (N (Name name)) = N (Name name)
122+
123+ {-# INLINABLE encodeTag #-}
124+ {-# INLINABLE decodeTag #-}
125+ {-# INLINABLE nullifier #-}
126+ {-# INLINABLE unnullifier #-}
127+
128+
129+runTag :: Nullity a -> Expr Bool -> Expr a -> Expr (Nullify a)
130+runTag nullity tag a = case nullity of
131+ Null -> boolExpr null a tag
132+ NotNull -> boolExpr null (nullify a) tag
131133 where
132134 null = fromPrimExpr $ Opaleye.ConstExpr Opaleye.NullLit
133135
134136
135−snullify :: Nullity a -> Expr a -> Expr (Nullify a)
136−snullify nullity a = case nullity of
137+unnull :: Nullity a -> Expr (Nullify a) -> Expr a
138+unnull nullity a = case nullity of
137139 Null -> a
138− NotNull -> nullify a
140+ NotNull -> unsafeUnnullify a
139141
140142
141−sunnullify :: Nullity a -> Expr (Nullify a) -> Expr a
142−sunnullify nullity a = case nullity of
143− Null -> a
144− NotNull -> unsafeUnnullify a
143+type HNullifiable :: K.HContext -> Constraint
144+class HNullifiable context where
145+ type HConstrainTag context :: Type -> Constraint
146+ type HConstrainTag _context = DefaultConstrainTag
147+
148+ hencodeTag :: (Sql (HConstrainTag context) a, KnownSymbol label, Taggable a)
149+ => Tag label a
150+ -> context ('Spec labels a)
151+
152+ hdecodeTag :: (Sql (HConstrainTag context) a, KnownSymbol label, Taggable a)
153+ => context ('Spec labels a)
154+ -> Tag label a
155+
156+ hnullifier :: ()
157+ => Tag label a
158+ -> (Expr a -> Expr Bool)
159+ -> SSpec ('Spec labels x)
160+ -> context ('Spec labels x)
161+ -> context ('Spec labels (Nullify x))
162+
163+ hunnullifier :: ()
164+ => SSpec ('Spec labels x)
165+ -> context ('Spec labels (Nullify x))
166+ -> context ('Spec labels x)
167+
168+
169+instance Nullifiable context => HNullifiable (Col context) where
170+ type HConstrainTag (Col context) = ConstrainTag context
171+ hencodeTag = encodeTag
172+ hdecodeTag = decodeTag
173+ hnullifier = nullifier
174+ hunnullifier = unnullifier
175+
176+
177+instance HNullifiable (Dict (ConstrainDBType constraint)) where
178+ type HConstrainTag (Dict (ConstrainDBType constraint)) = constraint
179+
180+ hencodeTag _ = Dict
181+ hdecodeTag = mempty
182+ hnullifier _ _ = ConstrainDBType.nullifier
183+ hunnullifier = ConstrainDBType.unnullifier
184+
185+
186+type DefaultConstrainTag :: Type -> Constraint
187+class DefaultConstrainTag a
188+instance DefaultConstrainTag a
src/Rel8/Schema/Field.hsdeleted−50
@@ -1,50 +0,0 @@
1−{-# language DataKinds #-}
2−{-# language FlexibleContexts #-}
3−{-# language MultiParamTypeClasses #-}
4−{-# language StandaloneKindSignatures #-}
5−{-# language TypeFamilies #-}
6−
7−module Rel8.Schema.Field
8− ( Field(..)
9− , fields
10− )
11−where
12−
13−-- base
14−import Data.Functor.Identity ( Identity( Identity ) )
15−import Data.Kind ( Type )
16−import Prelude
17−
18−-- rel8
19−import Rel8.Schema.HTable ( HField, htabulate )
20−import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) )
21−import Rel8.Schema.Kind as K
22−import Rel8.Schema.Null ( Sql )
23−import Rel8.Table
24− ( Table, Columns, Context, fromColumns, toColumns
25− , FromExprs, fromResult, toResult
26− , Transpose
27− )
28−import Rel8.Table.Transpose ( Transposes )
29−import Rel8.Type ( DBType )
30−
31−
32−-- | A special context used in the construction of 'Rel8.Projection's.
33−type Field :: Type -> K.Context
34−newtype Field table a = Field (HField (Columns table) a)
35−
36−
37−instance Sql DBType a => Table (Field table) (Field table a) where
38− type Columns (Field table a) = HIdentity a
39− type Context (Field table a) = Field table
40− type FromExprs (Field table a) = a
41− type Transpose to (Field table a) = to a
42−
43− toColumns = HIdentity
44− fromColumns (HIdentity a) = a
45− toResult a = HIdentity (Identity a)
46− fromResult (HIdentity (Identity a)) = a
47−
48−
49−fields :: Transposes context (Field table) table fields => fields
50−fields = fromColumns $ htabulate Field
src/Rel8/Schema/HTable.hs+24−51
@@ -16,12 +16,11 @@
1616module Rel8.Schema.HTable
1717 ( HTable (HField, HConstrainTable)
1818 , hfield, htabulate, htraverse, hdicts, hspecs
19− , hfoldMap, hmap, htabulateA, htraverseP, htraversePWithField
19+ , hmap, htabulateA
2020 )
2121where
2222
2323-- base
24−import Data.Functor.Const ( Const( Const ), getConst )
2524import Data.Kind ( Constraint, Type )
2625import Data.Functor.Compose ( Compose( Compose ), getCompose )
2726import Data.Proxy ( Proxy )
@@ -33,21 +32,16 @@ import GHC.Generics
3332 )
3433import Prelude
3534
36−-- profunctors
37−import Data.Profunctor ( rmap, Profunctor (lmap) )
38−
39−-- product-profunctors
40−import Data.Profunctor.Product ( ProductProfunctor ((****)) )
41−
4235-- rel8
4336import Rel8.Schema.Dict ( Dict )
44−import Rel8.Schema.Spec ( Spec )
37+import Rel8.Schema.Spec ( Spec, SSpec )
4538import Rel8.Schema.HTable.Product ( HProduct( HProduct ) )
4639import qualified Rel8.Schema.Kind as K
4740
4841-- semigroupoids
4942import Data.Functor.Apply ( Apply, (<.>) )
5043
44+
5145-- | A @HTable@ is a functor-indexed/higher-kinded data type that is
5246-- representable ('htabulate'/'hfield'), constrainable ('hdicts'), and
5347-- specified ('hspecs').
@@ -56,14 +50,14 @@ import Data.Functor.Apply ( Apply, (<.>) )
5650-- instances yourself or specify this constraint.
5751type HTable :: K.HTable -> Constraint
5852class HTable t where
59− type HField t = (field :: Type -> Type) | field -> t
60− type HConstrainTable t (c :: Type -> Constraint) :: Constraint
53+ type HField t = (field :: Spec -> Type) | field -> t
54+ type HConstrainTable t (c :: Spec -> Constraint) :: Constraint
6155
62− hfield :: t context -> HField t a -> context a
63− htabulate :: (forall a. HField t a -> context a) -> t context
64− htraverse :: Apply m => (forall a. f a -> m (g a)) -> t f -> m (t g)
56+ hfield :: t context -> HField t spec -> context spec
57+ htabulate :: (forall spec. HField t spec -> context spec) -> t context
58+ htraverse :: Apply m => (forall spec. f spec -> m (g spec)) -> t f -> m (t g)
6559 hdicts :: HConstrainTable t c => t (Dict c)
66− hspecs :: t Spec
60+ hspecs :: t SSpec
6761
6862 type HField t = GHField t
6963 type HConstrainTable t c = HConstrainTable (GHColumns (Rep (t Proxy))) c
@@ -74,7 +68,7 @@ class HTable t where
7468 , HField (GHColumns (Rep (t Proxy))) ~ HField (GHColumns (Rep (t context)))
7569 , GHTable context (Rep (t context))
7670 )
77− => t context -> HField t a -> context a
71+ => t context -> HField t spec -> context spec
7872 hfield table (GHField field) = hfield (toGHColumns (from table)) field
7973
8074 default htabulate ::
@@ -83,7 +77,7 @@ class HTable t where
8377 , HField (GHColumns (Rep (t Proxy))) ~ HField (GHColumns (Rep (t context)))
8478 , GHTable context (Rep (t context))
8579 )
86− => (forall a. HField t a -> context a) -> t context
80+ => (forall spec. HField t spec -> context spec) -> t context
8781 htabulate f = to $ fromGHColumns $ htabulate (f . GHField)
8882
8983 default htraverse
@@ -93,7 +87,7 @@ class HTable t where
9387 , Generic (t g), GHTable g (Rep (t g))
9488 , GHColumns (Rep (t f)) ~ GHColumns (Rep (t g))
9589 )
96− => (forall a. f a -> m (g a)) -> t f -> m (t g)
90+ => (forall spec. f spec -> m (g spec)) -> t f -> m (t g)
9791 htraverse f = fmap (to . fromGHColumns) . htraverse f . toGHColumns . from
9892
9993 default hdicts
@@ -107,10 +101,10 @@ class HTable t where
107101 hdicts = to $ fromGHColumns (hdicts @(GHColumns (Rep (t Proxy))) @c)
108102
109103 default hspecs ::
110− ( Generic (t Spec)
111− , GHTable Spec (Rep (t Spec))
104+ ( Generic (t SSpec)
105+ , GHTable SSpec (Rep (t SSpec))
112106 )
113− => t Spec
107+ => t SSpec
114108 hspecs = to $ fromGHColumns hspecs
115109
116110 {-# INLINABLE hfield #-}
@@ -120,43 +114,22 @@ class HTable t where
120114 {-# INLINABLE hspecs #-}
121115
122116
123−hfoldMap :: (HTable t, Semigroup s)
124− => (forall a. context a -> s) -> t context -> s
125−hfoldMap f a = getConst $ htraverse (Const . f) a
126−
127−
128117hmap :: HTable t
129− => (forall a. context a -> context' a) -> t context -> t context'
118+ => (forall spec. context spec -> context' spec) -> t context -> t context'
130119hmap f a = htabulate $ \field -> f (hfield a field)
131120
132121
133122htabulateA :: (HTable t, Apply m)
134− => (forall a. HField t a -> m (context a)) -> m (t context)
123+ => (forall spec. HField t spec -> m (context spec)) -> m (t context)
135124htabulateA f = htraverse getCompose $ htabulate $ Compose . f
136125{-# INLINABLE htabulateA #-}
137126
138−newtype ApplyP p a b = ApplyP { unApplyP :: p a b }
139−
140−instance Profunctor p => Functor (ApplyP p a) where
141− fmap f = ApplyP . rmap f . unApplyP
142−
143−instance ProductProfunctor p => Apply (ApplyP p a) where
144− ApplyP f <.> ApplyP x = ApplyP (rmap id f **** x)
145−
146−htraverseP :: (HTable t, ProductProfunctor p)
147− => (forall a. p (f a) (g a)) -> p (t f) (t g)
148−htraverseP f = htraversePWithField (const f)
149−
150−htraversePWithField :: (HTable t, ProductProfunctor p)
151− => (forall a. HField t a -> p (f a) (g a)) -> p (t f) (t g)
152−htraversePWithField f = unApplyP $ htabulateA $ \field -> ApplyP $
153− lmap (flip hfield field) (f field)
154127
155−type GHField :: K.HTable -> Type -> Type
156−newtype GHField t a = GHField (HField (GHColumns (Rep (t Proxy))) a)
128+type GHField :: K.HTable -> Spec -> Type
129+newtype GHField t spec = GHField (HField (GHColumns (Rep (t Proxy))) spec)
157130
158131
159−type GHTable :: K.Context -> (Type -> Type) -> Constraint
132+type GHTable :: K.HContext -> (Type -> Type) -> Constraint
160133class HTable (GHColumns rep) => GHTable context rep | rep -> context where
161134 type GHColumns rep :: K.HTable
162135 toGHColumns :: rep x -> GHColumns rep context
@@ -182,10 +155,10 @@ instance (GHTable context a, GHTable context b) => GHTable context (a :*: b) whe
182155
183156
184157-- | A HField type for indexing into HProduct.
185−type HProductField :: K.HTable -> K.HTable -> Type -> Type
186−data HProductField x y a
187− = HFst (HField x a)
188− | HSnd (HField y a)
158+type HProductField :: K.HTable -> K.HTable -> Spec -> Type
159+data HProductField x y spec
160+ = HFst (HField x spec)
161+ | HSnd (HField y spec)
189162
190163
191164instance (HTable x, HTable y) => HTable (HProduct x y) where
src/Rel8/Schema/HTable/Either.hs+3−2
@@ -15,16 +15,17 @@ import Prelude ()
1515
1616-- rel8
1717import Rel8.Schema.HTable ( HTable )
18−import Rel8.Schema.HTable.Identity ( HIdentity )
18+import Rel8.Schema.HTable.Identity ( HIdentity(..) )
1919import Rel8.Schema.HTable.Label ( HLabel )
2020import Rel8.Schema.HTable.Nullify ( HNullify )
2121import qualified Rel8.Schema.Kind as K
22+import Rel8.Schema.Spec ( Spec( Spec ) )
2223import Rel8.Type.Tag ( EitherTag )
2324
2425
2526type HEitherTable :: K.HTable -> K.HTable -> K.HTable
2627data HEitherTable left right context = HEitherTable
27− { htag :: HLabel "isRight" (HIdentity EitherTag) context
28+ { htag :: HIdentity ('Spec '["isRight"] EitherTag) context
2829 , hleft :: HLabel "Left" (HNullify left) context
2930 , hright :: HLabel "Right" (HNullify right) context
3031 }
src/Rel8/Schema/HTable/Identity.hs+28−16
@@ -1,17 +1,17 @@
11{-# language DataKinds #-}
2−{-# language FlexibleContexts #-}
2+{-# language GADTs #-}
3+{-# language PatternSynonyms #-}
34{-# language StandaloneKindSignatures #-}
45{-# language TypeFamilies #-}
5−{-# language UndecidableInstances #-}
66
77module Rel8.Schema.HTable.Identity
8− ( HIdentity( HIdentity, unHIdentity )
8+ ( HIdentity( HIdentity, HType, unHIdentity )
9+ , HType
910 )
1011where
1112
1213-- base
1314import Data.Kind ( Type )
14−import Data.Type.Equality ( (:~:)( Refl ) )
1515import Prelude
1616
1717-- rel8
@@ -21,23 +21,35 @@ import Rel8.Schema.HTable
2121 , hfield, htabulate, htraverse, hdicts, hspecs
2222 )
2323import qualified Rel8.Schema.Kind as K
24−import Rel8.Schema.Null ( Sql )
25−import Rel8.Schema.Spec ( specification )
26−import Rel8.Type ( DBType )
24+import Rel8.Schema.Spec ( Spec( Spec ), KnownSpec, specSing )
2725
2826
29−type HIdentity :: Type -> K.HTable
30−newtype HIdentity a context = HIdentity
31− { unHIdentity :: context a
27+type HType :: Type -> K.HTable
28+type HType a = HIdentity ('Spec '[] a)
29+
30+
31+pattern HType :: context ('Spec '[] a) -> HType a context
32+pattern HType a = HIdentity a
33+{-# COMPLETE HType #-}
34+
35+
36+type HIdentity :: Spec -> K.HTable
37+newtype HIdentity spec context = HIdentity
38+ { unHIdentity :: context spec
3239 }
3340
3441
35−instance Sql DBType a => HTable (HIdentity a) where
36− type HConstrainTable (HIdentity a) constraint = constraint a
37− type HField (HIdentity a) = (:~:) a
42+type HIdentityField :: Spec -> Spec -> Type
43+data HIdentityField _spec spec where
44+ HIdentityField :: HIdentityField spec spec
45+
46+
47+instance KnownSpec spec => HTable (HIdentity spec) where
48+ type HConstrainTable (HIdentity spec) c = c spec
49+ type HField (HIdentity spec) = HIdentityField spec
3850
39− hfield (HIdentity a) Refl = a
40− htabulate f = HIdentity $ f Refl
51+ hfield (HIdentity a) HIdentityField = a
52+ htabulate f = HIdentity $ f HIdentityField
4153 htraverse f (HIdentity a) = HIdentity <$> f a
4254 hdicts = HIdentity Dict
43− hspecs = HIdentity specification
55+ hspecs = HIdentity specSing
src/Rel8/Schema/HTable/Label.hs+50−37
@@ -1,70 +1,83 @@
1+{-# LANGUAGE DeriveAnyClass #-}
2+{-# LANGUAGE DeriveGeneric #-}
3+{-# LANGUAGE DerivingStrategies #-}
4+{-# LANGUAGE LambdaCase #-}
5+{-# language ConstraintKinds #-}
16{-# language DataKinds #-}
7+{-# language FlexibleInstances #-}
8+{-# language GADTs #-}
9+{-# language MultiParamTypeClasses #-}
10+{-# language QuantifiedConstraints #-}
211{-# language RankNTypes #-}
312{-# language RecordWildCards #-}
413{-# language ScopedTypeVariables #-}
514{-# language StandaloneKindSignatures #-}
6−{-# language TypeApplications #-}
715{-# language TypeFamilies #-}
16+{-# language TypeOperators #-}
17+{-# language UndecidableInstances #-}
818
919module Rel8.Schema.HTable.Label
10− ( HLabel, hlabel, hrelabel, hunlabel
11− , hproject
20+ ( HLabel, Label
21+ , hlabel, hunlabel
1222 )
1323where
1424
1525-- base
16−import Data.Kind ( Type )
1726import Data.Proxy ( Proxy( Proxy ) )
18−import GHC.TypeLits ( KnownSymbol, Symbol, symbolVal )
27+import GHC.TypeLits ( KnownSymbol, Symbol )
1928import Prelude
2029
2130-- rel8
31+import Rel8.Kind.Labels ( SLabels( SCons ) )
2232import Rel8.Schema.HTable
23− ( HTable, HConstrainTable, HField
24− , htabulate, hfield, htraverse, hdicts, hspecs
33+ ( HTable
34+ , hfield, htabulate, hspecs
2535 )
2636import qualified Rel8.Schema.Kind as K
27−import Rel8.Schema.Spec ( Spec(..) )
37+import Rel8.Schema.Spec ( Spec( Spec ), SSpec(..) )
38+import Rel8.FCF
39+import Rel8.Schema.HTable.MapTable
40+import GHC.Generics (Generic)
2841
2942
3043type HLabel :: Symbol -> K.HTable -> K.HTable
31−newtype HLabel label table context = HLabel (table context)
44+newtype HLabel label table context = HLabel (HMapTable (Label label) table context)
45+ deriving stock Generic
46+ deriving anyclass HTable
3247
3348
34−type HLabelField :: Symbol -> K.HTable -> Type -> Type
35−newtype HLabelField label table a = HLabelField (HField table a)
49+data Label :: Symbol -> Spec -> Exp Spec
3650
3751
38−instance (HTable table, KnownSymbol label) => HTable (HLabel label table) where
39− type HField (HLabel label table) = HLabelField label table
40− type HConstrainTable (HLabel label table) constraint =
41− HConstrainTable table constraint
52+type instance Eval (Label label ('Spec labels a)) = 'Spec (label : labels) a
4253
43− hfield (HLabel a) (HLabelField field) = hfield a field
44− htabulate f = HLabel (htabulate (f . HLabelField))
45− htraverse f (HLabel a) = HLabel <$> htraverse f a
46− hdicts = HLabel (hdicts @table)
47− hspecs = HLabel $ htabulate $ \field -> case hfield (hspecs @table) field of
48− Spec {..} -> Spec {labels = symbolVal (Proxy @label) : labels, ..}
49− {-# INLINABLE hspecs #-}
5054
51−
52−hlabel :: forall label t context. t context -> HLabel label t context
53−hlabel = HLabel
54−{-# INLINABLE hlabel #-}
55+instance KnownSymbol l => MapSpec (Label l) where
56+ mapInfo = \case
57+ SSpec {..} -> SSpec {labels = SCons Proxy labels, ..}
5558
5659
57−hrelabel :: forall label' label t context. HLabel label t context -> HLabel label' t context
58−hrelabel = hlabel . hunlabel
59−{-# INLINABLE hrelabel #-}
60+hlabel :: (HTable t, KnownSymbol label)
61+ => (forall labels a. ()
62+ => context ('Spec labels a)
63+ -> context ('Spec (label ': labels) a))
64+ -> t context
65+ -> HLabel label t context
66+hlabel labeler a = HLabel $ htabulate $ \(HMapTableField field) ->
67+ case hfield hspecs field of
68+ SSpec {} -> labeler (hfield a field)
69+{-# INLINABLE hlabel #-}
6070
6171
62−hunlabel :: forall label t context. HLabel label t context -> t context
63−hunlabel (HLabel a) = a
72+hunlabel :: (HTable t, KnownSymbol label)
73+ => (forall labels a. ()
74+ => context ('Spec (label ': labels) a)
75+ -> context ('Spec labels a))
76+ -> HLabel label t context
77+ -> t context
78+hunlabel unlabler (HLabel as) =
79+ htabulate $ \field ->
80+ case hfield hspecs field of
81+ SSpec {} -> case hfield as (HMapTableField field) of
82+ a -> unlabler a
6483{-# INLINABLE hunlabel #-}
65−
66−
67−hproject :: ()
68− => (forall ctx. t ctx -> t' ctx)
69− -> HLabel label t context -> HLabel label t' context
70−hproject f (HLabel a) = HLabel (f a)
src/Rel8/Schema/HTable/MapTable.hs+12−22
@@ -6,7 +6,7 @@
66{-# language GADTs #-}
77{-# language InstanceSigs #-}
88{-# language MultiParamTypeClasses #-}
9−{-# language RankNTypes #-}
9+{-# language PolyKinds #-}
1010{-# language ScopedTypeVariables #-}
1111{-# language StandaloneKindSignatures #-}
1212{-# language TypeApplications #-}
@@ -19,38 +19,34 @@ module Rel8.Schema.HTable.MapTable
1919 , MapSpec(..)
2020 , Precompose(..)
2121 , HMapTableField(..)
22− , hproject
2322 )
2423where
2524
2625-- base
2726import Data.Kind ( Constraint, Type )
28−import Prelude
27+import Prelude ( ($), (.), (<$>), fmap )
2928
3029-- rel8
31−import Rel8.FCF ( Exp, Eval )
30+import Rel8.FCF
3231import Rel8.Schema.HTable
33− ( HTable, HConstrainTable, HField
34− , hfield, htabulate, htraverse, hdicts, hspecs
35− )
36−import Rel8.Schema.Spec ( Spec )
32+import Rel8.Schema.Spec ( Spec, SSpec )
3733import qualified Rel8.Schema.Kind as K
3834import Rel8.Schema.Dict ( Dict( Dict ) )
3935
4036
41−type HMapTable :: (Type -> Exp Type) -> K.HTable -> K.HTable
42−newtype HMapTable f t context = HMapTable
43− { unHMapTable :: t (Precompose f context)
37+type HMapTable :: (a -> Exp b) -> ((a -> Type) -> Type) -> (b -> Type) -> Type
38+newtype HMapTable f t g = HMapTable
39+ { unHMapTable :: t (Precompose f g)
4440 }
4541
4642
47−type Precompose :: (Type -> Exp Type) -> K.Context -> K.Context
43+type Precompose :: (a -> Exp b) -> (b -> Type) -> a -> Type
4844newtype Precompose f g x = Precompose
4945 { precomposed :: g (Eval (f x))
5046 }
5147
5248
53−type HMapTableField :: (Type -> Exp Type) -> K.HTable -> K.Context
49+type HMapTableField :: (Spec -> Exp a) -> K.HTable -> a -> Type
5450data HMapTableField f t x where
5551 HMapTableField :: HField t a -> HMapTableField f t (Eval (f a))
5652
@@ -83,17 +79,11 @@ instance (HTable t, MapSpec f) => HTable (HMapTable f t) where
8379 {-# INLINABLE hspecs #-}
8480
8581
86−type MapSpec :: (Type -> Exp Type) -> Constraint
82+type MapSpec :: (Spec -> Exp Spec) -> Constraint
8783class MapSpec f where
88− mapInfo :: Spec x -> Spec (Eval (f x))
84+ mapInfo :: SSpec x -> SSpec (Eval (f x))
8985
9086
91−type ComposeConstraint :: (Type -> Exp Type) -> (Type -> Constraint) -> Type -> Constraint
87+type ComposeConstraint :: (a -> Exp b) -> (b -> Constraint) -> a -> Constraint
9288class c (Eval (f a)) => ComposeConstraint f c a
9389instance c (Eval (f a)) => ComposeConstraint f c a
94−
95−
96−hproject :: ()
97− => (forall ctx. t ctx -> t' ctx)
98− -> HMapTable f t context -> HMapTable f t' context
99−hproject f (HMapTable a) = HMapTable (f a)
src/Rel8/Schema/HTable/Maybe.hs+3−2
@@ -15,16 +15,17 @@ import Prelude
1515
1616-- rel8
1717import Rel8.Schema.HTable ( HTable )
18−import Rel8.Schema.HTable.Identity ( HIdentity )
18+import Rel8.Schema.HTable.Identity ( HIdentity(..) )
1919import Rel8.Schema.HTable.Label ( HLabel )
2020import Rel8.Schema.HTable.Nullify ( HNullify )
2121import qualified Rel8.Schema.Kind as K
22+import Rel8.Schema.Spec ( Spec( Spec ) )
2223import Rel8.Type.Tag ( MaybeTag )
2324
2425
2526type HMaybeTable :: K.HTable -> K.HTable
2627data HMaybeTable table context = HMaybeTable
27− { htag :: HLabel "isJust" (HIdentity (Maybe MaybeTag)) context
28+ { htag :: HIdentity ('Spec '["isJust"] (Maybe MaybeTag)) context
2829 , hjust :: HLabel "Just" (HNullify table) context
2930 }
3031 deriving stock Generic
src/Rel8/Schema/HTable/Nullify.hs+32−47
@@ -10,7 +10,6 @@
1010{-# language NamedFieldPuns #-}
1111{-# language QuantifiedConstraints #-}
1212{-# language RankNTypes #-}
13−{-# language RecordWildCards #-}
1413{-# language ScopedTypeVariables #-}
1514{-# language StandaloneKindSignatures #-}
1615{-# language TypeFamilies #-}
@@ -19,34 +18,25 @@
1918module Rel8.Schema.HTable.Nullify
2019 ( HNullify( HNullify )
2120 , Nullify
22− , hguard
23− , hnulls
24− , hnullify
25− , hunnullify
26− , hproject
21+ , hnulls, hnullify, hunnullify
2722 )
2823where
2924
3025-- base
31−import Data.Kind ( Type )
32−import GHC.Generics ( Generic )
3326import Prelude hiding ( null )
3427
3528-- rel8
36−import Rel8.FCF ( Eval, Exp )
3729import Rel8.Schema.HTable ( HTable, hfield, htabulate, htabulateA, hspecs )
38−import Rel8.Schema.HTable.MapTable
39− ( HMapTable, HMapTableField( HMapTableField )
40− , MapSpec, mapInfo
41− )
42−import qualified Rel8.Schema.HTable.MapTable as HMapTable ( hproject )
4330import qualified Rel8.Schema.Kind as K
4431import Rel8.Schema.Null ( Nullity( Null, NotNull ) )
4532import qualified Rel8.Schema.Null as Type ( Nullify )
46−import Rel8.Schema.Spec ( Spec(..) )
33+import Rel8.Schema.Spec ( Spec( Spec ), SSpec(..) )
4734
4835-- semigroupoids
4936import Data.Functor.Apply ( Apply )
37+import Rel8.Schema.HTable.MapTable
38+import Rel8.FCF
39+import GHC.Generics (Generic)
5040
5141
5242type HNullify :: K.HTable -> K.HTable
@@ -55,63 +45,58 @@ newtype HNullify table context = HNullify (HMapTable Nullify table context)
5545 deriving anyclass HTable
5646
5747
58−-- | Transform a 'Type' by allowing it to be @null@.
59−data Nullify :: Type -> Exp Type
60−type instance Eval (Nullify a) = Type.Nullify a
48+
49+-- | Transform a 'Spec' by allowing it to be @null@.
50+data Nullify :: Spec -> Exp Spec
51+
52+
53+type instance Eval (Nullify ('Spec labels a)) =
54+ 'Spec labels (Type.Nullify a)
6155
6256
6357instance MapSpec Nullify where
6458 mapInfo = \case
65− Spec {nullity, ..} -> Spec
66− { nullity = case nullity of
59+ SSpec{labels, info, nullity} -> SSpec
60+ { labels
61+ , info
62+ , nullity = case nullity of
6763 Null -> Null
6864 NotNull -> Null
69− , ..
70− }
71−
72−
73−hguard :: HTable t
74− => (forall a. context (Maybe a) -> context (Maybe a))
75− -> HNullify t context -> HNullify t context
76−hguard guarder (HNullify as) = HNullify $ htabulate $ \(HMapTableField field) ->
77− case hfield hspecs field of
78− Spec {nullity} -> case hfield as (HMapTableField field) of
79− a -> case nullity of
80− Null -> guarder a
81− NotNull -> guarder a
65+ }
8266
8367
8468hnulls :: HTable t
85− => (forall a. Spec a -> context (Type.Nullify a))
69+ => (forall labels a. ()
70+ => SSpec ('Spec labels a)
71+ -> context ('Spec labels (Type.Nullify a)))
8672 -> HNullify t context
87−hnulls null = HNullify $ htabulate $ \(HMapTableField field) ->
88− case hfield hspecs field of
89− spec@Spec {} -> null spec
73+hnulls null = HNullify $ htabulate $ \(HMapTableField field) -> case hfield hspecs field of
74+ spec@SSpec {} -> null spec
9075{-# INLINABLE hnulls #-}
9176
9277
9378hnullify :: HTable t
94− => (forall a. Spec a -> context a -> context (Type.Nullify a))
79+ => (forall labels a. ()
80+ => SSpec ('Spec labels a)
81+ -> context ('Spec labels a)
82+ -> context ('Spec labels (Type.Nullify a)))
9583 -> t context
9684 -> HNullify t context
9785hnullify nullifier a = HNullify $ htabulate $ \(HMapTableField field) ->
9886 case hfield hspecs field of
99− spec@Spec {} -> nullifier spec (hfield a field)
87+ spec@SSpec {} -> nullifier spec (hfield a field)
10088{-# INLINABLE hnullify #-}
10189
10290
10391hunnullify :: (HTable t, Apply m)
104− => (forall a. Spec a -> context (Type.Nullify a) -> m (context a))
92+ => (forall labels a. ()
93+ => SSpec ('Spec labels a)
94+ -> context ('Spec labels (Type.Nullify a))
95+ -> m (context ('Spec labels a)))
10596 -> HNullify t context
10697 -> m (t context)
10798hunnullify unnullifier (HNullify as) =
10899 htabulateA $ \field -> case hfield hspecs field of
109− spec@Spec {} -> case hfield as (HMapTableField field) of
100+ spec@SSpec {} -> case hfield as (HMapTableField field) of
110101 a -> unnullifier spec a
111102{-# INLINABLE hunnullify #-}
112−
113−
114−hproject :: ()
115− => (forall ctx. t ctx -> t' ctx)
116− -> HNullify t context -> HNullify t' context
117−hproject f (HNullify a) = HNullify (HMapTable.hproject f a)
src/Rel8/Schema/HTable/These.hs+3−2
@@ -19,14 +19,15 @@ import Rel8.Schema.HTable.Identity ( HIdentity )
1919import Rel8.Schema.HTable.Label ( HLabel )
2020import Rel8.Schema.HTable.Nullify ( HNullify )
2121import qualified Rel8.Schema.Kind as K
22+import Rel8.Schema.Spec ( Spec( Spec ) )
2223import Rel8.Type.Tag ( MaybeTag )
2324
2425
2526type HTheseTable :: K.HTable -> K.HTable -> K.HTable
2627data HTheseTable here there context = HTheseTable
27− { hhereTag :: HLabel "hereTag" (HIdentity (Maybe MaybeTag)) context
28+ { hhereTag :: HIdentity ('Spec '["hasHere"] (Maybe MaybeTag)) context
2829 , hhere :: HLabel "Here" (HNullify here) context
29− , hthereTag :: HLabel "thereTag" (HIdentity (Maybe MaybeTag)) context
30+ , hthereTag :: HIdentity ('Spec '["hasThere"] (Maybe MaybeTag)) context
3031 , hthere :: HLabel "There" (HNullify there) context
3132 }
3233 deriving stock Generic
src/Rel8/Schema/HTable/Vectorize.hs+45−37
@@ -9,6 +9,8 @@
99{-# language GADTs #-}
1010{-# language LambdaCase #-}
1111{-# language MultiParamTypeClasses #-}
12+{-# language NamedFieldPuns #-}
13+{-# language QuantifiedConstraints #-}
1214{-# language RankNTypes #-}
1315{-# language RecordWildCards #-}
1416{-# language ScopedTypeVariables #-}
@@ -21,39 +23,34 @@ module Rel8.Schema.HTable.Vectorize
2123 ( HVectorize
2224 , hvectorize, hunvectorize
2325 , happend, hempty
24− , hproject
25− , hcolumn
2626 )
2727where
2828
2929-- base
30−import Data.Kind ( Constraint, Type )
30+import Data.Kind ( Type )
3131import Data.List.NonEmpty ( NonEmpty )
32−import GHC.Generics (Generic)
3332import Prelude
3433
3534-- rel8
36−import Rel8.FCF ( Eval, Exp )
35+import Rel8.Schema.Context.Label ( HLabelable, hlabeler, hunlabeler )
3736import Rel8.Schema.Dict ( Dict( Dict ) )
3837import qualified Rel8.Schema.Kind as K
39−import Rel8.Schema.HTable ( HTable, hfield, htabulate, htabulateA, hspecs )
40−import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) )
41−import Rel8.Schema.HTable.MapTable
42− ( HMapTable( HMapTable ), HMapTableField( HMapTableField )
43− , MapSpec, mapInfo
44− , Precompose( Precompose )
38+import Rel8.Schema.HTable
39+ ( HTable
40+ , hfield, htabulate, htabulateA, hspecs
4541 )
46−import qualified Rel8.Schema.HTable.MapTable as HMapTable ( hproject )
4742import Rel8.Schema.Null ( Unnullify, NotNull, Nullity( NotNull ) )
48−import Rel8.Schema.Spec ( Spec(..) )
43+import Rel8.Schema.Spec ( Spec( Spec ), SSpec(..) )
4944import Rel8.Type.Array ( listTypeInformation, nonEmptyTypeInformation )
5045import Rel8.Type.Information ( TypeInformation )
5146
5247-- semialign
5348import Data.Zip ( Unzip, Zip, Zippy(..) )
49+import Rel8.FCF
50+import Rel8.Schema.HTable.MapTable
51+import GHC.Generics (Generic)
5452
5553
56−type Vector :: (Type -> Type) -> Constraint
5754class Vector list where
5855 listNotNull :: proxy a -> Dict NotNull (list a)
5956 vectorTypeInformation :: ()
@@ -78,16 +75,16 @@ newtype HVectorize list table context = HVectorize (HMapTable (Vectorize list) t
7875 deriving anyclass HTable
7976
8077
81−data Vectorize :: (Type -> Type) -> Type -> Exp Type
78+data Vectorize :: (Type -> Type) -> Spec -> Exp Spec
8279
8380
84−type instance Eval (Vectorize list a) = list a
81+type instance Eval (Vectorize list ('Spec labels a)) = 'Spec labels (list a)
8582
8683
8784instance Vector list => MapSpec (Vectorize list) where
8885 mapInfo = \case
89− Spec {..} -> case listNotNull @list nullity of
90− Dict -> Spec
86+ SSpec {..} -> case listNotNull @list nullity of
87+ Dict -> SSpec
9188 { nullity = NotNull
9289 , info = vectorTypeInformation nullity info
9390 , ..
@@ -95,49 +92,60 @@ instance Vector list => MapSpec (Vectorize list) where
9592
9693
9794hvectorize :: (HTable t, Unzip f, Vector list)
98− => (forall a. Spec a -> f (context a) -> context' (list a))
95+ => (forall labels a. ()
96+ => SSpec ('Spec labels a)
97+ -> f (context ('Spec labels a))
98+ -> context' ('Spec labels (list a)))
9999 -> f (t context)
100100 -> HVectorize list t context'
101101hvectorize vectorizer as = HVectorize $ htabulate $ \(HMapTableField field) ->
102102 case hfield hspecs field of
103− spec -> vectorizer spec (fmap (`hfield` field) as)
103+ spec@SSpec {} -> vectorizer spec (fmap (`hfield` field) as)
104104{-# INLINABLE hvectorize #-}
105105
106106
107107hunvectorize :: (HTable t, Zip f, Vector list)
108− => (forall a. Spec a -> context (list a) -> f (context' a))
108+ => (forall labels a. ()
109+ => SSpec ('Spec labels a)
110+ -> context ('Spec labels (list a))
111+ -> f (context' ('Spec labels a)))
109112 -> HVectorize list t context
110113 -> f (t context')
111114hunvectorize unvectorizer (HVectorize table) =
112115 getZippy $ htabulateA $ \field -> case hfield hspecs field of
113− spec -> case hfield table (HMapTableField field) of
116+ spec@SSpec{} -> case hfield table (HMapTableField field) of
114117 a -> Zippy (unvectorizer spec a)
115118{-# INLINABLE hunvectorize #-}
116119
117120
118−happend :: (HTable t, Vector list)
119− => (forall a. Spec a -> context (list a) -> context (list a) -> context (list a))
121+happend :: (HTable t, Vector list) =>
122+ ( forall labels a. ()
123+ => Nullity a
124+ -> TypeInformation (Unnullify a)
125+ -> context ('Spec labels (list a))
126+ -> context ('Spec labels (list a))
127+ -> context ('Spec labels (list a))
128+ )
120129 -> HVectorize list t context
121130 -> HVectorize list t context
122131 -> HVectorize list t context
123132happend append (HVectorize as) (HVectorize bs) = HVectorize $
124133 htabulate $ \field@(HMapTableField j) -> case (hfield as field, hfield bs field) of
125134 (a, b) -> case hfield hspecs j of
126− spec -> append spec a b
135+ SSpec {nullity, info} -> append nullity info a b
127136
128137
129−hempty :: HTable t
130− => (forall a. Spec a -> context [a])
138+hempty :: HTable t =>
139+ ( forall labels a. ()
140+ => Nullity a
141+ -> TypeInformation (Unnullify a)
142+ -> context ('Spec labels [a])
143+ )
131144 -> HVectorize [] t context
132−hempty empty = HVectorize $ htabulate $ \(HMapTableField field) ->
133− empty (hfield hspecs field)
134−
135−
136−hproject :: ()
137− => (forall ctx. t ctx -> t' ctx)
138− -> HVectorize list t context -> HVectorize list t' context
139−hproject f (HVectorize a) = HVectorize (HMapTable.hproject f a)
145+hempty empty = HVectorize $ htabulate $ \(HMapTableField field) -> case hfield hspecs field of
146+ SSpec {nullity, info} -> empty nullity info
140147
141148
142−hcolumn :: HVectorize list (HIdentity a) context -> context (list a)
143−hcolumn (HVectorize (HMapTable (HIdentity (Precompose a)))) = a
149+instance HLabelable g => HLabelable (Precompose (Vectorize list) g) where
150+ hlabeler = Precompose . hlabeler . precomposed
151+ hunlabeler = Precompose . hunlabeler . precomposed
src/Rel8/Schema/Kind.hs+15−6
@@ -1,9 +1,8 @@
11{-# language StandaloneKindSignatures #-}
22
33module Rel8.Schema.Kind
4− ( Rel8able
5− , Context
6− , HTable
4+ ( Context, Rel8able
5+ , HContext, HTable
76 )
87where
98
@@ -11,13 +10,23 @@ where
1110import Data.Kind ( Type )
1211import Prelude ()
1312
13+-- rel8
14+import Rel8.Schema.Spec ( Spec )
1415
15−type Context :: Type
16−type Context = Type -> Type
16+
17+type HContext :: Type
18+type HContext = Spec -> Type
1719
1820
1921type HTable :: Type
20−type HTable = Context -> Type
22+type HTable = HContext -> Type
23+
24+
25+data X
26+
27+
28+type Context :: Type
29+type Context = X -> Type
2130
2231
2332type Rel8able :: Type
src/Rel8/Schema/Name.hs+56−34
@@ -3,44 +3,41 @@
33{-# language FlexibleContexts #-}
44{-# language FlexibleInstances #-}
55{-# language GADTs #-}
6−{-# language GeneralizedNewtypeDeriving #-}
76{-# language MultiParamTypeClasses #-}
7+{-# language PolyKinds #-}
88{-# language RankNTypes #-}
9+{-# language StandaloneDeriving #-}
910{-# language StandaloneKindSignatures #-}
1011{-# language TypeFamilies #-}
1112{-# language UndecidableInstances #-}
13+{-# language UndecidableSuperClasses #-}
1214
1315module Rel8.Schema.Name
1416 ( Name(..)
17+ , Col( N, unN )
1518 , Selects
16− , ppColumn
1719 )
1820where
1921
2022-- base
21−import Data.Functor.Identity ( Identity( Identity ) )
23+import Data.Functor.Identity ( Identity )
2224import Data.Kind ( Constraint, Type )
23−import Data.String ( IsString )
25+import Data.String ( IsString, fromString )
2426import Prelude
2527
26−-- opaleye
27−import qualified Opaleye.Internal.HaskellDB.Sql as Opaleye
28−import qualified Opaleye.Internal.HaskellDB.Sql.Print as Opaleye
29−
30−-- pretty
31−import Text.PrettyPrint ( Doc )
32−
3328-- rel8
3429import Rel8.Expr ( Expr )
35−import qualified Rel8.Schema.Kind as K
36−import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) )
30+import Rel8.Schema.Context ( Interpretation, Col )
31+import Rel8.Schema.Context.Label ( Labelable, labeler, unlabeler )
32+import Rel8.Schema.HTable.Identity ( HIdentity( HType ), HType )
3733import Rel8.Schema.Null ( Sql )
34+import Rel8.Schema.Reify ( notReify )
35+import Rel8.Schema.Result ( Result )
36+import Rel8.Schema.Spec ( Spec( Spec ) )
3837import Rel8.Table
39− ( Table, Columns, Context, fromColumns, toColumns
40− , FromExprs, fromResult, toResult
41− , Transpose
38+ ( Table, Columns, Context, fromColumns, toColumns, reify, unreify
4239 )
43−import Rel8.Table.Transpose ( Transposes )
40+import Rel8.Table.Recontextualize ( Recontextualize )
4441import Rel8.Type ( DBType )
4542
4643
@@ -48,30 +45,55 @@ import Rel8.Type ( DBType )
4845-- schema definition. You can construct names by using the @OverloadedStrings@
4946-- extension and writing string literals. This is typically done when providing
5047-- a 'TableSchema' value.
51−type Name :: K.Context
52−newtype Name a = Name String
53− deriving stock Show
54− deriving newtype IsString
48+type Name :: k -> Type
49+data Name a where
50+ Name :: k ~ Type => !String -> Name (a :: k)
51+
52+
53+deriving stock instance Show (Name a)
54+
55+
56+instance k ~ Type => IsString (Name (a :: k)) where
57+ fromString = Name
5558
5659
5760instance Sql DBType a => Table Name (Name a) where
58− type Columns (Name a) = HIdentity a
61+ type Columns (Name a) = HType a
5962 type Context (Name a) = Name
60− type FromExprs (Name a) = a
61− type Transpose to (Name a) = to a
6263
63− toColumns a = HIdentity a
64− fromColumns (HIdentity a) = a
65− toResult a = HIdentity (Identity a)
66− fromResult (HIdentity (Identity a)) = a
64+ toColumns a = HType (N a)
65+ fromColumns (HType (N a)) = a
66+ reify = notReify
67+ unreify = notReify
68+
69+
70+instance Sql DBType a => Recontextualize Expr Name (Expr a) (Name a)
71+
72+
73+instance Sql DBType a => Recontextualize Result Name (Identity a) (Name a)
74+
75+
76+instance Sql DBType a => Recontextualize Name Expr (Name a) (Expr a)
77+
78+
79+instance Sql DBType a => Recontextualize Name Result (Name a) (Identity a)
80+
81+
82+instance Sql DBType a => Recontextualize Name Name (Name a) (Name a)
83+
84+
85+instance Interpretation Name where
86+ data Col Name _spec where
87+ N :: {unN :: !(Name a)} -> Col Name ('Spec labels a)
88+
89+
90+instance Labelable Name where
91+ labeler (N a) = N a
92+ unlabeler (N a) = N a
6793
6894
6995-- | @Selects a b@ means that @a@ is a schema (i.e., a 'Table' of 'Name's) for
7096-- the 'Expr' columns in @b@.
7197type Selects :: Type -> Type -> Constraint
72−class Transposes Name Expr names exprs => Selects names exprs
73−instance Transposes Name Expr names exprs => Selects names exprs
74−
75−
76−ppColumn :: String -> Doc
77−ppColumn = Opaleye.ppSqlExpr . Opaleye.ColumnSqlExpr . Opaleye.SqlColumn
98+class Recontextualize Name Expr names exprs => Selects names exprs
99+instance Recontextualize Name Expr names exprs => Selects names exprs
src/Rel8/Schema/Reify.hsnew file+74
@@ -0,0 +1,74 @@
1+{-# language AllowAmbiguousTypes #-}
2+{-# language EmptyCase #-}
3+{-# language DataKinds #-}
4+{-# language FlexibleInstances #-}
5+{-# language ScopedTypeVariables #-}
6+{-# language StandaloneKindSignatures #-}
7+{-# language TypeApplications #-}
8+{-# language TypeFamilies #-}
9+{-# language TypeOperators #-}
10+{-# language UndecidableInstances #-}
11+
12+module Rel8.Schema.Reify
13+ ( Reify, Col( Reify ), hreify, hunreify
14+ , UnwrapReify
15+ , NotReify, notReify
16+ )
17+where
18+
19+-- base
20+import Data.Kind ( Constraint )
21+import Data.Type.Equality ( (:~:)( Refl ) )
22+import Prelude
23+
24+-- rel8
25+import Rel8.Schema.Context ( Interpretation, Col )
26+import Rel8.Schema.Context.Label ( Labelable, labeler, unlabeler )
27+import Rel8.Schema.HTable ( HTable, hmap )
28+import Rel8.Schema.Kind ( Context )
29+
30+
31+type Reify :: Context -> Context
32+data Reify context a
33+
34+
35+instance Interpretation (Reify context) where
36+ newtype Col (Reify context) spec = Reify (Col context spec)
37+
38+
39+instance Labelable context => Labelable (Reify context) where
40+ labeler (Reify a) = Reify (labeler a)
41+ unlabeler (Reify a) = Reify (unlabeler a)
42+
43+
44+hreify :: HTable t => t (Col context) -> t (Col (Reify context))
45+hreify = hmap Reify
46+
47+
48+hunreify :: HTable t => t (Col (Reify context)) -> t (Col context)
49+hunreify = hmap (\(Reify a) -> a)
50+
51+
52+type UnwrapReify :: Context -> Context
53+type family UnwrapReify context where
54+ UnwrapReify (Reify context) = context
55+
56+
57+type IsReify :: Context -> Bool
58+type family IsReify context where
59+ IsReify (Reify _) = 'True
60+ IsReify _ = 'False
61+
62+
63+type NotReify :: Context -> Constraint
64+class IsReify context ~ 'False => NotReify context
65+instance IsReify context ~ 'False => NotReify context
66+
67+
68+notReify :: forall context ctx a. NotReify context => context :~: Reify ctx -> a
69+notReify refl = case lemma @context of
70+ Refl -> case refl of
71+
72+
73+lemma :: NotReify context => IsReify context :~: 'False
74+lemma = Refl
src/Rel8/Schema/Result.hs+41−16
@@ -5,20 +5,22 @@
55{-# language TypeFamilies #-}
66
77module Rel8.Schema.Result
8− ( Result
8+ ( Col( R, unR ), Result
9+ , relabel
910 , null, nullifier, unnullifier
1011 , vectorizer, unvectorizer
1112 )
1213where
1314
1415-- base
15−import Data.Functor.Identity ( Identity( Identity), runIdentity )
1616import Prelude hiding ( null )
1717
1818-- rel8
19+import Rel8.Schema.Context ( Interpretation( Col ) )
20+import Rel8.Schema.HTable.Identity ( HIdentity(..) )
1921import Rel8.Schema.Kind ( Context )
2022import Rel8.Schema.Null ( Nullify, Nullity( Null, NotNull ) )
21−import Rel8.Schema.Spec ( Spec(..) )
23+import Rel8.Schema.Spec ( Spec( Spec ), SSpec(..) )
2224
2325
2426-- | The @Result@ context is the context used for decoded query results.
@@ -26,29 +28,52 @@ import Rel8.Schema.Spec ( Spec(..) )
2628-- When a query is executed against a PostgreSQL database, Rel8 parses the
2729-- returned rows, decoding each row into the @Result@ context.
2830type Result :: Context
29−type Result = Identity
31+data Result a
3032
3133
32−null :: Result (Maybe a)
33−null = Identity Nothing
34+instance Interpretation Result where
35+ data Col Result _spec where
36+ R :: {unR :: !a} -> Col Result ('Spec labels a)
3437
3538
36−nullifier :: Spec a -> Result a -> Result (Nullify a)
37−nullifier Spec {nullity} (Identity a) = Identity $ case nullity of
39+relabel :: ()
40+ => HIdentity ('Spec labels a) (Col Result)
41+ -> HIdentity ('Spec relabels a) (Col Result)
42+relabel (HIdentity (R a)) = HIdentity (R a)
43+
44+
45+null :: Col Result ('Spec labels (Maybe a))
46+null = R Nothing
47+
48+
49+nullifier :: ()
50+ => SSpec ('Spec labels a)
51+ -> Col Result ('Spec labels a)
52+ -> Col Result ('Spec labels (Nullify a))
53+nullifier SSpec {nullity} (R a) = R $ case nullity of
3854 Null -> a
3955 NotNull -> Just a
4056
4157
42−unnullifier :: Spec a -> Result (Nullify a) -> Maybe (Result a)
43−unnullifier Spec {nullity} (Identity a) =
58+unnullifier :: ()
59+ => SSpec ('Spec labels a)
60+ -> Col Result ('Spec labels (Nullify a))
61+ -> Maybe (Col Result ('Spec labels a))
62+unnullifier SSpec {nullity} (R a) =
4463 case nullity of
45− Null -> pure $ Identity a
46− NotNull -> Identity <$> a
64+ Null -> pure $ R a
65+ NotNull -> R <$> a
4766
4867
49−vectorizer :: Functor f => Spec a -> f (Result a) -> Result (f a)
50−vectorizer _ = Identity . fmap runIdentity
68+vectorizer :: Functor f
69+ => SSpec ('Spec labels a)
70+ -> f (Col Result ('Spec labels a))
71+ -> Col Result ('Spec labels (f a))
72+vectorizer _ = R . fmap unR
5173
5274
53−unvectorizer :: Functor f => Spec a -> Result (f a) -> f (Result a)
54−unvectorizer _ (Identity results) = Identity <$> results
75+unvectorizer :: Functor f
76+ => SSpec ('Spec labels a)
77+ -> Col Result ('Spec labels (f a))
78+ -> f (Col Result ('Spec labels a))
79+unvectorizer _ (R results) = R <$> results
src/Rel8/Schema/Spec.hs+37−17
@@ -1,34 +1,54 @@
1+{-# language DataKinds #-}
12{-# language FlexibleContexts #-}
2−{-# language MonoLocalBinds #-}
3+{-# language GADTs #-}
34{-# language StandaloneKindSignatures #-}
5+{-# language UndecidableInstances #-}
46
57module Rel8.Schema.Spec
6− ( Spec( Spec, labels, info, nullity )
7− , specification
8+ ( Spec( Spec )
9+ , SSpec( SSpec, labels, info, nullity )
10+ , KnownSpec( specSing )
811 )
912where
1013
1114-- base
12−import Data.Kind ( Type )
13−import Prelude
15+import Data.Kind ( Constraint, Type )
16+import Prelude ()
1417
1518-- rel8
19+import Rel8.Kind.Labels ( Labels, SLabels, KnownLabels, labelsSing )
1620import Rel8.Schema.Null ( Nullity, Sql, Unnullify, nullable )
1721import Rel8.Type ( DBType, typeInformation )
1822import Rel8.Type.Information ( TypeInformation )
1923
2024
21−type Spec :: Type -> Type
22−data Spec a = Spec
23− { labels :: [String]
24− , info :: TypeInformation (Unnullify a)
25− , nullity :: Nullity a
26− }
25+type Spec :: Type
26+data Spec = Spec Labels Type
2727
2828
29−specification :: Sql DBType a => Spec a
30−specification = Spec
31− { labels = []
32− , info = typeInformation
33− , nullity = nullable
34− }
29+type SSpec :: Spec -> Type
30+data SSpec spec where
31+ SSpec ::
32+ { labels :: SLabels labels
33+ , info :: TypeInformation (Unnullify a)
34+ , nullity :: Nullity a
35+ }
36+ -> SSpec ('Spec labels a)
37+
38+
39+type KnownSpec :: Spec -> Constraint
40+class KnownSpec spec where
41+ specSing :: SSpec spec
42+
43+
44+instance
45+ ( KnownLabels labels
46+ , Sql DBType a
47+ )
48+ => KnownSpec ('Spec labels a)
49+ where
50+ specSing = SSpec
51+ { labels = labelsSing
52+ , info = typeInformation
53+ , nullity = nullable
54+ }
src/Rel8/Schema/Spec/ConstrainDBType.hsnew file+89
@@ -0,0 +1,89 @@
1+{-# language ConstraintKinds #-}
2+{-# language DataKinds #-}
3+{-# language FlexibleInstances #-}
4+{-# language MultiParamTypeClasses #-}
5+{-# language NamedFieldPuns #-}
6+{-# language QuantifiedConstraints #-}
7+{-# language StandaloneKindSignatures #-}
8+{-# language TypeFamilies #-}
9+{-# language UndecidableInstances #-}
10+
11+module Rel8.Schema.Spec.ConstrainDBType
12+ ( ConstrainDBType
13+ , dbTypeNullity, dbTypeDict
14+ , nullifier, unnullifier
15+ )
16+where
17+
18+-- base
19+import Data.Kind ( Constraint, Type )
20+import Prelude
21+
22+-- rel8
23+import Rel8.Schema.Dict ( Dict( Dict ) )
24+import Rel8.Schema.Null
25+ ( Nullify, Unnullify
26+ , Nullity( Null, NotNull )
27+ , Sql, nullable
28+ )
29+import Rel8.Schema.Spec ( Spec( Spec ), SSpec( SSpec, nullity ) )
30+
31+
32+type ConstrainDBType :: (Type -> Constraint) -> Spec -> Constraint
33+class
34+ ( forall c labels a. ()
35+ => (spec ~ 'Spec labels a)
36+ => (forall x. (constraint x => c x)) => Sql c a
37+ )
38+ => ConstrainDBType constraint spec
39+instance
40+ ( spec ~ 'Spec labels a
41+ , Sql constraint a
42+ )
43+ => ConstrainDBType constraint spec
44+
45+
46+dbTypeNullity :: Dict (ConstrainDBType c) ('Spec l a) -> Nullity a
47+dbTypeNullity = step2 . step1
48+ where
49+ step1 :: Dict (ConstrainDBType c) ('Spec l a) -> Dict (Sql c) a
50+ step1 Dict = Dict
51+
52+ step2 :: Dict (Sql c) a -> Nullity a
53+ step2 Dict = nullable
54+
55+
56+dbTypeDict :: Dict (ConstrainDBType c) ('Spec l a) -> Dict c (Unnullify a)
57+dbTypeDict = step2 . step1
58+ where
59+ step1 :: Dict (ConstrainDBType c) ('Spec l a) -> Dict (Sql c) a
60+ step1 Dict = Dict
61+
62+ step2 :: Dict (Sql c) a -> Dict c (Unnullify a)
63+ step2 Dict = Dict
64+
65+
66+fromNullityDict :: Nullity a -> Dict c (Unnullify a) -> Dict (ConstrainDBType c) ('Spec l a)
67+fromNullityDict Null Dict = Dict
68+fromNullityDict NotNull Dict = Dict
69+
70+
71+nullifier :: ()
72+ => SSpec ('Spec labels a)
73+ -> Dict (ConstrainDBType c) ('Spec labels a)
74+ -> Dict (ConstrainDBType c) ('Spec labels (Nullify a))
75+nullifier SSpec {} dict = case dbTypeDict dict of
76+ Dict -> case dbTypeNullity dict of
77+ Null -> Dict
78+ NotNull -> Dict
79+
80+
81+unnullifier :: ()
82+ => SSpec ('Spec labels a)
83+ -> Dict (ConstrainDBType c) ('Spec labels (Nullify a))
84+ -> Dict (ConstrainDBType c) ('Spec labels a)
85+unnullifier SSpec {nullity} dict = case dbTypeDict dict of
86+ Dict -> case nullity of
87+ Null -> Dict
88+ NotNull -> case dbTypeNullity dict of
89+ Null -> fromNullityDict nullity Dict
src/Rel8/Schema/Table.hs−20
@@ -1,26 +1,14 @@
11{-# language DeriveFunctor #-}
22{-# language DerivingStrategies #-}
3−{-# language DisambiguateRecordFields #-}
4−{-# language NamedFieldPuns #-}
5−{-# language StandaloneKindSignatures #-}
63
74module Rel8.Schema.Table
85 ( TableSchema(..)
9− , ppTable
106 )
117where
128
139-- base
14−import Data.Kind ( Type )
1510import Prelude
1611
17−-- opaleye
18−import qualified Opaleye.Internal.HaskellDB.Sql as Opaleye
19−import qualified Opaleye.Internal.HaskellDB.Sql.Print as Opaleye
20−
21−-- pretty
22−import Text.PrettyPrint ( Doc )
23−
2412
2513-- | The schema for a table. This is used to specify the name and schema that a
2614-- table belongs to (the @FROM@ part of a SQL query), along with the schema of
@@ -28,7 +16,6 @@ import Text.PrettyPrint ( Doc )
2816--
2917-- For each selectable table in your database, you should provide a
3018-- @TableSchema@ in order to interact with the table via Rel8.
31−type TableSchema :: Type -> Type
3219data TableSchema names = TableSchema
3320 { name :: String
3421 -- ^ The name of the table.
@@ -40,10 +27,3 @@ data TableSchema names = TableSchema
4027 -- data type here, parameterized by the 'Rel8.ColumnSchema.ColumnSchema' functor.
4128 }
4229 deriving stock Functor
43−
44−
45−ppTable :: TableSchema a -> Doc
46−ppTable TableSchema {name, schema} = Opaleye.ppTable Opaleye.SqlTable
47− { sqlTableSchemaName = schema
48− , sqlTableName = name
49− }
src/Rel8/Statement/Delete.hs+48−35
@@ -1,37 +1,40 @@
11{-# language DuplicateRecordFields #-}
22{-# language GADTs #-}
33{-# language NamedFieldPuns #-}
4−{-# language RankNTypes #-}
5−{-# language RecordWildCards #-}
4+{-# language ScopedTypeVariables #-}
65{-# language StandaloneKindSignatures #-}
7−{-# language StrictData #-}
6+{-# language TypeApplications #-}
87
98module Rel8.Statement.Delete
109 ( Delete(..)
1110 , delete
12− , ppDelete
1311 )
1412where
1513
1614-- base
15+import Control.Exception ( throwIO )
1716import Data.Kind ( Type )
1817import Prelude
1918
2019-- hasql
20+import Hasql.Connection ( Connection )
21+import qualified Hasql.Decoders as Hasql
2122import qualified Hasql.Encoders as Hasql
23+import qualified Hasql.Session as Hasql
2224import qualified Hasql.Statement as Hasql
2325
24−-- pretty
25−import Text.PrettyPrint ( Doc, (<+>), ($$), text )
26+-- opaleye
27+import qualified Opaleye.Internal.Manipulation as Opaleye
2628
2729-- rel8
2830import Rel8.Expr ( Expr )
29−import Rel8.Query ( Query )
31+import Rel8.Expr.Opaleye ( toColumn, toPrimExpr )
3032import Rel8.Schema.Name ( Selects )
31−import Rel8.Schema.Table ( TableSchema, ppTable )
32−import Rel8.Statement.Returning ( Returning, decodeReturning, ppReturning )
33−import Rel8.Statement.Using ( ppUsing )
34−import Rel8.Statement.Where ( ppWhere )
33+import Rel8.Schema.Table ( TableSchema )
34+import Rel8.Statement.Returning ( Returning( NumberOfRowsAffected, Projection ) )
35+import Rel8.Table ( fromColumns, toColumns )
36+import Rel8.Table.Opaleye ( castTable, table, unpackspec )
37+import Rel8.Table.Serialize ( Serializable, parse )
3538
3639-- text
3740import qualified Data.Text as Text
@@ -44,10 +47,7 @@ data Delete a where
4447 Delete :: Selects names exprs =>
4548 { from :: TableSchema names
4649 -- ^ Which table to delete from.
47− , using :: Query using
48− -- ^ @USING@ clause — this can be used to join against other tables,
49− -- and its results can be referenced in the @WHERE@ clause
50− , deleteWhere :: using -> exprs -> Expr Bool
50+ , deleteWhere :: exprs -> Expr Bool
5151 -- ^ Which rows should be selected for deletion.
5252 , returning :: Returning names a
5353 -- ^ What to return from the @DELETE@ statement.
@@ -55,25 +55,38 @@ data Delete a where
5555 -> Delete a
5656
5757
58−ppDelete :: Delete a -> Doc
59−ppDelete Delete {..} = case ppUsing using of
60− Nothing ->
61− text "DELETE FROM" <+> ppTable from $$
62− text "WHERE false"
63− Just (usingDoc, i) ->
64− text "DELETE FROM" <+> ppTable from $$
65− usingDoc $$
66− ppWhere from (deleteWhere i) $$
67− ppReturning from returning
58+-- | Run a @DELETE@ statement.
59+delete :: Connection -> Delete a -> IO a
60+delete c Delete {from, deleteWhere, returning} =
61+ case returning of
62+ NumberOfRowsAffected -> Hasql.run session c >>= either throwIO pure
63+ where
64+ session = Hasql.statement () statement
65+ statement = Hasql.Statement bytes params decode prepare
66+ bytes = encodeUtf8 $ Text.pack sql
67+ params = Hasql.noParams
68+ decode = Hasql.rowsAffected
69+ prepare = False
70+ sql = Opaleye.arrangeDeleteSql from' where'
71+ where
72+ from' = table $ toColumns <$> from
73+ where' = toColumn . toPrimExpr . deleteWhere . fromColumns
6874
69−
70−-- | Run a 'Delete' statement.
71−delete :: Delete a -> Hasql.Statement () a
72−delete d@Delete {returning} = Hasql.Statement bytes params decode prepare
75+ Projection project -> Hasql.run session c >>= either throwIO pure
76+ where
77+ session = Hasql.statement () statement
78+ statement = Hasql.Statement bytes params decode prepare
79+ bytes = encodeUtf8 $ Text.pack sql
80+ params = Hasql.noParams
81+ decode = decoder project
82+ prepare = False
83+ sql =
84+ Opaleye.arrangeDeleteReturningSql unpackspec from' where' project'
85+ where
86+ from' = table $ toColumns <$> from
87+ where' = toColumn . toPrimExpr . deleteWhere . fromColumns
88+ project' = castTable . toColumns . project . fromColumns
7389 where
74− bytes = encodeUtf8 $ Text.pack sql
75− params = Hasql.noParams
76− decode = decodeReturning returning
77− prepare = False
78− sql = show doc
79− doc = ppDelete d
90+ decoder :: forall exprs projection a. Serializable projection a
91+ => (exprs -> projection) -> Hasql.Result [a]
92+ decoder _ = Hasql.rowList (parse @projection @a)
src/Rel8/Statement/Insert.hs+72−43
@@ -1,59 +1,64 @@
11{-# language DuplicateRecordFields #-}
2−{-# language FlexibleContexts #-}
32{-# language GADTs #-}
43{-# language NamedFieldPuns #-}
5−{-# language RecordWildCards #-}
4+{-# language ScopedTypeVariables #-}
65{-# language StandaloneKindSignatures #-}
7−{-# language StrictData #-}
6+{-# language TypeApplications #-}
87
98module Rel8.Statement.Insert
109 ( Insert(..)
10+ , OnConflict(..)
1111 , insert
12− , ppInsert
13− , ppInto
1412 )
1513where
1614
1715-- base
18−import Data.Foldable ( toList )
16+import Control.Exception ( throwIO )
17+import Data.List.NonEmpty ( NonEmpty( (:|) ) )
1918import Data.Kind ( Type )
2019import Prelude
2120
2221-- hasql
22+import Hasql.Connection ( Connection )
23+import qualified Hasql.Decoders as Hasql
2324import qualified Hasql.Encoders as Hasql
25+import qualified Hasql.Session as Hasql
2426import qualified Hasql.Statement as Hasql
2527
2628-- opaleye
27−import qualified Opaleye.Internal.HaskellDB.Sql.Print as Opaleye
28−
29−-- pretty
30−import Text.PrettyPrint ( Doc, (<+>), ($$), parens, text )
29+import qualified Opaleye.Internal.Manipulation as Opaleye
30+import qualified Opaleye.Manipulation as Opaleye
3131
3232-- rel8
33−import Rel8.Query ( Query )
34−import Rel8.Schema.Name ( Name, Selects, ppColumn )
35−import Rel8.Schema.Table ( TableSchema(..), ppTable )
36−import Rel8.Statement.OnConflict ( OnConflict, ppOnConflict )
37−import Rel8.Statement.Returning ( Returning, decodeReturning, ppReturning )
38−import Rel8.Statement.Select ( ppRows )
39−import Rel8.Table ( Table )
40−import Rel8.Table.Name ( showNames )
33+import Rel8.Schema.Name ( Selects )
34+import Rel8.Schema.Table ( TableSchema )
35+import Rel8.Statement.Returning ( Returning( Projection, NumberOfRowsAffected ) )
36+import Rel8.Table ( fromColumns, toColumns )
37+import Rel8.Table.Opaleye ( castTable, table, unpackspec )
38+import Rel8.Table.Serialize ( Serializable, parse )
4139
4240-- text
4341import qualified Data.Text as Text ( pack )
4442import Data.Text.Encoding ( encodeUtf8 )
4543
4644
45+-- | @OnConflict@ allows you to add an @ON CONFLICT@ clause to an @INSERT@
46+-- statement.
47+type OnConflict :: Type
48+data OnConflict
49+ = Abort -- ^ @ON CONFLICT ABORT@
50+ | DoNothing -- ^ @ON CONFLICT DO NOTHING@
51+
52+
4753-- | The constituent parts of a SQL @INSERT@ statement.
4854type Insert :: Type -> Type
4955data Insert a where
5056 Insert :: Selects names exprs =>
5157 { into :: TableSchema names
5258 -- ^ Which table to insert into.
53− , rows :: Query exprs
54− -- ^ The rows to insert. This can be an arbitrary query — use
55− -- 'Rel8.values' insert a static list of rows.
56− , onConflict :: OnConflict names
59+ , rows :: [exprs]
60+ -- ^ The rows to insert.
61+ , onConflict :: OnConflict
5762 -- ^ What to do if the inserted rows conflict with data already in the
5863 -- table.
5964 , returning :: Returning names a
@@ -62,28 +67,52 @@ data Insert a where
6267 -> Insert a
6368
6469
65−ppInsert :: Insert a -> Doc
66−ppInsert Insert {..} =
67− text "INSERT INTO" <+>
68− ppInto into $$
69− ppRows rows $$
70− ppOnConflict into onConflict $$
71− ppReturning into returning
70+-- | Run an @INSERT@ statement
71+insert :: Connection -> Insert a -> IO a
72+insert c Insert {into, rows, onConflict, returning} =
73+ case (rows, returning) of
74+ ([], NumberOfRowsAffected) -> pure 0
75+ ([], Projection _) -> pure []
7276
77+ (x:xs, NumberOfRowsAffected) -> Hasql.run session c >>= either throwIO pure
78+ where
79+ session = Hasql.statement () statement
80+ statement = Hasql.Statement bytes params decode prepare
81+ bytes = encodeUtf8 $ Text.pack sql
82+ params = Hasql.noParams
83+ decode = Hasql.rowsAffected
84+ prepare = False
85+ sql = Opaleye.arrangeInsertManySql into' rows' onConflict'
86+ where
87+ into' = table $ toColumns <$> into
88+ rows' = toColumns <$> x :| xs
7389
74−ppInto :: Table Name a => TableSchema a -> Doc
75−ppInto table@TableSchema {columns} =
76− ppTable table <+>
77− parens (Opaleye.commaV ppColumn (toList (showNames columns)))
90+ (x:xs, Projection project) -> Hasql.run session c >>= either throwIO pure
91+ where
92+ session = Hasql.statement () statement
93+ statement = Hasql.Statement bytes params decode prepare
94+ bytes = encodeUtf8 $ Text.pack sql
95+ params = Hasql.noParams
96+ decode = decoder project
97+ prepare = False
98+ sql =
99+ Opaleye.arrangeInsertManyReturningSql
100+ unpackspec
101+ into'
102+ rows'
103+ project'
104+ onConflict'
105+ where
106+ into' = table $ toColumns <$> into
107+ rows' = toColumns <$> x :| xs
108+ project' = castTable . toColumns . project . fromColumns
78109
79−
80−-- | Run an 'Insert' statement.
81−insert :: Insert a -> Hasql.Statement () a
82−insert i@Insert {returning} = Hasql.Statement bytes params decode prepare
83110 where
84− bytes = encodeUtf8 $ Text.pack sql
85− params = Hasql.noParams
86− decode = decodeReturning returning
87− prepare = False
88− sql = show doc
89− doc = ppInsert i
111+ onConflict' =
112+ case onConflict of
113+ DoNothing -> Just Opaleye.DoNothing
114+ Abort -> Nothing
115+
116+ decoder :: forall exprs projection a. Serializable projection a
117+ => (exprs -> projection) -> Hasql.Result [a]
118+ decoder _ = Hasql.rowList (parse @projection @a)
src/Rel8/Statement/OnConflict.hsdeleted−106
@@ -1,106 +0,0 @@
1−{-# language DuplicateRecordFields #-}
2−{-# language FlexibleContexts #-}
3−{-# language GADTs #-}
4−{-# language LambdaCase #-}
5−{-# language NamedFieldPuns #-}
6−{-# language RecordWildCards #-}
7−{-# language StandaloneKindSignatures #-}
8−{-# language StrictData #-}
9−
10−module Rel8.Statement.OnConflict
11− ( OnConflict(..)
12− , Upsert(..)
13− , ppOnConflict
14− )
15−where
16−
17−-- base
18−import Data.Foldable ( toList )
19−import Data.Kind ( Type )
20−import Prelude
21−
22−-- opaleye
23−import qualified Opaleye.Internal.HaskellDB.Sql.Print as Opaleye
24−
25−-- pretty
26−import Text.PrettyPrint ( Doc, (<+>), ($$), parens, text )
27−
28−-- rel8
29−import Rel8.Expr ( Expr )
30−import Rel8.Schema.Name ( Name, Selects, ppColumn )
31−import Rel8.Schema.Table ( TableSchema(..) )
32−import Rel8.Statement.Set ( ppSet )
33−import Rel8.Statement.Where ( ppWhere )
34−import Rel8.Table ( Table, toColumns )
35−import Rel8.Table.Cols ( Cols( Cols ) )
36−import Rel8.Table.Name ( showNames )
37−import Rel8.Table.Opaleye ( attributes )
38−import Rel8.Table.Projection ( Projecting, Projection, apply )
39−
40−
41−-- | 'OnConflict' represents the @ON CONFLICT@ clause of an @INSERT@
42−-- statement. This specifies what ought to happen when one or more of the
43−-- rows proposed for insertion conflict with an existing row in the table.
44−type OnConflict :: Type -> Type
45−data OnConflict names
46− = Abort
47− -- ^ Abort the transaction if there are conflicting rows (Postgres' default)
48− | DoNothing
49− -- ^ @ON CONFLICT DO NOTHING@
50− | DoUpdate (Upsert names)
51− -- ^ @ON CONFLICT DO UPDATE@
52−
53−
54−-- | The @ON CONFLICT (...) DO UPDATE@ clause of an @INSERT@ statement, also
55−-- known as \"upsert\".
56−--
57−-- When an existing row conflicts with a row proposed for insertion,
58−-- @ON CONFLICT DO UPDATE@ allows you to instead update this existing row. The
59−-- conflicting row proposed for insertion is then \"excluded\", but its values
60−-- can still be referenced from the @SET@ and @WHERE@ clauses of the @UPDATE@
61−-- statement.
62−--
63−-- Upsert in Postgres requires an explicit set of \"conflict targets\" — the
64−-- set of columns comprising the @UNIQUE@ index from conflicts with which we
65−-- would like to recover.
66−type Upsert :: Type -> Type
67−data Upsert names where
68− Upsert :: (Selects names exprs, Projecting names index, excluded ~ exprs) =>
69− { index :: Projection names index
70− -- ^ The set of conflict targets, projected from the set of columns for
71− -- the whole table
72− , set :: excluded -> exprs -> exprs
73− -- ^ How to update each selected row.
74− , updateWhere :: excluded -> exprs -> Expr Bool
75− -- ^ Which rows to select for update.
76− }
77− -> Upsert names
78−
79−
80−ppOnConflict :: TableSchema names -> OnConflict names -> Doc
81−ppOnConflict schema = \case
82− Abort -> mempty
83− DoNothing -> text "ON CONFLICT DO NOTHING"
84− DoUpdate upsert -> ppUpsert schema upsert
85−
86−
87−ppUpsert :: TableSchema names -> Upsert names -> Doc
88−ppUpsert schema@TableSchema {columns} Upsert {..} =
89− text "ON CONFLICT" <+>
90− ppIndex schema index <+>
91− text "DO UPDATE" $$
92− ppSet schema (set excluded) $$
93− ppWhere schema (updateWhere excluded)
94− where
95− excluded = attributes TableSchema
96− { schema = Nothing
97− , name = "excluded"
98− , columns
99− }
100−
101−
102−ppIndex :: (Table Name names, Projecting names index)
103− => TableSchema names -> Projection names index -> Doc
104−ppIndex TableSchema {columns} index =
105− parens $ Opaleye.commaV ppColumn $ toList $
106− showNames $ Cols $ apply index $ toColumns columns
src/Rel8/Statement/Returning.hs+10−114
@@ -1,133 +1,29 @@
11{-# language GADTs #-}
2−{-# language LambdaCase #-}
3−{-# language NamedFieldPuns #-}
4−{-# language RankNTypes #-}
5−{-# language ScopedTypeVariables #-}
62{-# language StandaloneKindSignatures #-}
7−{-# language StrictData #-}
8−{-# language TypeApplications #-}
93
104module Rel8.Statement.Returning
11− ( Returning( NumberOfRowsAffected, Projection )
12− , decodeReturning
13− , ppReturning
5+ ( Returning(..)
146 )
157where
168
179-- base
18−import Control.Applicative ( liftA2 )
19−import Data.Foldable ( toList )
2010import Data.Int ( Int64 )
2111import Data.Kind ( Type )
22−import Data.List.NonEmpty ( NonEmpty )
23−import Prelude
24−
25−-- hasql
26−import qualified Hasql.Decoders as Hasql
27−
28−-- opaleye
29−import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
30−import qualified Opaleye.Internal.HaskellDB.Sql.Print as Opaleye
31−import qualified Opaleye.Internal.Sql as Opaleye
32−
33−-- pretty
34−import Text.PrettyPrint ( Doc, (<+>), text )
12+import Prelude ()
3513
3614-- rel8
3715import Rel8.Schema.Name ( Selects )
38−import Rel8.Schema.Table ( TableSchema(..) )
39−import Rel8.Table.Opaleye ( castTable, exprs, view )
40−import Rel8.Table.Serialize ( Serializable, parse )
16+import Rel8.Table.Serialize ( Serializable )
4117
42−-- semigropuoids
43−import Data.Functor.Apply ( Apply, (<.>) )
4418
45−
46−-- | 'Rel8.Insert', 'Rel8.Update' and 'Rel8.Delete' all support returning either
47−-- the number of rows affected, or the actual rows modified.
19+-- | @INSERT@, @UPDATE@ and @DELETE@ all support returning either the number of
20+-- rows affected, or the actual rows modified. 'Projection' allows you to
21+-- project out of these returned rows, which can be useful if you want to log
22+-- exactly which rows were deleted, or to view a generated id (for example, if
23+-- using a column with an autoincrementing counter as a default value).
4824type Returning :: Type -> Type -> Type
4925data Returning names a where
50− Pure :: a -> Returning names a
51− Ap :: Returning names (a -> b) -> Returning names a -> Returning names b
52−
53− -- | Return the number of rows affected.
5426 NumberOfRowsAffected :: Returning names Int64
55−
56− -- | 'Projection' allows you to project out of the affected rows, which can
57− -- be useful if you want to log exactly which rows were deleted, or to view
58− -- a generated id (for example, if using a column with an autoincrementing
59− -- counter via 'Rel8.nextval').
60− Projection :: (Selects names exprs, Serializable returning a)
61− => (exprs -> returning)
27+ Projection :: (Selects names exprs, Serializable projection a)
28+ => (exprs -> projection)
6229 -> Returning names [a]
63−
64−
65−instance Functor (Returning names) where
66− fmap f = \case
67− Pure a -> Pure (f a)
68− Ap g a -> Ap (fmap (f .) g) a
69− m -> Ap (Pure f) m
70−
71−
72−instance Apply (Returning names) where
73− (<.>) = Ap
74−
75−
76−instance Applicative (Returning names) where
77− pure = Pure
78− (<*>) = Ap
79−
80−
81−projections :: ()
82− => TableSchema names -> Returning names a -> Maybe (NonEmpty Opaleye.PrimExpr)
83−projections schema@TableSchema {columns} = \case
84− Pure _ -> Nothing
85− Ap f a -> projections schema f <> projections schema a
86− NumberOfRowsAffected -> Nothing
87− Projection f -> Just (exprs (castTable (f (view columns))))
88−
89−
90−runReturning :: ()
91− => ((Int64 -> a) -> r)
92− -> (forall x. Hasql.Row x -> ([x] -> a) -> r)
93− -> Returning names a
94− -> r
95−runReturning rowCount rowList = \case
96− Pure a -> rowCount (const a)
97− Ap fs as ->
98− runReturning
99− (\withCount ->
100− runReturning
101− (\withCount' -> rowCount (withCount <*> withCount'))
102− (\decoder -> rowList decoder . liftA2 withCount length64)
103− as)
104− (\decoder withRows ->
105− runReturning
106− (\withCount -> rowList decoder $ withRows <*> withCount . length64)
107− (\decoder' withRows' ->
108− rowList (liftA2 (,) decoder decoder') $
109− withRows <$> fmap fst <*> withRows' . fmap snd)
110− as)
111− fs
112− NumberOfRowsAffected -> rowCount id
113− Projection (_ :: exprs -> returning) -> rowList decoder' id
114− where
115− decoder' = parse @returning
116− where
117− length64 :: Foldable f => f x -> Int64
118− length64 = fromIntegral . length
119−
120−
121−decodeReturning :: Returning names a -> Hasql.Result a
122−decodeReturning = runReturning
123− (<$> Hasql.rowsAffected)
124− (\decoder withRows -> withRows <$> Hasql.rowList decoder)
125−
126−
127−ppReturning :: TableSchema names -> Returning names a -> Doc
128−ppReturning schema returning = case projections schema returning of
129− Nothing -> mempty
130− Just columns ->
131− text "RETURNING" <+> Opaleye.commaV Opaleye.ppSqlExpr (toList sqlExprs)
132− where
133− sqlExprs = Opaleye.sqlExpr <$> columns
src/Rel8/Statement/SQL.hsdeleted−29
@@ -1,29 +0,0 @@
1−module Rel8.Statement.SQL
2− ( showDelete
3− , showInsert
4− , showUpdate
5− )
6−where
7−
8−-- base
9−import Prelude
10−
11−-- rel8
12−import Rel8.Statement.Delete ( Delete, ppDelete )
13−import Rel8.Statement.Insert ( Insert, ppInsert )
14−import Rel8.Statement.Update ( Update, ppUpdate )
15−
16−
17−-- | Convert a 'Delete' to a 'String' containing a @DELETE@ statement.
18−showDelete :: Delete a -> String
19−showDelete = show . ppDelete
20−
21−
22−-- | Convert an 'Insert' to a 'String' containing an @INSERT@ statement.
23−showInsert :: Insert a -> String
24−showInsert = show . ppInsert
25−
26−
27−-- | Convert an 'Update' to a 'String' containing an @UPDATE@ statement.
28−showUpdate :: Update a -> String
29−showUpdate = show . ppUpdate
src/Rel8/Statement/Select.hs+35−120
@@ -1,147 +1,62 @@
1−{-# language DeriveTraversable #-}
2−{-# language DerivingStrategies #-}
3−{-# language FlexibleContexts #-}
41{-# language MonoLocalBinds #-}
52{-# language ScopedTypeVariables #-}
6−{-# language StandaloneKindSignatures #-}
73{-# language TypeApplications #-}
84
95module Rel8.Statement.Select
106 ( select
11− , ppSelect
12−
13− , Optimized(..)
14− , ppPrimSelect
15− , ppRows
7+ , selectWithNames
168 )
179where
1810
1911-- base
20−import Data.Foldable ( toList )
21−import Data.Kind ( Type )
22−import Data.Void ( Void )
23−import Prelude hiding ( undefined )
12+import Control.Exception ( throwIO )
13+import Prelude
2414
2515-- hasql
16+import Hasql.Connection ( Connection )
2617import qualified Hasql.Decoders as Hasql
2718import qualified Hasql.Encoders as Hasql
19+import qualified Hasql.Session as Hasql
2820import qualified Hasql.Statement as Hasql
2921
30−-- opaleye
31−import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
32−import qualified Opaleye.Internal.HaskellDB.Sql as Opaleye
33−import qualified Opaleye.Internal.HaskellDB.Sql.Print as Opaleye
34−import qualified Opaleye.Internal.PrimQuery as Opaleye
35−import qualified Opaleye.Internal.Print as Opaleye
36−import qualified Opaleye.Internal.Optimize as Opaleye
37−import qualified Opaleye.Internal.QueryArr as Opaleye hiding ( Select )
38−import qualified Opaleye.Internal.Sql as Opaleye hiding ( Values )
39−import qualified Opaleye.Internal.Tag as Opaleye
40−
41−-- pretty
42−import Text.PrettyPrint ( Doc )
43−
4422-- rel8
45−import Rel8.Expr ( Expr )
46−import Rel8.Expr.Bool ( false )
47−import Rel8.Expr.Opaleye ( toPrimExpr )
4823import Rel8.Query ( Query )
49−import Rel8.Query.Opaleye ( toOpaleye )
24+import Rel8.Query.SQL ( sqlForQuery, sqlForQueryWithNames )
5025import Rel8.Schema.Name ( Selects )
51−import Rel8.Table ( Table )
52−import Rel8.Table.Cols ( toCols )
53−import Rel8.Table.Name ( namesFromLabels )
54−import Rel8.Table.Opaleye ( castTable, exprsWithNames )
55−import qualified Rel8.Table.Opaleye as T
5626import Rel8.Table.Serialize ( Serializable, parse )
57−import Rel8.Table.Undefined ( undefined )
5827
5928-- text
6029import qualified Data.Text as Text
6130import Data.Text.Encoding ( encodeUtf8 )
6231
6332
64−-- | Run a @SELECT@ statement, returning all rows.
33+-- | Run a @SELECT@ query, returning all rows.
6534select :: forall exprs a. Serializable exprs a
66− => Query exprs -> Hasql.Statement () [a]
67−select query = Hasql.Statement bytes params decode prepare
68− where
69− bytes = encodeUtf8 (Text.pack sql)
70− params = Hasql.noParams
71− decode = Hasql.rowList (parse @exprs @a)
72− prepare = False
73− sql = show doc
74− doc = ppSelect query
75−
76−
77−ppSelect :: Table Expr a => Query a -> Doc
78−ppSelect query =
79− Opaleye.ppSql $ primSelectWith names (toCols exprs') primQuery'
80− where
81− names = namesFromLabels
82− (exprs, primQuery, _) =
83− Opaleye.runSimpleQueryArrStart (toOpaleye query) ()
84− (exprs', primQuery') = case optimize primQuery of
85− Empty -> (undefined, Opaleye.Product (pure (pure Opaleye.Unit)) never)
86− Unit -> (exprs, Opaleye.Unit)
87− Optimized pq -> (exprs, pq)
88− never = pure (toPrimExpr false)
89−
90−
91−ppRows :: Table Expr a => Query a -> Doc
92−ppRows query = case optimize primQuery of
93− -- Special case VALUES because we can't use DEFAULT inside a SELECT
94− Optimized (Opaleye.Values symbols rows)
95− | eqSymbols symbols (toList (T.exprs a)) ->
96− Opaleye.ppValues_ (map Opaleye.sqlExpr <$> toList rows)
97− _ -> ppSelect query
98− where
99− (a, primQuery, _) = Opaleye.runSimpleQueryArrStart (toOpaleye query) ()
100−
101− eqSymbols (symbol : symbols) (Opaleye.AttrExpr symbol' : exprs)
102− | eqSymbol symbol symbol' = eqSymbols symbols exprs
103− | otherwise = False
104− eqSymbols [] [] = True
105− eqSymbols _ _ = False
106−
107− eqSymbol
108− (Opaleye.Symbol name (Opaleye.UnsafeTag tag))
109− (Opaleye.Symbol name' (Opaleye.UnsafeTag tag'))
110− = name == name' && tag == tag'
111−
112−
113−ppPrimSelect :: Query a -> (Optimized Doc, a)
114−ppPrimSelect query =
115− (Opaleye.ppSql . primSelect <$> optimize primQuery, a)
116− where
117− (a, primQuery, _) = Opaleye.runSimpleQueryArrStart (toOpaleye query) ()
118−
119−
120−type Optimized :: Type -> Type
121−data Optimized a = Empty | Unit | Optimized a
122− deriving stock (Functor, Foldable, Traversable, Show)
123−
124−
125−optimize :: Opaleye.PrimQuery' a -> Optimized (Opaleye.PrimQuery' Void)
126−optimize query = case Opaleye.removeEmpty (Opaleye.optimize query) of
127− Nothing -> Empty
128− Just Opaleye.Unit -> Unit
129− Just query' -> Optimized query'
130−
131−
132−primSelect :: Opaleye.PrimQuery' Void -> Opaleye.Select
133−primSelect = Opaleye.foldPrimQuery Opaleye.sqlQueryGenerator
134−
135−
136−primSelectWith :: Selects names exprs
137− => names -> exprs -> Opaleye.PrimQuery' Void -> Opaleye.Select
138−primSelectWith names exprs query =
139− Opaleye.SelectFrom $ Opaleye.newSelect
140− { Opaleye.attrs = Opaleye.SelectAttrs attrs
141− , Opaleye.tables = Opaleye.oneTable (primSelect query)
142− }
143− where
144− attrs = makeAttr <$> exprsWithNames names (castTable exprs)
145− where
146− makeAttr (label, expr) =
147− (Opaleye.sqlExpr expr, Just (Opaleye.SqlColumn label))
35+ => Connection -> Query exprs -> IO [a]
36+select c query = case sqlForQuery query of
37+ Nothing -> pure []
38+ Just sql -> Hasql.run session c >>= either throwIO pure
39+ where
40+ session = Hasql.statement () statement
41+ statement = Hasql.Statement bytes params decode prepare
42+ bytes = encodeUtf8 (Text.pack sql)
43+ params = Hasql.noParams
44+ decode = Hasql.rowList (parse @exprs @a)
45+ prepare = False
46+
47+
48+selectWithNames :: forall exprs a names.
49+ ( Selects names exprs
50+ , Serializable exprs a
51+ )
52+ => Connection -> names -> Query exprs -> IO [a]
53+selectWithNames c names query = case sqlForQueryWithNames names query of
54+ Nothing -> pure []
55+ Just sql -> Hasql.run session c >>= either throwIO pure
56+ where
57+ session = Hasql.statement () statement
58+ statement = Hasql.Statement bytes params decode prepare
59+ bytes = encodeUtf8 (Text.pack sql)
60+ params = Hasql.noParams
61+ decode = Hasql.rowList (parse @exprs @a)
62+ prepare = False
src/Rel8/Statement/Set.hsdeleted−33
@@ -1,33 +0,0 @@
1−{-# language MonoLocalBinds #-}
2−{-# language NamedFieldPuns #-}
3−
4−module Rel8.Statement.Set
5− ( ppSet
6− )
7−where
8−
9−-- base
10−import Data.Foldable ( toList )
11−import Prelude ()
12−
13−-- opaleye
14−import qualified Opaleye.Internal.HaskellDB.Sql.Print as Opaleye
15−import qualified Opaleye.Internal.Sql as Opaleye
16−
17−-- pretty
18−import Text.PrettyPrint ( Doc, (<+>), equals, text )
19−
20−-- rel8
21−import Rel8.Schema.Name ( Selects, ppColumn )
22−import Rel8.Schema.Table ( TableSchema(..) )
23−import Rel8.Table.Opaleye ( attributes, exprsWithNames )
24−
25−
26−ppSet :: Selects names exprs
27− => TableSchema names -> (exprs -> exprs) -> Doc
28−ppSet schema@TableSchema {columns} f =
29− text "SET" <+> Opaleye.commaV ppAssign (toList assigns)
30− where
31− assigns = exprsWithNames columns (f (attributes schema))
32− ppAssign (column, expr) =
33− ppColumn column <+> equals <+> Opaleye.ppSqlExpr (Opaleye.sqlExpr expr)
src/Rel8/Statement/Update.hs+56−38
@@ -1,37 +1,39 @@
1−{-# language DuplicateRecordFields #-}
21{-# language GADTs #-}
32{-# language NamedFieldPuns #-}
4−{-# language RecordWildCards #-}
3+{-# language ScopedTypeVariables #-}
54{-# language StandaloneKindSignatures #-}
6−{-# language StrictData #-}
5+{-# language TypeApplications #-}
76
87module Rel8.Statement.Update
98 ( Update(..)
109 , update
11− , ppUpdate
1210 )
1311where
1412
1513-- base
14+import Control.Exception ( throwIO )
1615import Data.Kind ( Type )
1716import Prelude
1817
1918-- hasql
19+import Hasql.Connection ( Connection )
20+import qualified Hasql.Decoders as Hasql
2021import qualified Hasql.Encoders as Hasql
22+import qualified Hasql.Session as Hasql
2123import qualified Hasql.Statement as Hasql
2224
23−-- pretty
24−import Text.PrettyPrint ( Doc, (<+>), ($$), text )
25+-- opaleye
26+import qualified Opaleye.Internal.Manipulation as Opaleye
2527
2628-- rel8
2729import Rel8.Expr ( Expr )
28−import Rel8.Query ( Query )
30+import Rel8.Expr.Opaleye ( toColumn, toPrimExpr )
2931import Rel8.Schema.Name ( Selects )
30−import Rel8.Schema.Table ( TableSchema(..), ppTable )
31−import Rel8.Statement.Returning ( Returning, decodeReturning, ppReturning )
32−import Rel8.Statement.Set ( ppSet )
33−import Rel8.Statement.Using ( ppFrom )
34−import Rel8.Statement.Where ( ppWhere )
32+import Rel8.Schema.Table ( TableSchema )
33+import Rel8.Statement.Returning ( Returning( Projection, NumberOfRowsAffected ) )
34+import Rel8.Table ( fromColumns, toColumns )
35+import Rel8.Table.Opaleye ( castTable, table, unpackspec )
36+import Rel8.Table.Serialize ( Serializable, parse )
3537
3638-- text
3739import qualified Data.Text as Text
@@ -44,12 +46,9 @@ data Update a where
4446 Update :: Selects names exprs =>
4547 { target :: TableSchema names
4648 -- ^ Which table to update.
47− , from :: Query from
48− -- ^ @FROM@ clause — this can be used to join against other tables,
49− -- and its results can be referenced in the @SET@ and @WHERE@ clauses.
50− , set :: from -> exprs -> exprs
49+ , set :: exprs -> exprs
5150 -- ^ How to update each selected row.
52− , updateWhere :: from -> exprs -> Expr Bool
51+ , updateWhere :: exprs -> Expr Bool
5352 -- ^ Which rows to select for update.
5453 , returning :: Returning names a
5554 -- ^ What to return from the @UPDATE@ statement.
@@ -57,27 +56,46 @@ data Update a where
5756 -> Update a
5857
5958
60−ppUpdate :: Update a -> Doc
61−ppUpdate Update {..} = case ppFrom from of
62− Nothing ->
63− text "UPDATE" <+> ppTable target $$
64− ppSet target id $$
65− text "WHERE false"
66− Just (fromDoc, i) ->
67− text "UPDATE" <+> ppTable target $$
68− ppSet target (set i) $$
69− fromDoc $$
70− ppWhere target (updateWhere i) $$
71− ppReturning target returning
59+-- | Run an @UPDATE@ statement.
60+update :: Connection -> Update a -> IO a
61+update c Update {target, set, updateWhere, returning} =
62+ case returning of
63+ NumberOfRowsAffected -> Hasql.run session c >>= either throwIO pure
64+ where
65+ session = Hasql.statement () statement
66+ statement = Hasql.Statement bytes params decode prepare
67+ bytes = encodeUtf8 $ Text.pack sql
68+ params = Hasql.noParams
69+ decode = Hasql.rowsAffected
70+ prepare = False
71+ sql = Opaleye.arrangeUpdateSql target' set' where'
72+ where
73+ target' = table $ toColumns <$> target
74+ set' = toColumns . set . fromColumns
75+ where' = toColumn . toPrimExpr . updateWhere . fromColumns
7276
77+ Projection project -> Hasql.run session c >>= either throwIO pure
78+ where
79+ session = Hasql.statement () statement
80+ statement = Hasql.Statement bytes params decode prepare
81+ bytes = encodeUtf8 $ Text.pack sql
82+ params = Hasql.noParams
83+ decode = decoder project
84+ prepare = False
85+ sql =
86+ Opaleye.arrangeUpdateReturningSql
87+ unpackspec
88+ target'
89+ set'
90+ where'
91+ project'
92+ where
93+ target' = table $ toColumns <$> target
94+ set' = toColumns . set . fromColumns
95+ where' = toColumn . toPrimExpr . updateWhere . fromColumns
96+ project' = castTable . toColumns . project . fromColumns
7397
74−-- | Run an @UPDATE@ statement.
75−update :: Update a -> Hasql.Statement () a
76−update u@Update {returning} = Hasql.Statement bytes params decode prepare
7798 where
78− bytes = encodeUtf8 $ Text.pack sql
79− params = Hasql.noParams
80− decode = decodeReturning returning
81− prepare = False
82− sql = show doc
83− doc = ppUpdate u
99+ decoder :: forall exprs projection a. Serializable projection a
100+ => (exprs -> projection) -> Hasql.Result [a]
101+ decoder _ = Hasql.rowList (parse @projection @a)
src/Rel8/Statement/Using.hsdeleted−36
@@ -1,36 +0,0 @@
1−module Rel8.Statement.Using
2− ( ppFrom
3− , ppUsing
4− )
5−where
6−
7−-- base
8−import Prelude
9−
10−-- pretty
11−import Text.PrettyPrint ( Doc, (<+>), parens, text )
12−
13−-- rel8
14−import Rel8.Query ( Query )
15−import Rel8.Schema.Table ( TableSchema(..), ppTable )
16−import Rel8.Statement.Select ( Optimized(..), ppPrimSelect )
17−
18−
19−ppFrom :: Query a -> Maybe (Doc, a)
20−ppFrom = ppJoin "FROM"
21−
22−
23−ppUsing :: Query a -> Maybe (Doc, a)
24−ppUsing = ppJoin "USING"
25−
26−
27−ppJoin :: String -> Query a -> Maybe (Doc, a)
28−ppJoin clause join = do
29− doc <- case ofrom of
30− Empty -> Nothing
31− Unit -> Just mempty
32− Optimized doc -> Just $ text clause <+> parens doc <+> ppTable alias
33− pure (doc, a)
34− where
35− alias = TableSchema {name = "T1", schema = Nothing, columns = ()}
36− (ofrom, a) = ppPrimSelect join
src/Rel8/Statement/View.hs+28−43
@@ -3,76 +3,61 @@
33
44module Rel8.Statement.View
55 ( createView
6− , createOrReplaceView
76 )
87where
98
109-- base
10+import Control.Exception ( throwIO )
11+import Control.Monad ( (>=>) )
12+import Data.Foldable ( fold )
13+import Data.Maybe ( fromMaybe )
1114import Prelude
1215
1316-- hasql
17+import Hasql.Connection ( Connection )
1418import qualified Hasql.Decoders as Hasql
1519import qualified Hasql.Encoders as Hasql
20+import qualified Hasql.Session as Hasql
1621import qualified Hasql.Statement as Hasql
1722
1823-- rel8
1924import Rel8.Query ( Query )
25+import Rel8.Query.SQL ( sqlForQueryWithNames )
2026import Rel8.Schema.Name ( Selects )
21−import Rel8.Schema.Table ( TableSchema )
22−import Rel8.Statement.Insert ( ppInto )
23−import Rel8.Statement.Select ( ppSelect )
24−
25−-- pretty
26−import Text.PrettyPrint ( Doc, (<+>), ($$), text )
27+import Rel8.Schema.Table ( TableSchema( TableSchema ) )
28+import Rel8.Table.Alternative ( emptyTable )
2729
2830-- text
2931import qualified Data.Text as Text
3032import Data.Text.Encoding ( encodeUtf8 )
3133
3234
33−data CreateView = Create | CreateOrReplace
34−
35−
3635-- | Given a 'TableSchema' and 'Query', @createView@ runs a @CREATE VIEW@
3736-- statement that will save the given query as a view. This can be useful if
3837-- you want to share Rel8 queries with other applications.
3938createView :: Selects names exprs
40− => TableSchema names -> Query exprs -> Hasql.Statement () ()
41−createView =
42− createViewGeneric Create
43−
44−
45−-- | Given a 'TableSchema' and 'Query', @createOrReplaceView@ runs a
46−-- @CREATE OR REPLACE VIEW@ statement that will save the given query
47−-- as a view, replacing the current view definition if it exists and
48−-- adheres to the restrictions in place for replacing a view in
49−-- PostgreSQL.
50−createOrReplaceView :: Selects names exprs
51− => TableSchema names -> Query exprs -> Hasql.Statement () ()
52−createOrReplaceView =
53− createViewGeneric CreateOrReplace
54−
55−
56−createViewGeneric :: Selects names exprs
57− => CreateView -> TableSchema names -> Query exprs -> Hasql.Statement () ()
58−createViewGeneric replace schema query =
59− Hasql.Statement bytes params decode prepare
39+ => TableSchema names -> Query exprs -> Connection -> IO ()
40+createView (TableSchema name mschema names) query =
41+ Hasql.run session >=> either throwIO pure
6042 where
43+ session = Hasql.statement () statement
44+ statement = Hasql.Statement bytes params decode prepare
6145 bytes = encodeUtf8 (Text.pack sql)
6246 params = Hasql.noParams
6347 decode = Hasql.noResult
6448 prepare = False
65− sql = show doc
66− doc = ppCreateView schema query replace
67−
68−
69−ppCreateView :: Selects names exprs
70− => TableSchema names -> Query exprs -> CreateView -> Doc
71−ppCreateView schema query replace =
72− createOrReplace replace <+>
73− ppInto schema $$
74− text "AS" <+>
75− ppSelect query
49+ sql = "CREATE VIEW " <> title <> " AS " <> select
50+ where
51+ title = case mschema of
52+ Nothing -> quote name
53+ Just schema -> quote schema <> "." <> quote name
54+ select = fromMaybe fallback $ sqlForQueryWithNames names query
55+ where
56+ fallback = fold $ sqlForQueryWithNames names emptyTable
57+
58+
59+quote :: String -> String
60+quote string = "\"" <> concatMap go string <> "\""
7661 where
77− createOrReplace Create = text "CREATE VIEW"
78− createOrReplace CreateOrReplace = text "CREATE OR REPLACE VIEW"
62+ go '"' = "\"\""
63+ go c = [c]
src/Rel8/Statement/Where.hsdeleted−31
@@ -1,31 +0,0 @@
1−{-# language MonoLocalBinds #-}
2−
3−module Rel8.Statement.Where
4− ( ppWhere
5− )
6−where
7−
8−-- base
9−import Prelude
10−
11−-- opaleye
12−import qualified Opaleye.Internal.HaskellDB.Sql.Print as Opaleye
13−import qualified Opaleye.Internal.Sql as Opaleye
14−
15−-- pretty
16−import Text.PrettyPrint ( Doc, (<+>), text )
17−
18−-- rel8
19−import Rel8.Expr ( Expr )
20−import Rel8.Expr.Opaleye ( toPrimExpr )
21−import Rel8.Schema.Name ( Selects )
22−import Rel8.Schema.Table ( TableSchema )
23−import Rel8.Table.Opaleye ( attributes )
24−
25−
26−ppWhere :: Selects names exprs
27− => TableSchema names -> (exprs -> Expr Bool) -> Doc
28−ppWhere schema where_ = text "WHERE" <+> ppExpr condition
29− where
30− ppExpr = Opaleye.ppSqlExpr . Opaleye.sqlExpr . toPrimExpr
31− condition = where_ (attributes schema)
src/Rel8/Table.hs+252−88
@@ -1,47 +1,77 @@
11{-# language AllowAmbiguousTypes #-}
22{-# language DataKinds #-}
33{-# language DefaultSignatures #-}
4+{-# language DisambiguateRecordFields #-}
45{-# language FlexibleContexts #-}
56{-# language FlexibleInstances #-}
67{-# language FunctionalDependencies #-}
8+{-# language LambdaCase #-}
9+{-# language NamedFieldPuns #-}
710{-# language ScopedTypeVariables #-}
811{-# language StandaloneKindSignatures #-}
912{-# language TypeApplications #-}
1013{-# language TypeFamilies #-}
14+{-# language TypeOperators #-}
1115{-# language UndecidableInstances #-}
1216
1317module Rel8.Table
14− ( Table
15− ( Columns, Context, fromColumns, toColumns
16− , FromExprs, fromResult, toResult
17− , Transpose
18− )
18+ ( Table (Columns, Context, Unreify, toColumns, fromColumns, reify, unreify)
1919 , Congruent
20− , TTable, TColumns, TContext, TFromExprs, TTranspose
21− , TSerialize
20+ , TTable, TColumns, TContext, TUnreify
2221 )
2322where
2423
2524-- base
25+import Data.Functor ( ($>) )
2626import Data.Functor.Identity ( Identity( Identity ) )
2727import Data.Kind ( Constraint, Type )
28+import Data.List.NonEmpty ( NonEmpty )
29+import Data.Proxy ( Proxy( Proxy ) )
30+import Data.Type.Equality ( (:~:)( Refl ) )
2831import GHC.Generics ( Generic, Rep, from, to )
2932import Prelude hiding ( null )
3033
3134-- rel8
3235import Rel8.FCF ( Eval, Exp )
33−import Rel8.Generic.Map ( Map )
34−import Rel8.Generic.Table.Record
35− ( GTable, GColumns, GContext, gfromColumns, gtoColumns
36− , GSerialize, gfromResult, gtoResult
36+import Rel8.Kind.Algebra ( KnownAlgebra )
37+import Rel8.Generic.Map ( GMap, GMappable, gmap, gunmap )
38+import Rel8.Generic.Table
39+ ( GGTable, GGColumns, GGContext, ggfromColumns, ggtoColumns
40+ , GAlgebra
3741 )
3842import Rel8.Generic.Record ( Record(..) )
43+import Rel8.Generic.Reify ( ARep )
44+import Rel8.Schema.Context.Label ( Labelable, labeler, unlabeler )
3945import Rel8.Schema.HTable ( HTable )
40−import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) )
46+import Rel8.Schema.HTable.Either ( HEitherTable(..) )
47+import Rel8.Schema.HTable.Identity ( HIdentity(..), HType )
48+import Rel8.Schema.HTable.Label ( hlabel, hunlabel )
49+import Rel8.Schema.HTable.List ( HListTable )
50+import Rel8.Schema.HTable.Maybe ( HMaybeTable(..) )
51+import Rel8.Schema.HTable.NonEmpty ( HNonEmptyTable )
52+import Rel8.Schema.HTable.Nullify ( hnulls, hnullify, hunnullify )
53+import Rel8.Schema.HTable.These ( HTheseTable(..) )
54+import Rel8.Schema.HTable.Vectorize ( hvectorize, hunvectorize )
4155import qualified Rel8.Schema.Kind as K
4256import Rel8.Schema.Null ( Sql )
43−import Rel8.Schema.Result ( Result )
57+import Rel8.Schema.Reify
58+ ( Reify, Col( Reify ), hreify, hunreify
59+ , UnwrapReify
60+ , notReify
61+ )
62+import Rel8.Schema.Result
63+ ( Col( R ), Result
64+ , relabel
65+ , null, nullifier, unnullifier
66+ , vectorizer, unvectorizer
67+ )
68+import Rel8.Schema.Spec ( KnownSpec )
4469import Rel8.Type ( DBType )
70+import Rel8.Type.Tag ( EitherTag( IsLeft, IsRight ), MaybeTag( IsJust ) )
71+
72+-- these
73+import Data.These ( These( This, That, These ) )
74+import Data.These.Combinators ( justHere, justThere )
4575
4676
4777-- | @Table@s are one of the foundational elements of Rel8, and describe data
@@ -56,102 +86,90 @@ import Rel8.Type ( DBType )
5686-- yourself, as anything that's an instance of 'Rel8.Rel8able' is always a
5787-- 'Table'.
5888type Table :: K.Context -> Type -> Constraint
59−class
60− ( HTable (Columns a)
61− , context ~ Context a
62− , a ~ Transpose context a
63− )
64− => Table context a | a -> context
65− where
89+class (HTable (Columns a), context ~ Context a) => Table context a | a -> context where
6690 -- | The 'HTable' functor that describes the schema of this table.
6791 type Columns a :: K.HTable
6892
6993 -- | The common context that all columns use as an interpretation.
7094 type Context a :: K.Context
7195
72− -- | The @FromExprs@ type family maps a type in the @Expr@ context to the
73− -- corresponding Haskell type.
74− type FromExprs a :: Type
75−
76− type Transpose (context' :: K.Context) a :: Type
96+ type Unreify a :: Type
7797
98+ toColumns :: a -> Columns a (Col context)
99+ fromColumns :: Columns a (Col context) -> a
78100
79− toColumns :: a -> Columns a context
80− fromColumns :: Columns a context -> a
101+ reify :: context :~: Reify ctx -> Unreify a -> a
102+ unreify :: context :~: Reify ctx -> a -> Unreify a
81103
82− fromResult :: Columns a Result -> FromExprs a
83− toResult :: FromExprs a -> Columns a Result
84−
85− type Columns a = GColumns TColumns (Rep (Record a))
86− type Context a = GContext TContext (Rep (Record a))
87− type FromExprs a = Map TFromExprs a
88− type Transpose context a = Map (TTranspose context) a
104+ type Columns a = Eval (GGColumns (GAlgebra (Rep (Record a))) TColumns (Rep (Record a)))
105+ type Context a = Eval (GGContext (GAlgebra (Rep (Record a))) TContext (Rep (Record a)))
106+ type Unreify a = DefaultUnreify a
89107
90108 default toColumns ::
91109 ( Generic (Record a)
92− , GTable (TTable context) TColumns (Rep (Record a))
93− , Columns a ~ GColumns TColumns (Rep (Record a))
110+ , KnownAlgebra (GAlgebra (Rep (Record a)))
111+ , Eval (GGTable (GAlgebra (Rep (Record a))) (TTable context) TColumns (Col context) (Rep (Record a)))
112+ , Columns a ~ Eval (GGColumns (GAlgebra (Rep (Record a))) TColumns (Rep (Record a)))
113+ , Context a ~ Eval (GGContext (GAlgebra (Rep (Record a))) TContext (Rep (Record a)))
94114 )
95− => a -> Columns a context
115+ => a -> Columns a (Col context)
96116 toColumns =
97− gtoColumns @(TTable context) @TColumns toColumns .
117+ ggtoColumns
118+ @(GAlgebra (Rep (Record a)))
119+ @(TTable context)
120+ @TColumns
121+ id
122+ id
123+ toColumns .
98124 from .
99125 Record
100126
101127 default fromColumns ::
102128 ( Generic (Record a)
103− , GTable (TTable context) TColumns (Rep (Record a))
104− , Columns a ~ GColumns TColumns (Rep (Record a))
129+ , KnownAlgebra (GAlgebra (Rep (Record a)))
130+ , Eval (GGTable (GAlgebra (Rep (Record a))) (TTable context) TColumns (Col context) (Rep (Record a)))
131+ , Columns a ~ Eval (GGColumns (GAlgebra (Rep (Record a))) TColumns (Rep (Record a)))
132+ , Context a ~ Eval (GGContext (GAlgebra (Rep (Record a))) TContext (Rep (Record a)))
105133 )
106− => Columns a context -> a
134+ => Columns a (Col context) -> a
107135 fromColumns =
108136 unrecord .
109137 to .
110− gfromColumns @(TTable context) @TColumns fromColumns
138+ ggfromColumns
139+ @(GAlgebra (Rep (Record a)))
140+ @(TTable context)
141+ @TColumns
142+ id
143+ id
144+ fromColumns
111145
112− default toResult ::
113− ( Generic (Record (FromExprs a))
114− , GSerialize TSerialize TColumns (Rep (Record a)) (Rep (Record (FromExprs a)))
115− , Columns a ~ GColumns TColumns (Rep (Record a))
146+ default reify ::
147+ ( Generic (Record a)
148+ , Generic (Record (Unreify a))
149+ , GMappable (TTable context) (Rep (Record a))
150+ , Rep (Record (Unreify a)) ~ GMap TUnreify (Rep (Record a))
116151 )
117− => FromExprs a -> Columns a Result
118− toResult =
119− gtoResult
120− @TSerialize
121− @TColumns
122− @(Rep (Record a))
123− @(Rep (Record (FromExprs a)))
124− (\(_ :: proxy x) -> toResult @(Context x) @x) .
152+ => context :~: Reify ctx -> Unreify a -> a
153+ reify Refl =
154+ unrecord .
155+ to .
156+ gunmap @(TTable context) (Proxy @TUnreify) (reify Refl) .
125157 from .
126158 Record
127159
128− default fromResult ::
129− ( Generic (Record (FromExprs a))
130− , GSerialize TSerialize TColumns (Rep (Record a)) (Rep (Record (FromExprs a)))
131− , Columns a ~ GColumns TColumns (Rep (Record a))
160+ default unreify ::
161+ ( Generic (Record a)
162+ , Generic (Record (Unreify a))
163+ , GMappable (TTable context) (Rep (Record a))
164+ , Rep (Record (Unreify a)) ~ GMap TUnreify (Rep (Record a))
132165 )
133− => Columns a Result -> FromExprs a
134− fromResult =
166+ => context :~: Reify ctx -> a -> Unreify a
167+ unreify Refl =
135168 unrecord .
136169 to .
137− gfromResult
138− @TSerialize
139− @TColumns
140− @(Rep (Record a))
141− @(Rep (Record (FromExprs a)))
142− (\(_ :: proxy x) -> fromResult @(Context x) @x)
143−
144−
145−instance Sql DBType a => Table Result (Identity a) where
146− type Columns (Identity a) = HIdentity a
147− type Context (Identity a) = Result
148− type FromExprs (Identity a) = a
149− type Transpose to (Identity a) = to a
150−
151− toColumns = HIdentity
152− fromColumns (HIdentity a) = a
153− toResult a = HIdentity (Identity a)
154− fromResult (HIdentity (Identity a)) = a
170+ gmap @(TTable context) (Proxy @TUnreify) (unreify Refl) .
171+ from .
172+ Record
155173
156174
157175data TTable :: K.Context -> Type -> Exp Constraint
@@ -166,32 +184,175 @@ data TContext :: Type -> Exp K.Context
166184type instance Eval (TContext a) = Context a
167185
168186
169−data TFromExprs :: Type -> Exp Type
170−type instance Eval (TFromExprs a) = FromExprs a
187+data TUnreify :: Type -> Exp Type
188+type instance Eval (TUnreify a) = Unreify a
171189
172190
173−data TTranspose :: K.Context -> Type -> Exp Type
174−type instance Eval (TTranspose context a) = Transpose context a
191+type DefaultUnreify :: Type -> Type
192+type family DefaultUnreify a where
193+ DefaultUnreify (t a b c d e f g) =
194+ t (Unreify a) (Unreify b) (Unreify c) (Unreify d) (Unreify e) (Unreify f) (Unreify g)
195+ DefaultUnreify (t a b c d e f) =
196+ t (Unreify a) (Unreify b) (Unreify c) (Unreify d) (Unreify e) (Unreify f)
197+ DefaultUnreify (t a b c d e) =
198+ t (Unreify a) (Unreify b) (Unreify c) (Unreify d) (Unreify e)
199+ DefaultUnreify (t a b c d) =
200+ t (Unreify a) (Unreify b) (Unreify c) (Unreify d)
201+ DefaultUnreify (t a b c) = t (Unreify a) (Unreify b) (Unreify c)
202+ DefaultUnreify (t a b) = t (Unreify a) (Unreify b)
203+ DefaultUnreify (t a) = t (Unreify a)
204+ DefaultUnreify a = ARep (GMap TUnreify (Rep a))
175205
176206
177−data TSerialize :: Type -> Type -> Exp Constraint
178−type instance Eval (TSerialize expr a) =
179− ( Table (Context expr) expr
180− , a ~ FromExprs expr
181− )
207+-- | Any 'HTable' is also a 'Table'.
208+instance HTable t => Table context (t (Col context)) where
209+ type Columns (t (Col context)) = t
210+ type Context (t (Col context)) = context
211+ type Unreify (t (Col context)) = t (Col (UnwrapReify context))
212+
213+ toColumns = id
214+ fromColumns = id
215+
216+ reify Refl = hreify
217+ unreify Refl = hunreify
182218
183219
184−instance (Table context a, Table context b) => Table context (a, b)
220+-- | Any context is trivially a table.
221+instance KnownSpec spec => Table context (Col context spec) where
222+ type Columns (Col context spec) = HIdentity spec
223+ type Context (Col context spec) = context
224+ type Unreify (Col context spec) = Col (UnwrapReify context) spec
225+
226+ toColumns = HIdentity
227+ fromColumns = unHIdentity
228+
229+ reify Refl = Reify
230+ unreify Refl (Reify a) = a
231+
232+
233+instance Sql DBType a => Table Result (Identity a) where
234+ type Columns (Identity a) = HType a
235+ type Context (Identity a) = Result
236+
237+ toColumns (Identity a) = HType (R a)
238+ fromColumns (HType (R a)) = Identity a
239+
240+ reify = notReify
241+ unreify = notReify
242+
243+
244+instance (Table Result a, Table Result b) => Table Result (Either a b) where
245+ type Columns (Either a b) = HEitherTable (Columns a) (Columns b)
246+ type Context (Either a b) = Result
247+
248+ toColumns = \case
249+ Left table -> HEitherTable
250+ { htag = HIdentity (R IsLeft)
251+ , hleft = hlabel labeler (hnullify nullifier (toColumns table))
252+ , hright = hlabel labeler (hnulls (const null))
253+ }
254+ Right table -> HEitherTable
255+ { htag = HIdentity (R IsRight)
256+ , hleft = hlabel labeler (hnulls (const null))
257+ , hright = hlabel labeler (hnullify nullifier (toColumns table))
258+ }
259+
260+ fromColumns HEitherTable {htag, hleft, hright} = case htag of
261+ HIdentity (R tag) -> case tag of
262+ IsLeft -> maybe err (Left . fromColumns) $ hunnullify unnullifier (hunlabel unlabeler hleft)
263+ IsRight -> maybe err (Right . fromColumns) $ hunnullify unnullifier (hunlabel unlabeler hright)
264+ where
265+ err = error "Either.fromColumns: mismatch between tag and data"
266+
267+
268+instance Table Result a => Table Result [a] where
269+ type Columns [a] = HListTable (Columns a)
270+ type Context [a] = Result
271+
272+ toColumns = hvectorize vectorizer . fmap toColumns
273+ fromColumns = fmap fromColumns . hunvectorize unvectorizer
274+
275+
276+instance Table Result a => Table Result (Maybe a) where
277+ type Columns (Maybe a) = HMaybeTable (Columns a)
278+ type Context (Maybe a) = Result
279+
280+ toColumns = \case
281+ Nothing -> HMaybeTable
282+ { htag = HIdentity (R Nothing)
283+ , hjust = hlabel labeler (hnulls (const null))
284+ }
285+ Just table -> HMaybeTable
286+ { htag = HIdentity (R (Just IsJust))
287+ , hjust = hlabel labeler (hnullify nullifier (toColumns table))
288+ }
289+
290+ fromColumns HMaybeTable {htag, hjust} = case htag of
291+ HIdentity (R tag) -> tag $>
292+ case hunnullify unnullifier (hunlabel unlabeler hjust) of
293+ Nothing -> error "Maybe.fromColumns: mismatch between tag and data"
294+ Just just -> fromColumns just
295+
296+
297+instance Table Result a => Table Result (NonEmpty a) where
298+ type Columns (NonEmpty a) = HNonEmptyTable (Columns a)
299+ type Context (NonEmpty a) = Result
300+
301+ toColumns = hvectorize vectorizer . fmap toColumns
302+ fromColumns = fmap fromColumns . hunvectorize unvectorizer
303+
304+
305+instance (Table Result a, Table Result b) => Table Result (These a b) where
306+ type Columns (These a b) = HTheseTable (Columns a) (Columns b)
307+ type Context (These a b) = Result
308+
309+ toColumns tables = HTheseTable
310+ { hhereTag = relabel hhereTag
311+ , hhere = hlabel labeler (hunlabel unlabeler (toColumns hhere))
312+ , hthereTag = relabel hthereTag
313+ , hthere = hlabel labeler (hunlabel unlabeler (toColumns hthere))
314+ }
315+ where
316+ HMaybeTable
317+ { htag = hhereTag
318+ , hjust = hhere
319+ } = toColumns (justHere tables)
320+ HMaybeTable
321+ { htag = hthereTag
322+ , hjust = hthere
323+ } = toColumns (justThere tables)
324+
325+ fromColumns HTheseTable {hhereTag, hhere, hthereTag, hthere} =
326+ case (fromColumns mhere, fromColumns mthere) of
327+ (Just a, Nothing) -> This (fromColumns a)
328+ (Nothing, Just b) -> That (fromColumns b)
329+ (Just a, Just b) -> These (fromColumns a) (fromColumns b)
330+ _ -> error "These.fromColumns: mismatch between tags and data"
331+ where
332+ mhere = HMaybeTable
333+ { htag = relabel hhereTag
334+ , hjust = hlabel labeler (hunlabel unlabeler hhere)
335+ }
336+ mthere = HMaybeTable
337+ { htag = relabel hthereTag
338+ , hjust = hlabel labeler (hunlabel unlabeler hthere)
339+ }
340+
341+
342+instance (Table context a, Table context b, Labelable context)
343+ => Table context (a, b)
185344
186345
187346instance
188347 ( Table context a, Table context b, Table context c
348+ , Labelable context
189349 )
190350 => Table context (a, b, c)
191351
192352
193353instance
194354 ( Table context a, Table context b, Table context c, Table context d
355+ , Labelable context
195356 )
196357 => Table context (a, b, c, d)
197358
@@ -199,6 +360,7 @@ instance
199360instance
200361 ( Table context a, Table context b, Table context c, Table context d
201362 , Table context e
363+ , Labelable context
202364 )
203365 => Table context (a, b, c, d, e)
204366
@@ -206,6 +368,7 @@ instance
206368instance
207369 ( Table context a, Table context b, Table context c, Table context d
208370 , Table context e, Table context f
371+ , Labelable context
209372 )
210373 => Table context (a, b, c, d, e, f)
211374
@@ -213,6 +376,7 @@ instance
213376instance
214377 ( Table context a, Table context b, Table context c, Table context d
215378 , Table context e, Table context f, Table context g
379+ , Labelable context
216380 )
217381 => Table context (a, b, c, d, e, f, g)
218382
src/Rel8/Table/ADT.hs+65−33
@@ -7,13 +7,13 @@
77{-# language ScopedTypeVariables #-}
88{-# language StandaloneKindSignatures #-}
99{-# language TypeApplications #-}
10−{-# language TypeFamilyDependencies #-}
10+{-# language TypeFamilies #-}
1111{-# language UndecidableInstances #-}
1212{-# language UndecidableSuperClasses #-}
1313
1414module Rel8.Table.ADT
1515 ( ADT( ADT )
16− , ADTable
16+ , ADTable, fromADT, toADT
1717 , BuildableADT
1818 , BuildADT, buildADT
1919 , ConstructableADT
@@ -27,7 +27,9 @@ where
2727
2828-- base
2929import Data.Kind ( Constraint, Type )
30−import GHC.Generics ( Generic, from, to )
30+import Data.Proxy ( Proxy( Proxy ) )
31+import Data.Type.Equality ( (:~:)( Refl ) )
32+import GHC.Generics ( Generic, Rep, from, to )
3133import GHC.TypeLits ( Symbol )
3234import Prelude
3335
@@ -44,65 +46,95 @@ import Rel8.Generic.Construction
4446 , GGName, ggname
4547 , GGAggregate, ggaggregate
4648 )
47−import Rel8.Generic.Record ( Record( Record ), unrecord )
49+import Rel8.Generic.Map ( GMappable, GMap, gmap, gunmap )
50+import Rel8.Generic.Record ( GRecordable, GRecord, grecord, gunrecord )
4851import Rel8.Generic.Rel8able
4952 ( Rel8able
5053 , GRep, GColumns, gfromColumns, gtoColumns
51− , GFromExprs, gfromResult, gtoResult
52− , TSerialize, deserialize, serialize
54+ , greify, gunreify
5355 )
5456import qualified Rel8.Generic.Table.ADT as G
5557import qualified Rel8.Kind.Algebra as K
58+import Rel8.Schema.Context ( Col )
5659import Rel8.Schema.HTable ( HTable )
5760import qualified Rel8.Schema.Kind as K
5861import Rel8.Schema.Name ( Name )
62+import Rel8.Schema.Reify ( Col( Reify ), Reify, hreify, hunreify )
5963import Rel8.Schema.Result ( Result )
60−import Rel8.Table ( Table, TColumns )
64+import Rel8.Table
65+ ( Table
66+ , fromColumns, toColumns, reify, unreify
67+ , TTable, TColumns, TUnreify
68+ )
6169
6270
6371type ADT :: K.Rel8able -> K.Rel8able
64−newtype ADT t context = ADT (GColumnsADT t context)
72+newtype ADT t context = ADT (GColumnsADT t (Col context))
6573
6674
6775instance ADTable t => Rel8able (ADT t) where
6876 type GColumns (ADT t) = GColumnsADT t
69− type GFromExprs (ADT t) = t Result
7077
71− gfromColumns _ = ADT
72− gtoColumns _ (ADT a) = a
78+ gfromColumns = ADT
79+ gtoColumns (ADT a) = a
80+
81+ greify (ADT a) = ADT (hreify a)
82+ gunreify (ADT a) = ADT (hunreify a)
7383
74− gfromResult =
75− unrecord .
76− to .
77− G.gfromResultADT
78− @TSerialize
79− @TColumns
80− @(Eval (ADTRep t Expr))
81− @(Eval (ADTRep t Result))
82− (\(_ :: proxy x) -> deserialize @_ @x)
8384
84− gtoResult =
85− G.gtoResultADT
86− @TSerialize
85+instance (ADTable t, context ~ Result) => Generic (ADT t context) where
86+ type Rep (ADT t context) = Rep (t context)
87+
88+ from =
89+ gmap @(TTable (Reify Result)) (Proxy @TUnreify) (unreify Refl) .
90+ gunrecord @(Rep (t (Reify Result))) .
91+ G.gfromColumnsADT
92+ @(TTable (Reify Result))
93+ @TColumns
94+ (\(Reify a) -> a)
95+ Reify
96+ fromColumns .
97+ hreify .
98+ (\(ADT a) -> a)
99+
100+ to =
101+ ADT .
102+ hunreify .
103+ G.gtoColumnsADT
104+ @(TTable (Reify Result))
87105 @TColumns
88− @(Eval (ADTRep t Expr))
89− @(Eval (ADTRep t Result))
90− (\(_ :: proxy x) -> serialize @_ @x) .
91− from .
92− Record
106+ (\(Reify a) -> a)
107+ Reify
108+ toColumns .
109+ grecord @(Rep (t (Reify Result))) .
110+ gunmap @(TTable (Reify Result)) (Proxy @TUnreify) (reify Refl)
111+
112+
113+fromADT :: ADTable t => ADT t Result -> t Result
114+fromADT = to . from
115+
116+
117+toADT :: ADTable t => t Result -> ADT t Result
118+toADT = to . from
93119
94120
95121type ADTable :: K.Rel8able -> Constraint
96122class
97− ( Generic (Record (t Result))
123+ ( Generic (t Result)
98124 , HTable (GColumnsADT t)
99− , G.GSerializeADT TSerialize TColumns (Eval (ADTRep t Expr)) (Eval (ADTRep t Result))
125+ , G.GTableADT (TTable (Reify Result)) TColumns (Col (Reify Result)) (GRecord (Rep (t (Reify Result))))
126+ , GRecordable (Rep (t (Reify Result)))
127+ , GMappable (TTable (Reify Result)) (Rep (t (Reify Result)))
128+ , GMap TUnreify (Rep (t (Reify Result))) ~ Rep (t Result)
100129 )
101130 => ADTable t
102131instance
103− ( Generic (Record (t Result))
132+ ( Generic (t Result)
104133 , HTable (GColumnsADT t)
105− , G.GSerializeADT TSerialize TColumns (Eval (ADTRep t Expr)) (Eval (ADTRep t Result))
134+ , G.GTableADT (TTable (Reify Result)) TColumns (Col (Reify Result)) (GRecord (Rep (t (Reify Result))))
135+ , GRecordable (Rep (t (Reify Result)))
136+ , GMappable (TTable (Reify Result)) (Rep (t (Reify Result)))
137+ , GMap TUnreify (Rep (t (Reify Result))) ~ Rep (t Result)
106138 )
107139 => ADTable t
108140
@@ -170,4 +202,4 @@ type instance Eval (ADTRep t context) = GRep t context
170202
171203
172204type GColumnsADT :: K.Rel8able -> K.HTable
173−type GColumnsADT t = G.GColumnsADT TColumns (GRep t Expr)
205+type GColumnsADT t = G.GColumnsADT TColumns (GRep t (Reify Result))
src/Rel8/Table/Aggregate.hs+13−24
@@ -16,8 +16,8 @@ import Data.Functor.Identity ( Identity( Identity ) )
1616import Prelude
1717
1818-- rel8
19−import Rel8.Aggregate ( Aggregate, Aggregates )
20−import Rel8.Expr ( Expr )
19+import Rel8.Aggregate ( Aggregate, Aggregates, Col( A ) )
20+import Rel8.Expr ( Expr, Col( E ) )
2121import Rel8.Expr.Aggregate
2222 ( groupByExpr
2323 , slistAggExpr
@@ -26,8 +26,8 @@ import Rel8.Expr.Aggregate
2626import Rel8.Schema.Dict ( Dict( Dict ) )
2727import Rel8.Schema.HTable ( HTable, hfield, htabulate )
2828import Rel8.Schema.HTable.Vectorize ( hvectorize )
29−import Rel8.Schema.Null ( Sql )
30−import Rel8.Schema.Spec ( Spec( Spec, info ) )
29+import Rel8.Schema.Spec ( SSpec( SSpec, info ) )
30+import Rel8.Schema.Spec.ConstrainDBType ( ConstrainDBType )
3131import Rel8.Table ( toColumns, fromColumns )
3232import Rel8.Table.Eq ( EqTable, eqTable )
3333import Rel8.Table.List ( ListTable )
@@ -37,28 +37,17 @@ import Rel8.Type.Eq ( DBEq )
3737
3838-- | Group equal tables together. This works by aggregating each column in the
3939-- given table with 'groupByExpr'.
40−--
41−-- For example, if we have a table of items, we could group the items by the
42−-- order they belong to:
43−--
44−-- @
45−-- itemsByOrder :: Query (OrderId Expr, ListTable Expr (Item Expr))
46−-- itemsByOrder = aggregate $ do
47−-- item <- each itemSchema
48−-- let orderId = groupBy (itemOrderId item)
49−-- let orderItems = listAgg item
50−-- pure (orderId, orderItems)
51−-- @
5240groupBy :: forall exprs aggregates. (EqTable exprs, Aggregates aggregates exprs)
5341 => exprs -> aggregates
5442groupBy = fromColumns . hgroupBy (eqTable @exprs) . toColumns
5543
5644
57−hgroupBy :: HTable t => t (Dict (Sql DBEq)) -> t Expr -> t Aggregate
58−hgroupBy eqs exprs = htabulate $ \field ->
45+hgroupBy :: HTable t
46+ => t (Dict (ConstrainDBType DBEq)) -> t (Col Expr) -> t (Col Aggregate)
47+hgroupBy eqs exprs = fromColumns $ htabulate $ \field ->
5948 case hfield eqs field of
6049 Dict -> case hfield exprs field of
61− expr -> groupByExpr expr
50+ E expr -> A $ groupByExpr expr
6251
6352
6453-- | Aggregate rows into a single row containing an array of all aggregated
@@ -71,22 +60,22 @@ hgroupBy eqs exprs = htabulate $ \field ->
7160-- items:
7261--
7362-- @
74−-- ordersWithItems :: Query (Order Expr, ListTable Expr (Item Expr))
63+-- ordersWithItems :: Query (Order Expr, ListTable (Item Expr))
7564-- ordersWithItems = do
7665-- order <- each orderSchema
7766-- items <- aggregate $ listAgg <$> itemsFromOrder order
7867-- return (order, items)
7968-- @
80−listAgg :: Aggregates aggregates exprs => exprs -> ListTable Aggregate aggregates
69+listAgg :: Aggregates aggregates exprs => exprs -> ListTable aggregates
8170listAgg (toColumns -> exprs) = fromColumns $
8271 hvectorize
83− (\Spec {info} (Identity a) -> slistAggExpr info a)
72+ (\SSpec {info} (Identity (E a)) -> A $ slistAggExpr info a)
8473 (pure exprs)
8574
8675
8776-- | Like 'listAgg', but the result is guaranteed to be a non-empty list.
88−nonEmptyAgg :: Aggregates aggregates exprs => exprs -> NonEmptyTable Aggregate aggregates
77+nonEmptyAgg :: Aggregates aggregates exprs => exprs -> NonEmptyTable aggregates
8978nonEmptyAgg (toColumns -> exprs) = fromColumns $
9079 hvectorize
91− (\Spec {info} (Identity a) -> snonEmptyAggExpr info a)
80+ (\SSpec {info} (Identity (E a)) -> A $ snonEmptyAggExpr info a)
9281 (pure exprs)
src/Rel8/Table/Bool.hs+6−5
@@ -13,7 +13,7 @@ where
1313import Prelude
1414
1515-- rel8
16−import Rel8.Expr ( Expr )
16+import Rel8.Expr ( Expr, Col( E, unE ) )
1717import Rel8.Expr.Bool ( boolExpr, caseExpr )
1818import Rel8.Expr.Null ( isNull, unsafeUnnullify )
1919import Rel8.Schema.HTable ( htabulate, hfield )
@@ -28,7 +28,8 @@ bool :: Table Expr a => a -> a -> Expr Bool -> a
2828bool (toColumns -> false) (toColumns -> true) condition =
2929 fromColumns $ htabulate $ \field ->
3030 case (hfield false field, hfield true field) of
31− (falseExpr, trueExpr) -> boolExpr falseExpr trueExpr condition
31+ (E falseExpr, E trueExpr) ->
32+ E (boolExpr falseExpr trueExpr condition)
3233{-# INLINABLE bool #-}
3334
3435
@@ -38,9 +39,9 @@ bool (toColumns -> false) (toColumns -> true) condition =
3839case_ :: Table Expr a => [(Expr Bool, a)] -> a -> a
3940case_ (map (fmap toColumns) -> branches) (toColumns -> fallback) =
4041 fromColumns $ htabulate $ \field -> case hfield fallback field of
41− fallbackExpr ->
42− case map (fmap (`hfield` field)) branches of
43− branchExprs -> caseExpr branchExprs fallbackExpr
42+ E fallbackExpr ->
43+ case map (fmap (unE . (`hfield` field))) branches of
44+ branchExprs -> E (caseExpr branchExprs fallbackExpr)
4445
4546
4647-- | Like 'maybe', but to eliminate @null@.
src/Rel8/Table/Cols.hsdeleted−50
@@ -1,50 +0,0 @@
1−{-# language DataKinds #-}
2−{-# language FlexibleInstances #-}
3−{-# language MultiParamTypeClasses #-}
4−{-# language StandaloneKindSignatures #-}
5−{-# language TypeFamilies #-}
6−{-# language UndecidableInstances #-}
7−
8−module Rel8.Table.Cols
9− ( Cols( Cols )
10− , fromCols
11− , toCols
12− )
13−where
14−
15−-- base
16−import Data.Kind ( Type )
17−import Prelude
18−
19−-- rel8
20−import qualified Rel8.Schema.Kind as K
21−import Rel8.Schema.HTable ( HTable )
22−import Rel8.Schema.Result ( Result )
23−import Rel8.Table ( Table(..) )
24−
25−
26−type Cols :: K.Context -> K.HTable -> Type
27−newtype Cols context columns = Cols (columns context)
28−
29−
30−instance (HTable columns, context ~ context') =>
31− Table context' (Cols context columns)
32− where
33− type Columns (Cols context columns) = columns
34− type Context (Cols context columns) = context
35− type FromExprs (Cols context columns) = Cols Result columns
36− type Transpose to (Cols context columns) = Cols to columns
37−
38− toColumns (Cols a) = a
39− fromColumns = Cols
40−
41− toResult (Cols a) = a
42− fromResult = Cols
43−
44−
45−fromCols :: Table context a => Cols context (Columns a) -> a
46−fromCols (Cols a) = fromColumns a
47−
48−
49−toCols :: Table context a => a -> Cols context (Columns a)
50−toCols = Cols . toColumns
src/Rel8/Table/Either.hs+131−126
@@ -18,44 +18,46 @@ module Rel8.Table.Either
1818 ( EitherTable(..)
1919 , eitherTable, leftTable, rightTable
2020 , isLeftTable, isRightTable
21− , aggregateEitherTable
2221 , nameEitherTable
2322 )
2423where
2524
2625-- base
26+import Control.Applicative ( liftA2 )
2727import Data.Bifunctor ( Bifunctor, bimap )
28−import Data.Functor.Identity ( Identity( Identity ) )
28+import Data.Functor.Identity ( runIdentity )
2929import Data.Kind ( Type )
3030import Prelude hiding ( undefined )
3131
32−-- comonad
33−import Control.Comonad ( extract )
34−
3532-- rel8
36−import Rel8.Aggregate ( Aggregate )
3733import Rel8.Expr ( Expr )
38−import Rel8.Expr.Aggregate ( groupByExpr )
3934import Rel8.Expr.Serialize ( litExpr )
40−import Rel8.Kind.Context ( Reifiable )
41−import Rel8.Schema.Context.Nullify ( Nullifiable )
42−import Rel8.Schema.Dict ( Dict( Dict ) )
35+import Rel8.Schema.Context.Label
36+ ( Labelable
37+ , HLabelable, hlabeler, hunlabeler
38+ )
39+import Rel8.Schema.Context.Nullify
40+ ( Nullifiable, ConstrainTag
41+ , HNullifiable, HConstrainTag
42+ , hencodeTag, hdecodeTag
43+ , hnullifier, hunnullifier
44+ )
45+import Rel8.Schema.HTable ( HTable )
4346import Rel8.Schema.HTable.Either ( HEitherTable(..) )
4447import Rel8.Schema.HTable.Identity ( HIdentity(..) )
4548import Rel8.Schema.HTable.Label ( hlabel, hunlabel )
46−import qualified Rel8.Schema.Kind as K
49+import Rel8.Schema.HTable.Nullify ( hnullify, hunnullify )
4750import Rel8.Schema.Name ( Name )
4851import Rel8.Table
4952 ( Table, Columns, Context, fromColumns, toColumns
50− , FromExprs, fromResult, toResult
51− , Transpose
53+ , reify, unreify
5254 )
5355import Rel8.Table.Bool ( bool )
5456import Rel8.Table.Eq ( EqTable, eqTable )
55−import Rel8.Table.Nullify ( Nullify, aggregateNullify, guard )
5657import Rel8.Table.Ord ( OrdTable, ordTable )
57−import Rel8.Table.Projection ( Biprojectable, Projectable, biproject, project )
58−import Rel8.Table.Serialize ( ToExprs )
58+import Rel8.Table.Recontextualize ( Recontextualize )
59+import Rel8.Table.Serialize ( FromExprs, ToExprs, fromResult, toResult )
60+import Rel8.Table.Tag ( Tag(..), fromExpr, fromName )
5961import Rel8.Table.Undefined ( undefined )
6062import Rel8.Type.Tag ( EitherTag( IsLeft, IsRight ), isLeft, isRight )
6163
@@ -70,162 +72,119 @@ import Data.Functor.Bind ( Bind, (>>-) )
7072--
7173-- An @EitherTable@ is operationally the same as Haskell's 'Either' type, but
7274-- adapted to work with Rel8.
73−type EitherTable :: K.Context -> Type -> Type -> Type
74−data EitherTable context a b = EitherTable
75− { tag :: context EitherTag
76− , left :: Nullify context a
77− , right :: Nullify context b
75+type EitherTable :: Type -> Type -> Type
76+data EitherTable a b = EitherTable
77+ { tag :: Tag "isRight" EitherTag
78+ , left :: a
79+ , right :: b
7880 }
7981 deriving stock Functor
8082
8183
82−instance Biprojectable (EitherTable context) where
83− biproject f g (EitherTable tag a b) =
84− EitherTable tag (project f a) (project g b)
85−
84+instance Bifunctor EitherTable where
85+ bimap f g (EitherTable tag a b) = EitherTable tag (f a) (g b)
8686
87−instance Nullifiable context => Bifunctor (EitherTable context) where
88− bimap f g (EitherTable tag a b) = EitherTable tag (fmap f a) (fmap g b)
8987
90−
91−instance Projectable (EitherTable context a) where
92− project f (EitherTable tag a b) = EitherTable tag a (project f b)
93−
94−
95−instance (context ~ Expr, Table Expr a) => Apply (EitherTable context a) where
88+instance Table Expr a => Apply (EitherTable a) where
9689 EitherTable tag l1 f <.> EitherTable tag' l2 a =
97− EitherTable (tag <> tag') (bool l1 l2 (isLeft tag)) (f <.> a)
90+ EitherTable (tag <> tag') (bool l1 l2 (isLeft (expr tag))) (f a)
9891
9992
100−instance (context ~ Expr, Table Expr a) => Applicative (EitherTable context a) where
93+instance Table Expr a => Applicative (EitherTable a) where
10194 pure = rightTable
10295 (<*>) = (<.>)
10396
10497
105−instance (context ~ Expr, Table Expr a) => Bind (EitherTable context a) where
106− EitherTable tag l1 a >>- f = case f (extract a) of
98+instance Table Expr a => Bind (EitherTable a) where
99+ EitherTable tag l1 a >>- f = case f a of
107100 EitherTable tag' l2 b ->
108− EitherTable (tag <> tag') (bool l1 l2 (isRight tag)) b
101+ EitherTable (tag <> tag') (bool l1 l2 (isRight (expr tag))) b
109102
110103
111−instance (context ~ Expr, Table Expr a) => Monad (EitherTable context a) where
104+instance Table Expr a => Monad (EitherTable a) where
112105 (>>=) = (>>-)
113106
114107
115−instance (context ~ Expr, Table Expr a, Table Expr b) =>
116− Semigroup (EitherTable context a b)
117− where
108+instance (Table Expr a, Table Expr b) => Semigroup (EitherTable a b) where
118109 a <> b = bool a b (isRightTable a)
119110
120111
121112instance
122113 ( Table context a, Table context b
123− , Reifiable context, context ~ context'
124− )
125− => Table context' (EitherTable context a b)
126− where
127− type Columns (EitherTable context a b) = HEitherTable (Columns a) (Columns b)
128− type Context (EitherTable context a b) = Context a
129− type FromExprs (EitherTable context a b) = Either (FromExprs a) (FromExprs b)
130− type Transpose to (EitherTable context a b) =
131− EitherTable to (Transpose to a) (Transpose to b)
132−
133− toColumns EitherTable {tag, left, right} = HEitherTable
134− { htag = hlabel $ HIdentity tag
135− , hleft = hlabel $ guard tag (== IsLeft) isLeft $ toColumns left
136− , hright = hlabel $ guard tag (== IsRight) isRight $ toColumns right
137− }
138−
139− fromColumns HEitherTable {htag, hleft, hright} = EitherTable
140− { tag = unHIdentity $ hunlabel htag
141− , left = fromColumns $ hunlabel hleft
142− , right = fromColumns $ hunlabel hright
143− }
144−
145− toResult = \case
146− Left table -> HEitherTable
147− { htag = hlabel (HIdentity (Identity IsLeft))
148− , hleft = hlabel (toResult @_ @(Nullify context a) (Just table))
149− , hright = hlabel (toResult @_ @(Nullify context b) Nothing)
150− }
151− Right table -> HEitherTable
152− { htag = hlabel (HIdentity (Identity IsRight))
153− , hleft = hlabel (toResult @_ @(Nullify context a) Nothing)
154− , hright = hlabel (toResult @_ @(Nullify context b) (Just table))
155− }
156−
157− fromResult HEitherTable {htag, hleft, hright} = case hunlabel htag of
158− HIdentity (Identity tag) -> case tag of
159− IsLeft -> maybe err Left $ fromResult @_ @(Nullify context a) (hunlabel hleft)
160− IsRight -> maybe err Right $ fromResult @_ @(Nullify context b) (hunlabel hright)
161− where
162− err = error "Either.fromColumns: mismatch between tag and data"
163−
164−
165−instance (EqTable a, EqTable b, context ~ Expr) =>
166− EqTable (EitherTable context a b)
114+ , Labelable context, Nullifiable context, ConstrainTag context EitherTag
115+ ) =>
116+ Table context (EitherTable a b)
167117 where
168− eqTable = HEitherTable
169− { htag = hlabel (HIdentity Dict)
170− , hleft = hlabel (eqTable @(Nullify context a))
171− , hright = hlabel (eqTable @(Nullify context b))
172− }
118+ type Columns (EitherTable a b) = HEitherTable (Columns a) (Columns b)
119+ type Context (EitherTable a b) = Context a
173120
121+ toColumns = toColumns2 toColumns toColumns
122+ fromColumns = fromColumns2 fromColumns fromColumns
123+ reify = liftA2 bimap reify reify
124+ unreify = liftA2 bimap unreify unreify
174125
175−instance (OrdTable a, OrdTable b, context ~ Expr) =>
176− OrdTable (EitherTable context a b)
177− where
178− ordTable = HEitherTable
179− { htag = hlabel (HIdentity Dict)
180− , hleft = hlabel (ordTable @(Nullify context a))
181− , hright = hlabel (ordTable @(Nullify context b))
182− }
126+
127+instance
128+ ( Nullifiable from, Labelable from, ConstrainTag from EitherTag
129+ , Nullifiable to, Labelable to, ConstrainTag to EitherTag
130+ , Recontextualize from to a1 b1
131+ , Recontextualize from to a2 b2
132+ )
133+ => Recontextualize from to (EitherTable a1 a2) (EitherTable b1 b2)
134+
135+
136+instance (EqTable a, EqTable b) => EqTable (EitherTable a b) where
137+ eqTable = toColumns2 id id (rightTableWith (eqTable @a) (eqTable @b))
183138
184139
185−instance (ToExprs exprs1 a, ToExprs exprs2 b, x ~ EitherTable Expr exprs1 exprs2) =>
140+instance (OrdTable a, OrdTable b) => OrdTable (EitherTable a b) where
141+ ordTable = toColumns2 id id (rightTableWith (ordTable @a) (ordTable @b))
142+
143+
144+type instance FromExprs (EitherTable a b) = Either (FromExprs a) (FromExprs b)
145+
146+
147+instance (ToExprs exprs1 a, ToExprs exprs2 b, x ~ EitherTable exprs1 exprs2) =>
186148 ToExprs x (Either a b)
149+ where
150+ fromResult =
151+ bimap (fromResult @exprs1) (fromResult @exprs2) .
152+ fromColumns
153+ toResult =
154+ toColumns .
155+ bimap (toResult @exprs1) (toResult @exprs2)
187156
188157
189158-- | Test if an 'EitherTable' is a 'leftTable'.
190−isLeftTable :: EitherTable Expr a b -> Expr Bool
191−isLeftTable EitherTable {tag} = isLeft tag
159+isLeftTable :: EitherTable a b -> Expr Bool
160+isLeftTable = isLeft . expr . tag
192161
193162
194163-- | Test if an 'EitherTable' is a 'rightTable'.
195−isRightTable :: EitherTable Expr a b -> Expr Bool
196−isRightTable EitherTable {tag} = isRight tag
164+isRightTable :: EitherTable a b -> Expr Bool
165+isRightTable = isRight . expr . tag
197166
198167
199168-- | Pattern match/eliminate an 'EitherTable', by providing mappings from a
200169-- 'leftTable' and 'rightTable'.
201170eitherTable :: Table Expr c
202− => (a -> c) -> (b -> c) -> EitherTable Expr a b -> c
171+ => (a -> c) -> (b -> c) -> EitherTable a b -> c
203172eitherTable f g EitherTable {tag, left, right} =
204− bool (f (extract left)) (g (extract right)) (isRight tag)
173+ bool (f left) (g right) (isRight (expr tag))
205174
206175
207176-- | Construct a left 'EitherTable'. Like 'Left'.
208−leftTable :: Table Expr b => a -> EitherTable Expr a b
209−leftTable a = EitherTable (litExpr IsLeft) (pure a) undefined
177+leftTable :: Table Expr b => a -> EitherTable a b
178+leftTable a = EitherTable (fromExpr (litExpr IsLeft)) a undefined
210179
211180
212181-- | Construct a right 'EitherTable'. Like 'Right'.
213−rightTable :: Table Expr a => b -> EitherTable Expr a b
214−rightTable = EitherTable (litExpr IsRight) undefined . pure
215−
216−
217−-- | Lift a pair of aggregating functions to operate on an 'EitherTable'.
218−-- @leftTable@s and @rightTable@s are grouped separately.
219−aggregateEitherTable :: ()
220− => (exprs -> aggregates)
221− -> (exprs' -> aggregates')
222− -> EitherTable Expr exprs exprs'
223− -> EitherTable Aggregate aggregates aggregates'
224−aggregateEitherTable f g (EitherTable tag a b) = EitherTable
225− { tag = groupByExpr tag
226− , left = aggregateNullify f a
227− , right = aggregateNullify g b
228− }
182+rightTable :: Table Expr a => b -> EitherTable a b
183+rightTable = rightTableWith undefined
184+
185+
186+rightTableWith :: a -> b -> EitherTable a b
187+rightTableWith = EitherTable (fromExpr (litExpr IsRight))
229188
230189
231190-- | Construct a 'EitherTable' in the 'Name' context. This can be useful if you
@@ -239,5 +198,51 @@ nameEitherTable
239198 -- ^ Names of the columns in the @a@ table.
240199 -> b
241200 -- ^ Names of the columns in the @b@ table.
242− -> EitherTable Name a b
243−nameEitherTable tag left right = EitherTable tag (pure left) (pure right)
201+ -> EitherTable a b
202+nameEitherTable = EitherTable . fromName
203+
204+
205+toColumns2 ::
206+ ( HTable t
207+ , HTable u
208+ , HConstrainTag context EitherTag
209+ , HLabelable context
210+ , HNullifiable context
211+ )
212+ => (a -> t context)
213+ -> (b -> u context)
214+ -> EitherTable a b
215+ -> HEitherTable t u context
216+toColumns2 f g EitherTable {tag, left, right} = HEitherTable
217+ { htag
218+ , hleft = hlabel hlabeler $ hnullify (hnullifier tag isLeft) $ f left
219+ , hright = hlabel hlabeler $ hnullify (hnullifier tag isRight) $ g right
220+ }
221+ where
222+ htag = HIdentity (hencodeTag tag)
223+
224+
225+fromColumns2 ::
226+ ( HTable t
227+ , HTable u
228+ , HConstrainTag context EitherTag
229+ , HLabelable context
230+ , HNullifiable context
231+ )
232+ => (t context -> a)
233+ -> (u context -> b)
234+ -> HEitherTable t u context
235+ -> EitherTable a b
236+fromColumns2 f g HEitherTable {htag, hleft, hright} = EitherTable
237+ { tag
238+ , left = f $ runIdentity $
239+ hunnullify (\a -> pure . hunnullifier a) $
240+ hunlabel hunlabeler
241+ hleft
242+ , right = g $ runIdentity $
243+ hunnullify (\a -> pure . hunnullifier a) $
244+ hunlabel hunlabeler
245+ hright
246+ }
247+ where
248+ tag = hdecodeTag $ unHIdentity htag
src/Rel8/Table/Eq.hs+39−12
@@ -11,6 +11,7 @@
1111{-# language TypeFamilies #-}
1212{-# language TypeOperators #-}
1313{-# language UndecidableInstances #-}
14+{-# language UndecidableSuperClasses #-}
1415{-# language ViewPatterns #-}
1516
1617module Rel8.Table.Eq
@@ -27,16 +28,24 @@ import GHC.Generics ( Rep )
2728import Prelude
2829
2930-- rel8
30−import Rel8.Expr ( Expr )
31+import Rel8.Expr ( Expr, Col( E ) )
3132import Rel8.Expr.Bool ( (||.), (&&.) )
3233import Rel8.Expr.Eq ( (==.), (/=.) )
3334import Rel8.FCF ( Eval, Exp )
3435import Rel8.Generic.Record ( Record )
35−import Rel8.Generic.Table.Record ( GTable, GColumns, gtable )
36+import Rel8.Generic.Table
37+ ( GGTable, GGColumns, ggtable
38+ , GAlgebra
39+ )
40+import Rel8.Kind.Algebra ( KnownAlgebra )
3641import Rel8.Schema.Dict ( Dict( Dict ) )
37−import Rel8.Schema.HTable ( htabulateA, hfield )
38−import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) )
42+import Rel8.Schema.HTable
43+ ( HTable, HConstrainTable
44+ , htabulateA, hfield, hdicts
45+ )
46+import Rel8.Schema.HTable.Identity ( HIdentity( HType ) )
3947import Rel8.Schema.Null ( Sql )
48+import Rel8.Schema.Spec.ConstrainDBType ( ConstrainDBType, nullifier )
4049import Rel8.Table ( Table, Columns, toColumns, TColumns )
4150import Rel8.Type.Eq ( DBEq )
4251
@@ -46,14 +55,22 @@ import Rel8.Type.Eq ( DBEq )
4655-- means "all columns in a 'Table' have an instance of 'DBEq'".
4756type EqTable :: Type -> Constraint
4857class Table Expr a => EqTable a where
49− eqTable :: Columns a (Dict (Sql DBEq))
58+ eqTable :: Columns a (Dict (ConstrainDBType DBEq))
5059
5160 default eqTable ::
52− ( GTable TEqTable TColumns (Rep (Record a))
53− , Columns a ~ GColumns TColumns (Rep (Record a))
61+ ( KnownAlgebra (GAlgebra (Rep (Record a)))
62+ , Eval (GGTable (GAlgebra (Rep (Record a))) TEqTable TColumns (Dict (ConstrainDBType DBEq)) (Rep (Record a)))
63+ , Columns a ~ Eval (GGColumns (GAlgebra (Rep (Record a))) TColumns (Rep (Record a)))
5464 )
55− => Columns a (Dict (Sql DBEq))
56− eqTable = gtable @TEqTable @TColumns @(Rep (Record a)) table
65+ => Columns a (Dict (ConstrainDBType DBEq))
66+ eqTable =
67+ ggtable
68+ @(GAlgebra (Rep (Record a)))
69+ @TEqTable
70+ @TColumns
71+ @(Rep (Record a))
72+ table
73+ nullifier
5774 where
5875 table (_ :: proxy x) = eqTable @x
5976
@@ -62,8 +79,18 @@ data TEqTable :: Type -> Exp Constraint
6279type instance Eval (TEqTable a) = EqTable a
6380
6481
82+instance
83+ ( HTable t
84+ , f ~ Col Expr
85+ , HConstrainTable t (ConstrainDBType DBEq)
86+ )
87+ => EqTable (t f)
88+ where
89+ eqTable = hdicts @(Columns (t f)) @(ConstrainDBType DBEq)
90+
91+
6592instance Sql DBEq a => EqTable (Expr a) where
66− eqTable = HIdentity Dict
93+ eqTable = HType Dict
6794
6895
6996instance (EqTable a, EqTable b) => EqTable (a, b)
@@ -97,7 +124,7 @@ instance
97124(toColumns -> as) ==: (toColumns -> bs) =
98125 foldl1' (&&.) $ getConst $ htabulateA $ \field ->
99126 case (hfield as field, hfield bs field) of
100− (a, b) -> case hfield (eqTable @a) field of
127+ (E a, E b) -> case hfield (eqTable @a) field of
101128 Dict -> Const (pure (a ==. b))
102129infix 4 ==:
103130
@@ -109,7 +136,7 @@ infix 4 ==:
109136(toColumns -> as) /=: (toColumns -> bs) =
110137 foldl1' (||.) $ getConst $ htabulateA $ \field ->
111138 case (hfield as field, hfield bs field) of
112− (a, b) -> case hfield (eqTable @a) field of
139+ (E a, E b) -> case hfield (eqTable @a) field of
113140 Dict -> Const (pure (a /=. b))
114141infix 4 /=:
115142
src/Rel8/Table/HKD.hs+105−65
@@ -13,7 +13,7 @@
1313
1414module Rel8.Table.HKD
1515 ( HKD( HKD )
16− , HKDable
16+ , HKDable, fromHKD, toHKD, HKDT(..)
1717 , BuildableHKD
1818 , BuildHKD, buildHKD
1919 , ConstructableHKD
@@ -27,6 +27,9 @@ where
2727
2828-- base
2929import Data.Kind ( Constraint, Type )
30+import Data.Proxy ( Proxy( Proxy ) )
31+import Data.Type.Equality ( (:~:)( Refl ) )
32+import Data.Void ( Void )
3033import GHC.Generics ( Generic, Rep, from, to )
3134import GHC.TypeLits ( Symbol )
3235import Prelude
@@ -46,118 +49,129 @@ import Rel8.Generic.Construction
4649 , GGName, ggname
4750 , GGAggregate, ggaggregate
4851 )
49−import Rel8.Generic.Map ( GMap )
50−import Rel8.Generic.Record
51− ( GRecord, GRecordable, grecord, gunrecord
52− , Record( Record ), unrecord
53− )
52+import Rel8.Generic.Map ( GMap, GMappable, gmap, gunmap )
53+import Rel8.Generic.Record ( GRecord, GRecordable, grecord, gunrecord )
5454import Rel8.Generic.Rel8able
5555 ( Rel8able
5656 , GColumns, gfromColumns, gtoColumns
57− , GFromExprs, gfromResult, gtoResult
57+ , greify, gunreify
58+ , TUnreifyContext
5859 )
5960import Rel8.Generic.Table
60− ( GGSerialize, GGColumns, GAlgebra, ggfromResult, ggtoResult
61+ ( GGTable, GGColumns, GGContext, ggfromColumns, ggtoColumns
62+ , GAlgebra
6163 )
62−import Rel8.Generic.Table.Record ( GTable, GContext )
63−import qualified Rel8.Generic.Table.Record as G
64+import Rel8.Schema.Context ( Col )
6465import qualified Rel8.Schema.Kind as K
6566import Rel8.Schema.HTable ( HTable )
6667import Rel8.Schema.Name ( Name )
68+import Rel8.Schema.Reify ( Col( Reify ), Reify, hreify, hunreify, notReify )
6769import Rel8.Schema.Result ( Result )
6870import Rel8.Table
69− ( Table, fromColumns, toColumns, fromResult, toResult
70− , TTable, TColumns, TContext
71− , TSerialize
71+ ( Table, Columns, Context, Unreify
72+ , fromColumns, toColumns, reify, unreify
73+ , TTable, TColumns, TUnreify
7274 )
75+import Rel8.Table.Serialize ( ToExprs, fromResult, toResult )
7376
7477
7578type GColumnsHKD :: Type -> K.HTable
7679type GColumnsHKD a =
77− Eval (GGColumns (GAlgebra (Rep a)) TColumns (GRecord (GMap (TColumn Expr) (Rep a))))
80+ Eval (GGColumns (GAlgebra (Rep a)) TColumns (GRecord (GMap (TColumn (Reify Result)) (Rep a))))
7881
7982
8083type HKD :: Type -> K.Rel8able
81−newtype HKD a f = HKD (GColumnsHKD a f)
84+newtype HKD a f = HKD (GColumnsHKD a (Col f))
8285
8386
8487instance HKDable a => Rel8able (HKD a) where
8588 type GColumns (HKD a) = GColumnsHKD a
86− type GFromExprs (HKD a) = a
8789
88− gfromColumns _ = HKD
89− gtoColumns _ (HKD a) = a
90+ gfromColumns = HKD
91+ gtoColumns (HKD a) = a
9092
91− gfromResult =
92− unrecord .
93− to .
94− ggfromResult
95− @(GAlgebra (Rep a))
96− @TSerialize
97− @TColumns
98− @(Eval (HKDRep a Expr))
99− @(Eval (HKDRep a Result))
100− (\(_ :: proxy x) -> fromResult @_ @x)
101−
102− gtoResult =
103− ggtoResult
104− @(GAlgebra (Rep a))
105− @TSerialize
106− @TColumns
107− @(Eval (HKDRep a Expr))
108− @(Eval (HKDRep a Result))
109− (\(_ :: proxy x) -> toResult @_ @x) .
110− from .
111− Record
93+ greify (HKD a) = HKD (hreify a)
94+ gunreify (HKD a) = HKD (hunreify a)
11295
11396
11497instance
115− ( GTable (TTable f) TColumns (GRecord (GMap (TColumn f) (Rep a)))
116− , G.GColumns TColumns (GRecord (GMap (TColumn f) (Rep a))) ~ GColumnsHKD a
117− , GContext TContext (GRecord (GMap (TColumn f) (Rep a))) ~ f
118− , GRecordable (GMap (TColumn f) (Rep a))
98+ ( KnownAlgebra (GAlgebra (Rep a))
99+ , HTable (GColumnsHKD a)
100+ , Eval (GGTable (GAlgebra (Rep a)) (TTable (Reify f)) TColumns (Col (Reify f)) (GRecord (GMap (TColumn (Reify f)) (Rep a))))
101+ , Eval (GGColumns (GAlgebra (Rep a)) TColumns (GRecord (GMap (TColumn (Reify f)) (Rep a)))) ~ GColumnsHKD a
102+ , Eval (GGContext (GAlgebra (Rep a)) TUnreifyContext (GRecord (GMap (TColumn (Reify f)) (Rep a)))) ~ f
103+ , GRecordable (GMap (TColumn (Reify f)) (Rep a))
104+ , GMappable (TTable (Reify f)) (GMap (TColumn (Reify f)) (Rep a))
105+ , GMap TUnreify (GMap (TColumn (Reify f)) (Rep a)) ~ GMap (TColumn f) (Rep a)
119106 )
120107 => Generic (HKD a f)
121108 where
122109 type Rep (HKD a f) = GMap (TColumn f) (Rep a)
123110
124111 from =
125− gunrecord @(GMap (TColumn f) (Rep a)) .
126− G.gfromColumns
127− @(TTable f)
112+ gmap @(TTable (Reify f)) (Proxy @TUnreify) (unreify Refl) .
113+ gunrecord @(GMap (TColumn (Reify f)) (Rep a)) .
114+ ggfromColumns
115+ @(GAlgebra (Rep a))
116+ @(TTable (Reify f))
128117 @TColumns
118+ (\(Reify a) -> a)
119+ Reify
129120 fromColumns .
121+ hreify .
130122 (\(HKD a) -> a)
131123
132124 to =
133125 HKD .
134− G.gtoColumns
135− @(TTable f)
126+ hunreify .
127+ ggtoColumns
128+ @(GAlgebra (Rep a))
129+ @(TTable (Reify f))
136130 @TColumns
131+ (\(Reify a) -> a)
132+ Reify
137133 toColumns .
138− grecord @(GMap (TColumn f) (Rep a))
134+ grecord @(GMap (TColumn (Reify f)) (Rep a)) .
135+ gunmap @(TTable (Reify f)) (Proxy @TUnreify) (reify Refl)
136+
137+
138+type HKDT :: Type -> Type
139+newtype HKDT a = HKDT
140+ { unHKDT :: a
141+ }
142+
143+
144+instance HKDable a => Table Result (HKDT a) where
145+ type Columns (HKDT a) = GColumnsHKD a
146+ type Context (HKDT a) = Result
147+ type Unreify (HKDT a) = Void
148+
149+ fromColumns = HKDT . fromHKD . HKD
150+ toColumns = (\(HKD a) -> a) . toHKD . (\(HKDT a) -> a)
151+ reify = notReify
152+ unreify = notReify
139153
140154
141−type HKDable :: Type -> Constraint
142−class
143− ( Generic (Record a)
144− , HTable (GColumns (HKD a))
145− , KnownAlgebra (GAlgebra (Rep a))
146− , Eval (GGSerialize (GAlgebra (Rep a)) TSerialize TColumns (Eval (HKDRep a Expr)) (Eval (HKDRep a Result)))
147− , GRecord (GMap (TColumn Result) (Rep a)) ~ Rep (Record a)
148− )
149− => HKDable a
150155instance
151− ( Generic (Record a)
152− , HTable (GColumns (HKD a))
153− , KnownAlgebra (GAlgebra (Rep a))
154− , Eval (GGSerialize (GAlgebra (Rep a)) TSerialize TColumns (Eval (HKDRep a Expr)) (Eval (HKDRep a Result)))
155− , GRecord (GMap (TColumn Result) (Rep a)) ~ Rep (Record a)
156+ ( Table Expr (HKD a Expr)
157+ , Columns (HKD a Expr) ~ GColumns (HKD a)
158+ , HKDable a
159+ , x ~ HKD a Expr
156160 )
157− => HKDable a
161+ => ToExprs x (HKDT a)
162+ where
163+ toResult = (\(HKD a) -> a) . toHKD . (\(HKDT a) -> a)
164+ fromResult = HKDT . fromHKD . HKD
165+
166+
167+fromHKD :: HKDable a => HKD a Result -> a
168+fromHKD = to . gunmap @Top (Proxy @(TColumn Result)) id . from
169+
170+
171+toHKD :: HKDable a => a -> HKD a Result
172+toHKD = to . gmap @Top (Proxy @(TColumn Result)) id . from
158173
159174
160−type Top_ :: Constraint
161175class Top_
162176instance Top_
163177
@@ -166,6 +180,32 @@ data Top :: Type -> Exp Constraint
166180type instance Eval (Top _) = Top_
167181
168182
183+class
184+ ( Generic a
185+ , HTable (GColumns (HKD a))
186+ , KnownAlgebra (GAlgebra (Rep a))
187+ , Eval (GGTable (GAlgebra (Rep a)) (TTable (Reify Result)) TColumns (Col (Reify Result)) (GRecord (GMap (TColumn (Reify Result)) (Rep a))))
188+ , Eval (GGContext (GAlgebra (Rep a)) TUnreifyContext (GRecord (GMap (TColumn (Reify Result)) (Rep a)))) ~ Result
189+ , GRecordable (GMap (TColumn (Reify Result)) (Rep a))
190+ , GMappable Top (Rep a)
191+ , GMappable (TTable (Reify Result)) (GMap (TColumn (Reify Result)) (Rep a))
192+ , GMap TUnreify (GMap (TColumn (Reify Result)) (Rep a)) ~ GMap (TColumn Result) (Rep a)
193+ )
194+ => HKDable a
195+instance
196+ ( Generic a
197+ , HTable (GColumns (HKD a))
198+ , KnownAlgebra (GAlgebra (Rep a))
199+ , Eval (GGTable (GAlgebra (Rep a)) (TTable (Reify Result)) TColumns (Col (Reify Result)) (GRecord (GMap (TColumn (Reify Result)) (Rep a))))
200+ , Eval (GGContext (GAlgebra (Rep a)) TUnreifyContext (GRecord (GMap (TColumn (Reify Result)) (Rep a)))) ~ Result
201+ , GRecordable (GMap (TColumn (Reify Result)) (Rep a))
202+ , GMappable Top (Rep a)
203+ , GMappable (TTable (Reify Result)) (GMap (TColumn (Reify Result)) (Rep a))
204+ , GMap TUnreify (GMap (TColumn (Reify Result)) (Rep a)) ~ GMap (TColumn Result) (Rep a)
205+ )
206+ => HKDable a
207+
208+
169209type BuildableHKD :: Type -> Symbol -> Constraint
170210class GGBuildable (GAlgebra (Rep a)) name (HKDRep a) => BuildableHKD a name
171211instance GGBuildable (GAlgebra (Rep a)) name (HKDRep a) => BuildableHKD a name
src/Rel8/Table/List.hs+55−65
@@ -1,4 +1,3 @@
1−{-# language DataKinds #-}
21{-# language FlexibleContexts #-}
32{-# language FlexibleInstances #-}
43{-# language MultiParamTypeClasses #-}
@@ -11,36 +10,30 @@
1110
1211module Rel8.Table.List
1312 ( ListTable(..)
14− , ($*)
15− , listTable
16− , nameListTable
13+ , listTable, nameListTable
1714 )
1815where
1916
2017-- base
2118import Data.Functor.Identity ( Identity( Identity ) )
2219import Data.Kind ( Type )
20+import Data.Type.Equality ( (:~:)( Refl ) )
2321import Prelude
2422
2523-- rel8
26−import Rel8.Expr ( Expr )
24+import Rel8.Expr ( Expr, Col( E, unE ) )
2725import Rel8.Expr.Array ( sappend, sempty, slistOf )
2826import Rel8.Schema.Dict ( Dict( Dict ) )
2927import Rel8.Schema.HTable.List ( HListTable )
30−import Rel8.Schema.HTable.Vectorize
31− ( hvectorize, hunvectorize
32− , happend, hempty
33− , hproject, hcolumn
34− )
35−import qualified Rel8.Schema.Kind as K
36−import Rel8.Schema.Name ( Name( Name ) )
28+import Rel8.Schema.HTable.Vectorize ( happend, hempty, hvectorize )
29+import Rel8.Schema.Name ( Col( N ), Name( Name ) )
3730import Rel8.Schema.Null ( Nullity( Null, NotNull ) )
38−import Rel8.Schema.Result ( vectorizer, unvectorizer )
39−import Rel8.Schema.Spec ( Spec(..) )
31+import Rel8.Schema.Spec ( SSpec(..) )
32+import Rel8.Schema.Spec.ConstrainDBType ( dbTypeDict, dbTypeNullity )
33+import Rel8.Schema.Reify ( hreify, hunreify )
4034import Rel8.Table
4135 ( Table, Context, Columns, fromColumns, toColumns
42− , FromExprs, fromResult, toResult
43− , Transpose
36+ , reify, unreify
4437 )
4538import Rel8.Table.Alternative
4639 ( AltTable, (<|>:)
@@ -48,90 +41,87 @@ import Rel8.Table.Alternative
4841 )
4942import Rel8.Table.Eq ( EqTable, eqTable )
5043import Rel8.Table.Ord ( OrdTable, ordTable )
51−import Rel8.Table.Projection
52− ( Projectable, Projecting, Projection, project, apply
53− )
54−import Rel8.Table.Serialize ( ToExprs )
44+import Rel8.Table.Recontextualize ( Recontextualize )
45+import Rel8.Table.Serialize ( FromExprs, ToExprs, fromResult, toResult )
46+import Rel8.Table.Unreify ( Unreifies )
5547
5648
5749-- | A @ListTable@ value contains zero or more instances of @a@. You construct
5850-- @ListTable@s with 'Rel8.many' or 'Rel8.listAgg'.
59−type ListTable :: K.Context -> Type -> Type
60−newtype ListTable context a =
61− ListTable (HListTable (Columns a) (Context a))
62−
63−
64−instance Projectable (ListTable context) where
65− project f (ListTable a) = ListTable (hproject (apply f) a)
51+type ListTable :: Type -> Type
52+newtype ListTable a = ListTable (HListTable (Columns a) (Col (Context a)))
6653
6754
68−instance (Table context a, context ~ context') =>
69− Table context' (ListTable context a)
55+instance (Table context a, Unreifies context a) =>
56+ Table context (ListTable a)
7057 where
71− type Columns (ListTable context a) = HListTable (Columns a)
72− type Context (ListTable context a) = Context a
73− type FromExprs (ListTable context a) = [FromExprs a]
74− type Transpose to (ListTable context a) = ListTable to (Transpose to a)
58+ type Columns (ListTable a) = HListTable (Columns a)
59+ type Context (ListTable a) = Context a
7560
7661 fromColumns = ListTable
7762 toColumns (ListTable a) = a
78− fromResult = fmap (fromResult @_ @a) . hunvectorize unvectorizer
79− toResult = hvectorize vectorizer . fmap (toResult @_ @a)
63+
64+ reify Refl (ListTable a) = ListTable (hreify a)
65+ unreify Refl (ListTable a) = ListTable (hunreify a)
66+
67+
68+instance
69+ ( Unreifies from a, Unreifies to b
70+ , Recontextualize from to a b
71+ )
72+ => Recontextualize from to (ListTable a) (ListTable b)
8073
8174
82−instance (EqTable a, context ~ Expr) => EqTable (ListTable context a) where
75+instance EqTable a => EqTable (ListTable a) where
8376 eqTable =
8477 hvectorize
85− (\Spec {nullity} (Identity Dict) -> case nullity of
86− Null -> Dict
87− NotNull -> Dict)
78+ (\SSpec {} (Identity dict) -> case dbTypeDict dict of
79+ Dict -> case dbTypeNullity dict of
80+ Null -> Dict
81+ NotNull -> Dict)
8882 (Identity (eqTable @a))
8983
9084
91−instance (OrdTable a, context ~ Expr) => OrdTable (ListTable context a) where
85+instance OrdTable a => OrdTable (ListTable a) where
9286 ordTable =
9387 hvectorize
94− (\Spec {nullity} (Identity Dict) -> case nullity of
95− Null -> Dict
96− NotNull -> Dict)
88+ (\SSpec {} (Identity dict) -> case dbTypeDict dict of
89+ Dict -> case dbTypeNullity dict of
90+ Null -> Dict
91+ NotNull -> Dict)
9792 (Identity (ordTable @a))
9893
9994
100−instance (ToExprs exprs a, context ~ Expr) =>
101− ToExprs (ListTable context exprs) [a]
95+type instance FromExprs (ListTable a) = [FromExprs a]
10296
10397
104−instance context ~ Expr => AltTable (ListTable context) where
105− (<|>:) = (<>)
98+instance ToExprs exprs a => ToExprs (ListTable exprs) [a] where
99+ fromResult = fmap (fromResult @exprs) . fromColumns
100+ toResult = toColumns . fmap (toResult @exprs)
106101
107102
108−instance context ~ Expr => AlternativeTable (ListTable context) where
109− emptyTable = mempty
103+instance AltTable ListTable where
104+ (<|>:) = (<>)
110105
111106
112−instance (context ~ Expr, Table Expr a) => Semigroup (ListTable context a)
113− where
114− ListTable as <> ListTable bs = ListTable $ happend (const sappend) as bs
107+instance AlternativeTable ListTable where
108+ emptyTable = mempty
115109
116110
117−instance (context ~ Expr, Table Expr a) =>
118− Monoid (ListTable context a)
119− where
120− mempty = ListTable $ hempty $ \Spec {info} -> sempty info
111+instance Table Expr a => Semigroup (ListTable a) where
112+ ListTable as <> ListTable bs = ListTable $
113+ happend (\_ _ (E a) (E b) -> E (sappend a b)) as bs
121114
122115
123−-- | Project a single expression out of a 'ListTable'.
124−($*) :: Projecting a (Expr b)
125− => Projection a (Expr b) -> ListTable Expr a -> Expr [b]
126−f $* ListTable a = hcolumn $ hproject (apply f) a
127−infixl 4 $*
116+instance Table Expr a => Monoid (ListTable a) where
117+ mempty = ListTable $ hempty $ \_ -> E . sempty
128118
129119
130120-- | Construct a @ListTable@ from a list of expressions.
131−listTable :: Table Expr a => [a] -> ListTable Expr a
121+listTable :: Table Expr a => [a] -> ListTable a
132122listTable =
133123 ListTable .
134− hvectorize (\Spec {info} -> slistOf info) .
124+ hvectorize (\SSpec {info} -> E . slistOf info . fmap unE) .
135125 fmap toColumns
136126
137127
@@ -141,9 +131,9 @@ listTable =
141131nameListTable
142132 :: Table Name a
143133 => a -- ^ The names of the columns of elements of the list.
144− -> ListTable Name a
134+ -> ListTable a
145135nameListTable =
146136 ListTable .
147− hvectorize (\_ (Identity (Name a)) -> Name a) .
137+ hvectorize (\_ (Identity (N (Name a))) -> N (Name a)) .
148138 pure .
149139 toColumns
src/Rel8/Table/Maybe.hs+118−110
@@ -3,6 +3,7 @@
33{-# language DerivingStrategies #-}
44{-# language FlexibleContexts #-}
55{-# language FlexibleInstances #-}
6+{-# language LambdaCase #-}
67{-# language MultiParamTypeClasses #-}
78{-# language NamedFieldPuns #-}
89{-# language ScopedTypeVariables #-}
@@ -15,42 +16,39 @@ module Rel8.Table.Maybe
1516 ( MaybeTable(..)
1617 , maybeTable, nothingTable, justTable
1718 , isNothingTable, isJustTable
18− , fromMaybeTable
1919 , ($?)
20− , aggregateMaybeTable
2120 , nameMaybeTable
2221 )
2322where
2423
2524-- base
26−import Data.Functor ( ($>) )
27−import Data.Functor.Identity ( Identity( Identity ) )
25+import Data.Functor.Identity ( runIdentity )
2826import Data.Kind ( Type )
29−import Data.Maybe ( fromMaybe, isJust )
3027import Prelude hiding ( null, undefined )
3128
32−-- comonad
33−import Control.Comonad ( extract )
34−
3529-- rel8
36−import Rel8.Aggregate ( Aggregate )
3730import Rel8.Expr ( Expr )
38−import Rel8.Expr.Aggregate ( groupByExpr )
3931import Rel8.Expr.Bool ( boolExpr )
4032import Rel8.Expr.Null ( isNull, isNonNull, null, nullify )
41−import Rel8.Kind.Context ( Reifiable )
42−import Rel8.Schema.Dict ( Dict( Dict ) )
43−import qualified Rel8.Schema.Kind as K
33+import Rel8.Schema.Context.Label
34+ ( Labelable, HLabelable, hlabeler, hunlabeler
35+ )
36+import Rel8.Schema.Context.Nullify
37+ ( Nullifiable, ConstrainTag
38+ , HNullifiable, HConstrainTag
39+ , hencodeTag, hdecodeTag
40+ , hnullifier, hunnullifier
41+ )
42+import Rel8.Schema.HTable ( HTable )
4443import Rel8.Schema.HTable.Identity ( HIdentity(..) )
4544import Rel8.Schema.HTable.Label ( hlabel, hunlabel )
4645import Rel8.Schema.HTable.Maybe ( HMaybeTable(..) )
46+import Rel8.Schema.HTable.Nullify ( hnullify, hunnullify )
4747import Rel8.Schema.Name ( Name )
48−import Rel8.Schema.Null ( Nullity( Null, NotNull ), Sql, nullable )
49−import qualified Rel8.Schema.Null as N
48+import Rel8.Schema.Null ( Nullify, Nullity( Null, NotNull ), Sql, nullable )
5049import Rel8.Table
5150 ( Table, Columns, Context, fromColumns, toColumns
52− , FromExprs, fromResult, toResult
53− , Transpose
51+ , reify, unreify
5452 )
5553import Rel8.Table.Alternative
5654 ( AltTable, (<|>:)
@@ -59,12 +57,12 @@ import Rel8.Table.Alternative
5957import Rel8.Table.Bool ( bool )
6058import Rel8.Table.Eq ( EqTable, eqTable )
6159import Rel8.Table.Ord ( OrdTable, ordTable )
62−import Rel8.Table.Projection ( Projectable, project )
63−import Rel8.Table.Nullify ( Nullify, aggregateNullify, guard )
64−import Rel8.Table.Serialize ( ToExprs )
60+import Rel8.Table.Recontextualize ( Recontextualize )
61+import Rel8.Table.Serialize ( FromExprs, ToExprs, fromResult, toResult )
62+import Rel8.Table.Tag ( Tag(..), fromExpr, fromName )
6563import Rel8.Table.Undefined ( undefined )
6664import Rel8.Type ( DBType )
67−import Rel8.Type.Tag ( MaybeTag( IsJust ) )
65+import Rel8.Type.Tag ( MaybeTag )
6866
6967-- semigroupoids
7068import Data.Functor.Apply ( Apply, (<.>) )
@@ -79,160 +77,135 @@ import Data.Functor.Bind ( Bind, (>>-) )
7977-- entirely of nulls, you can't distinguish if you matched an all null row, or
8078-- if the match failed. For this reason @MaybeTable@ contains an extra field -
8179-- a "nullTag" - to track whether or not the outer join produced any rows.
82−type MaybeTable :: K.Context -> Type -> Type
83−data MaybeTable context a = MaybeTable
84− { tag :: context (Maybe MaybeTag)
85− , just :: Nullify context a
80+type MaybeTable :: Type -> Type
81+data MaybeTable a = MaybeTable
82+ { tag :: Tag "isJust" (Maybe MaybeTag)
83+ , just :: a
8684 }
8785 deriving stock Functor
8886
8987
90−instance Projectable (MaybeTable context) where
91− project f (MaybeTable tag a) = MaybeTable tag (project f a)
92−
93−
94−instance context ~ Expr => Apply (MaybeTable context) where
95− MaybeTable tag f <.> MaybeTable tag' a = MaybeTable (tag <> tag') (f <.> a)
88+instance Apply MaybeTable where
89+ MaybeTable tag f <.> MaybeTable tag' a = MaybeTable (tag <> tag') (f a)
9690
9791
9892-- | Has the same behavior as the @Applicative@ instance for @Maybe@. See also:
9993-- 'Rel8.traverseMaybeTable'.
100−instance context ~ Expr => Applicative (MaybeTable context) where
94+instance Applicative MaybeTable where
10195 (<*>) = (<.>)
10296 pure = justTable
10397
10498
105−instance context ~ Expr => Bind (MaybeTable context) where
106− MaybeTable tag a >>- f = case f (extract a) of
99+instance Bind MaybeTable where
100+ MaybeTable tag a >>- f = case f a of
107101 MaybeTable tag' b -> MaybeTable (tag <> tag') b
108102
109103
110104-- | Has the same behavior as the @Monad@ instance for @Maybe@.
111−instance context ~ Expr => Monad (MaybeTable context) where
105+instance Monad MaybeTable where
112106 (>>=) = (>>-)
113107
114108
115−instance context ~ Expr => AltTable (MaybeTable context) where
116− ma <|>: mb = bool ma mb (isNothingTable ma)
109+instance AltTable MaybeTable where
110+ ma@(MaybeTable tag a) <|>: MaybeTable tag' b = MaybeTable
111+ { tag = (tag <> tag')
112+ { expr = boolExpr (expr tag) (expr tag') condition
113+ }
114+ , just = bool a b condition
115+ }
116+ where
117+ condition = isNothingTable ma
117118
118119
119−instance context ~ Expr => AlternativeTable (MaybeTable context) where
120+instance AlternativeTable MaybeTable where
120121 emptyTable = nothingTable
121122
122123
123−instance (context ~ Expr, Table Expr a, Semigroup a) =>
124− Semigroup (MaybeTable context a)
125− where
124+instance (Table Expr a, Semigroup a) => Semigroup (MaybeTable a) where
126125 ma <> mb = maybeTable mb (\a -> maybeTable ma (justTable . (a <>)) mb) ma
127126
128127
129−instance (context ~ Expr, Table Expr a, Semigroup a) =>
130− Monoid (MaybeTable context a)
131− where
128+instance (Table Expr a, Semigroup a) => Monoid (MaybeTable a) where
132129 mempty = nothingTable
133130
134131
135−instance (Table context a, Reifiable context, context ~ context') =>
136− Table context' (MaybeTable context a)
132+instance
133+ ( Table context a
134+ , Labelable context, Nullifiable context
135+ , ConstrainTag context MaybeTag
136+ ) => Table context (MaybeTable a)
137137 where
138− type Columns (MaybeTable context a) = HMaybeTable (Columns a)
139− type Context (MaybeTable context a) = Context a
140− type FromExprs (MaybeTable context a) = Maybe (FromExprs a)
141− type Transpose to (MaybeTable context a) = MaybeTable to (Transpose to a)
142−
143− toColumns MaybeTable {tag, just} = HMaybeTable
144− { htag = hlabel $ HIdentity tag
145− , hjust = hlabel $ guard tag isJust isNonNull $ toColumns just
146− }
138+ type Columns (MaybeTable a) = HMaybeTable (Columns a)
139+ type Context (MaybeTable a) = Context a
147140
148− fromColumns HMaybeTable {htag, hjust} = MaybeTable
149− { tag = unHIdentity $ hunlabel htag
150− , just = fromColumns $ hunlabel hjust
151− }
141+ toColumns = toColumns1 toColumns
142+ fromColumns = fromColumns1 fromColumns
143+ reify = fmap fmap reify
144+ unreify = fmap fmap unreify
152145
153− toResult ma = HMaybeTable
154− { htag = hlabel (HIdentity (Identity (IsJust <$ ma)))
155− , hjust = hlabel (toResult @_ @(Nullify context a) ma)
156− }
157146
158− fromResult HMaybeTable {htag, hjust} = case hunlabel htag of
159− HIdentity (Identity tag) -> tag $>
160− fromMaybe err (fromResult @_ @(Nullify context a) (hunlabel hjust))
161− where
162− err = error "Maybe.fromColumns: mismatch between tag and data"
147+instance
148+ ( Labelable from, Nullifiable from, ConstrainTag from MaybeTag
149+ , Labelable to, Nullifiable to, ConstrainTag to MaybeTag
150+ , Recontextualize from to a b
151+ )
152+ => Recontextualize from to (MaybeTable a) (MaybeTable b)
163153
164154
165−instance (EqTable a, context ~ Expr) => EqTable (MaybeTable context a) where
166− eqTable = HMaybeTable
167− { htag = hlabel (HIdentity Dict)
168− , hjust = hlabel (eqTable @(Nullify context a))
169− }
155+instance EqTable a => EqTable (MaybeTable a) where
156+ eqTable = toColumns1 id (justTable (eqTable @a))
170157
171158
172−instance (OrdTable a, context ~ Expr) => OrdTable (MaybeTable context a) where
173− ordTable = HMaybeTable
174− { htag = hlabel (HIdentity Dict)
175− , hjust = hlabel (ordTable @(Nullify context a))
176− }
159+instance OrdTable a => OrdTable (MaybeTable a) where
160+ ordTable = toColumns1 id (justTable (ordTable @a))
161+
177162
163+type instance FromExprs (MaybeTable a) = Maybe (FromExprs a)
178164
179−instance (ToExprs exprs a, context ~ Expr) =>
180− ToExprs (MaybeTable context exprs) (Maybe a)
165+
166+instance ToExprs exprs a => ToExprs (MaybeTable exprs) (Maybe a) where
167+ fromResult = fmap (fromResult @exprs) . fromColumns
168+ toResult = toColumns . fmap (toResult @exprs)
181169
182170
183171-- | Check if a @MaybeTable@ is absent of any row. Like 'Data.Maybe.isNothing'.
184−isNothingTable :: MaybeTable Expr a -> Expr Bool
185−isNothingTable (MaybeTable tag _) = isNull tag
172+isNothingTable :: MaybeTable a -> Expr Bool
173+isNothingTable (MaybeTable tag _) = isNull (expr tag)
186174
187175
188176-- | Check if a @MaybeTable@ contains a row. Like 'Data.Maybe.isJust'.
189−isJustTable :: MaybeTable Expr a -> Expr Bool
190−isJustTable (MaybeTable tag _) = isNonNull tag
177+isJustTable :: MaybeTable a -> Expr Bool
178+isJustTable (MaybeTable tag _) = isNonNull (expr tag)
191179
192180
193181-- | Perform case analysis on a 'MaybeTable'. Like 'maybe'.
194−maybeTable :: Table Expr b => b -> (a -> b) -> MaybeTable Expr a -> b
195−maybeTable b f ma@(MaybeTable _ a) = bool (f (extract a)) b (isNothingTable ma)
182+maybeTable :: Table Expr b => b -> (a -> b) -> MaybeTable a -> b
183+maybeTable b f ma@(MaybeTable _ a) = bool (f a) b (isNothingTable ma)
196184{-# INLINABLE maybeTable #-}
197185
198186
199187-- | The null table. Like 'Nothing'.
200−nothingTable :: Table Expr a => MaybeTable Expr a
201−nothingTable = MaybeTable null (pure undefined)
188+nothingTable :: Table Expr a => MaybeTable a
189+nothingTable = MaybeTable (fromExpr null) undefined
202190
203191
204192-- | Lift any table into 'MaybeTable'. Like 'Just'. Note you can also use
205193-- 'pure'.
206−justTable :: a -> MaybeTable Expr a
207−justTable = MaybeTable mempty . pure
208−
209−
210−-- | 'Data.Maybe.fromMaybe' for 'MaybeTable's.
211−fromMaybeTable :: Table Expr a => a -> MaybeTable Expr a -> a
212−fromMaybeTable fallback = maybeTable fallback id
194+justTable :: a -> MaybeTable a
195+justTable = MaybeTable (fromExpr mempty)
213196
214197
215198-- | Project a single expression out of a 'MaybeTable'. You can think of this
216199-- operator like the '$' operator, but it also has the ability to return
217200-- @null@.
218201($?) :: forall a b. Sql DBType b
219− => (a -> Expr b) -> MaybeTable Expr a -> Expr (N.Nullify b)
202+ => (a -> Expr b) -> MaybeTable a -> Expr (Nullify b)
220203f $? ma@(MaybeTable _ a) = case nullable @b of
221− Null -> boolExpr (f (extract a)) null (isNothingTable ma)
222− NotNull -> boolExpr (nullify (f (extract a))) null (isNothingTable ma)
204+ Null -> boolExpr (f a) null (isNothingTable ma)
205+ NotNull -> boolExpr (nullify (f a)) null (isNothingTable ma)
223206infixl 4 $?
224207
225208
226−-- | Lift an aggregating function to operate on a 'MaybeTable'.
227−-- @nothingTable@s and @justTable@s are grouped separately.
228−aggregateMaybeTable :: ()
229− => (exprs -> aggregates)
230− -> MaybeTable Expr exprs
231− -> MaybeTable Aggregate aggregates
232−aggregateMaybeTable f (MaybeTable tag a) =
233− MaybeTable (groupByExpr tag) (aggregateNullify f a)
234−
235−
236209-- | Construct a 'MaybeTable' in the 'Name' context. This can be useful if you
237210-- have a 'MaybeTable' that you are storing in a table and need to construct a
238211-- 'TableSchema'.
@@ -242,5 +215,40 @@ nameMaybeTable
242215 -- 'nothingTable'.
243216 -> a
244217 -- ^ Names of the columns in @a@.
245− -> MaybeTable Name a
246−nameMaybeTable tag = MaybeTable tag . pure
218+ -> MaybeTable a
219+nameMaybeTable = MaybeTable . fromName
220+
221+
222+toColumns1 ::
223+ ( HTable t
224+ , HConstrainTag context MaybeTag
225+ , HLabelable context
226+ , HNullifiable context
227+ )
228+ => (a -> t context)
229+ -> MaybeTable a
230+ -> HMaybeTable t context
231+toColumns1 f MaybeTable {tag, just} = HMaybeTable
232+ { htag
233+ , hjust = hlabel hlabeler $ hnullify (hnullifier tag isNonNull) $ f just
234+ }
235+ where
236+ htag = HIdentity (hencodeTag tag)
237+
238+
239+fromColumns1 ::
240+ ( HTable t
241+ , HConstrainTag context MaybeTag
242+ , HLabelable context
243+ , HNullifiable context
244+ )
245+ => (t context -> a)
246+ -> HMaybeTable t context
247+ -> MaybeTable a
248+fromColumns1 f HMaybeTable {htag = HIdentity htag, hjust} = MaybeTable
249+ { tag
250+ , just = f $ runIdentity $
251+ hunnullify (\a -> pure . hunnullifier a) (hunlabel hunlabeler hjust)
252+ }
253+ where
254+ tag = hdecodeTag htag
src/Rel8/Table/Name.hs+22−17
@@ -1,17 +1,14 @@
1−{-# language DataKinds #-}
21{-# language FlexibleContexts #-}
3−{-# language FlexibleInstances #-}
4−{-# language MultiParamTypeClasses #-}
52{-# language NamedFieldPuns #-}
63{-# language ScopedTypeVariables #-}
74{-# language TypeApplications #-}
85{-# language TypeFamilies #-}
9−{-# language UndecidableInstances #-}
106{-# language ViewPatterns #-}
117
128module Rel8.Table.Name
139 ( namesFromLabels
1410 , namesFromLabelsWith
11+ , showExprs
1512 , showLabels
1613 , showNames
1714 )
@@ -20,15 +17,20 @@ where
2017-- base
2118import Data.Foldable ( fold )
2219import Data.Functor.Const ( Const( Const ), getConst )
23−import Data.List.NonEmpty ( NonEmpty, intersperse, nonEmpty )
24−import Data.Maybe ( fromMaybe )
20+import Data.List.NonEmpty ( NonEmpty, intersperse )
2521import Prelude
2622
23+-- opaleye
24+import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
25+
2726-- rel8
27+import Rel8.Expr ( Expr, Col( E ) )
28+import Rel8.Expr.Opaleye ( toPrimExpr )
29+import Rel8.Kind.Labels ( renderLabels )
2830import Rel8.Schema.HTable ( htabulate, htabulateA, hfield, hspecs )
29−import Rel8.Schema.Name ( Name( Name ) )
30−import Rel8.Schema.Spec ( Spec(..) )
31−import Rel8.Table ( Table(..) )
31+import Rel8.Schema.Name ( Name( Name ), Col( N ) )
32+import Rel8.Schema.Spec ( SSpec(..) )
33+import Rel8.Table ( Table, Columns, Context, fromColumns, toColumns )
3234
3335
3436-- | Construct a table in the 'Name' context containing the names of all
@@ -60,20 +62,23 @@ namesFromLabelsWith :: Table Name a
6062 => (NonEmpty String -> String) -> a
6163namesFromLabelsWith f = fromColumns $ htabulate $ \field ->
6264 case hfield hspecs field of
63− Spec {labels} -> Name (f (renderLabels labels))
65+ SSpec {labels} -> N (Name (f (renderLabels labels)))
66+
67+
68+showExprs :: Table Expr a => a -> [(String, Opaleye.PrimExpr)]
69+showExprs as = case (namesFromLabels, toColumns as) of
70+ (names, exprs) -> getConst $ htabulateA $ \field ->
71+ case (hfield names field, hfield exprs field) of
72+ (N (Name name), E expr) -> Const [(name, toPrimExpr expr)]
6473
6574
6675showLabels :: forall a. Table (Context a) a => a -> [NonEmpty String]
6776showLabels _ = getConst $
6877 htabulateA @(Columns a) $ \field -> case hfield hspecs field of
69− Spec {labels} -> Const (pure (renderLabels labels))
78+ SSpec {labels} -> Const [renderLabels labels]
7079
7180
72−showNames :: forall a. Table Name a => a -> NonEmpty String
81+showNames :: forall a. Table Name a => a -> [String]
7382showNames (toColumns -> names) = getConst $
7483 htabulateA @(Columns a) $ \field -> case hfield names field of
75− Name name -> Const (pure name)
76−
77−
78−renderLabels :: [String] -> NonEmpty String
79−renderLabels labels = fromMaybe (pure "anon") (nonEmpty labels )
84+ N (Name name) -> Const [name]
src/Rel8/Table/NonEmpty.hs+54−66
@@ -1,4 +1,3 @@
1−{-# language DataKinds #-}
21{-# language FlexibleContexts #-}
32{-# language FlexibleInstances #-}
43{-# language MultiParamTypeClasses #-}
@@ -11,9 +10,7 @@
1110
1211module Rel8.Table.NonEmpty
1312 ( NonEmptyTable(..)
14− , ($+)
15− , nonEmptyTable
16− , nameNonEmptyTable
13+ , nonEmptyTable, nameNonEmptyTable
1714 )
1815where
1916
@@ -21,111 +18,102 @@ where
2118import Data.Functor.Identity ( Identity( Identity ) )
2219import Data.Kind ( Type )
2320import Data.List.NonEmpty ( NonEmpty )
24−import Prelude hiding ( id )
21+import Data.Type.Equality ( (:~:)( Refl ) )
22+import Prelude
2523
2624-- rel8
27−import Rel8.Expr ( Expr )
25+import Rel8.Expr ( Expr, Col( E, unE ) )
2826import Rel8.Expr.Array ( sappend1, snonEmptyOf )
2927import Rel8.Schema.Dict ( Dict( Dict ) )
3028import Rel8.Schema.HTable.NonEmpty ( HNonEmptyTable )
31−import Rel8.Schema.HTable.Vectorize
32− ( hvectorize, hunvectorize
33− , happend
34− , hproject, hcolumn
35− )
36−import qualified Rel8.Schema.Kind as K
37−import Rel8.Schema.Name ( Name( Name ) )
29+import Rel8.Schema.HTable.Vectorize ( happend, hvectorize )
30+import Rel8.Schema.Name ( Col( N ), Name( Name ) )
3831import Rel8.Schema.Null ( Nullity( Null, NotNull ) )
39−import Rel8.Schema.Result ( vectorizer, unvectorizer )
40−import Rel8.Schema.Spec ( Spec(..) )
32+import Rel8.Schema.Reify ( hreify, hunreify )
33+import Rel8.Schema.Spec ( SSpec(..) )
34+import Rel8.Schema.Spec.ConstrainDBType ( dbTypeDict, dbTypeNullity )
4135import Rel8.Table
4236 ( Table, Context, Columns, fromColumns, toColumns
43− , FromExprs, fromResult, toResult
44− , Transpose
37+ , reify, unreify
4538 )
4639import Rel8.Table.Alternative ( AltTable, (<|>:) )
4740import Rel8.Table.Eq ( EqTable, eqTable )
4841import Rel8.Table.Ord ( OrdTable, ordTable )
49−import Rel8.Table.Projection
50− ( Projectable, Projecting, Projection, project, apply
51− )
52−import Rel8.Table.Serialize ( ToExprs )
42+import Rel8.Table.Recontextualize ( Recontextualize )
43+import Rel8.Table.Serialize ( FromExprs, ToExprs, fromResult, toResult )
44+import Rel8.Table.Unreify ( Unreifies )
5345
5446
5547-- | A @NonEmptyTable@ value contains one or more instances of @a@. You
5648-- construct @NonEmptyTable@s with 'Rel8.some' or 'nonEmptyAgg'.
57−type NonEmptyTable :: K.Context -> Type -> Type
58−newtype NonEmptyTable context a =
59− NonEmptyTable (HNonEmptyTable (Columns a) (Context a))
60−
61−
62−instance Projectable (NonEmptyTable context) where
63− project f (NonEmptyTable a) = NonEmptyTable (hproject (apply f) a)
49+type NonEmptyTable :: Type -> Type
50+newtype NonEmptyTable a =
51+ NonEmptyTable (HNonEmptyTable (Columns a) (Col (Context a)))
6452
6553
66−instance (Table context a, context ~ context') =>
67− Table context' (NonEmptyTable context a)
54+instance (Table context a, Unreifies context a) =>
55+ Table context (NonEmptyTable a)
6856 where
69− type Columns (NonEmptyTable context a) = HNonEmptyTable (Columns a)
70− type Context (NonEmptyTable context a) = Context a
71− type FromExprs (NonEmptyTable context a) = NonEmpty (FromExprs a)
72− type Transpose to (NonEmptyTable context a) =
73− NonEmptyTable to (Transpose to a)
57+ type Columns (NonEmptyTable a) = HNonEmptyTable (Columns a)
58+ type Context (NonEmptyTable a) = Context a
7459
7560 fromColumns = NonEmptyTable
7661 toColumns (NonEmptyTable a) = a
77− fromResult = fmap (fromResult @_ @a) . hunvectorize unvectorizer
78− toResult = hvectorize vectorizer . fmap (toResult @_ @a)
7962
63+ reify Refl (NonEmptyTable a) = NonEmptyTable (hreify a)
64+ unreify Refl (NonEmptyTable a) = NonEmptyTable (hunreify a)
8065
81−instance (EqTable a, context ~ Expr) =>
82− EqTable (NonEmptyTable context a)
83− where
66+
67+instance
68+ ( Unreifies from a, Unreifies to b
69+ , Recontextualize from to a b
70+ )
71+ => Recontextualize from to (NonEmptyTable a) (NonEmptyTable b)
72+
73+
74+instance EqTable a => EqTable (NonEmptyTable a) where
8475 eqTable =
8576 hvectorize
86− (\Spec {nullity} (Identity Dict) -> case nullity of
87− Null -> Dict
88− NotNull -> Dict)
77+ (\SSpec {} (Identity dict) -> case dbTypeDict dict of
78+ Dict -> case dbTypeNullity dict of
79+ Null -> Dict
80+ NotNull -> Dict)
8981 (Identity (eqTable @a))
9082
9183
92−instance (OrdTable a, context ~ Expr) =>
93− OrdTable (NonEmptyTable context a)
94− where
84+instance OrdTable a => OrdTable (NonEmptyTable a) where
9585 ordTable =
9686 hvectorize
97− (\Spec {nullity} (Identity Dict) -> case nullity of
98− Null -> Dict
99− NotNull -> Dict)
87+ (\SSpec {} (Identity dict) -> case dbTypeDict dict of
88+ Dict -> case dbTypeNullity dict of
89+ Null -> Dict
90+ NotNull -> Dict)
10091 (Identity (ordTable @a))
10192
10293
103−instance (ToExprs exprs a, context ~ Expr) =>
104− ToExprs (NonEmptyTable context exprs) (NonEmpty a)
94+type instance FromExprs (NonEmptyTable a) = NonEmpty (FromExprs a)
10595
10696
107−instance context ~ Expr => AltTable (NonEmptyTable context) where
108− (<|>:) = (<>)
97+instance ToExprs exprs a => ToExprs (NonEmptyTable exprs) (NonEmpty a)
98+ where
99+ fromResult = fmap (fromResult @exprs) . fromColumns
100+ toResult = toColumns . fmap (toResult @exprs)
109101
110102
111−instance (Table Expr a, context ~ Expr) => Semigroup (NonEmptyTable context a)
112− where
113− NonEmptyTable as <> NonEmptyTable bs = NonEmptyTable $
114− happend (const sappend1) as bs
103+instance AltTable NonEmptyTable where
104+ (<|>:) = (<>)
115105
116106
117−-- | Project a single expression out of a 'NonEmptyTable'.
118−($+) :: Projecting a (Expr b)
119− => Projection a (Expr b) -> NonEmptyTable Expr a -> Expr (NonEmpty b)
120−f $+ NonEmptyTable a = hcolumn $ hproject (apply f) a
121−infixl 4 $+
107+instance Table Expr a => Semigroup (NonEmptyTable a) where
108+ NonEmptyTable as <> NonEmptyTable bs = NonEmptyTable $
109+ happend (\_ _ (E a) (E b) -> E (sappend1 a b)) as bs
122110
123111
124112-- | Construct a @NonEmptyTable@ from a non-empty list of expressions.
125−nonEmptyTable :: Table Expr a => NonEmpty a -> NonEmptyTable Expr a
113+nonEmptyTable :: Table Expr a => NonEmpty a -> NonEmptyTable a
126114nonEmptyTable =
127115 NonEmptyTable .
128− hvectorize (\Spec {info} -> snonEmptyOf info) .
116+ hvectorize (\SSpec {info} -> E . snonEmptyOf info . fmap unE) .
129117 fmap toColumns
130118
131119
@@ -135,9 +123,9 @@ nonEmptyTable =
135123nameNonEmptyTable
136124 :: Table Name a
137125 => a -- ^ The names of the columns of elements of the list.
138− -> NonEmptyTable Name a
126+ -> NonEmptyTable a
139127nameNonEmptyTable =
140128 NonEmptyTable .
141− hvectorize (\_ (Identity (Name a)) -> Name a) .
129+ hvectorize (\_ (Identity (N (Name a))) -> N (Name a)) .
142130 pure .
143131 toColumns
src/Rel8/Table/Null.hsdeleted−147
@@ -1,147 +0,0 @@
1−{-# language DataKinds #-}
2−{-# language FlexibleContexts #-}
3−{-# language FlexibleInstances #-}
4−{-# language MultiParamTypeClasses #-}
5−{-# language ScopedTypeVariables #-}
6−{-# language StandaloneKindSignatures #-}
7−{-# language TypeApplications #-}
8−{-# language TypeFamilies #-}
9−{-# language UndecidableInstances #-}
10−
11−module Rel8.Table.Null
12− ( NullTable(..)
13− , nullableTable, nullTable, nullifyTable, unsafeUnnullifyTable
14− , isNullTable, isNonNullTable
15− , nameNullTable
16− , toMaybeTable, toNullTable
17− )
18−where
19−
20−-- base
21−import Data.Kind ( Type )
22−import Prelude hiding ( null, undefined )
23−
24−-- comonad
25−import Control.Comonad ( extract )
26−
27−-- rel8
28−import Rel8.Expr ( Expr )
29−import Rel8.Expr.Bool ( not_ )
30−import Rel8.Kind.Context ( Reifiable )
31−import qualified Rel8.Schema.Kind as K
32−import Rel8.Schema.Name ( Name )
33−import Rel8.Table
34− ( Table, Columns, Context, fromColumns, toColumns
35− , FromExprs, fromResult, toResult
36− , Transpose
37− )
38−import Rel8.Table.Alternative
39− ( AltTable, (<|>:)
40− , AlternativeTable, emptyTable
41− )
42−import Rel8.Table.Bool ( bool )
43−import Rel8.Table.Eq ( EqTable, eqTable )
44−import Rel8.Table.Maybe ( MaybeTable, justTable, maybeTable, nothingTable )
45−import Rel8.Table.Nullify ( Nullify, isNull )
46−import Rel8.Table.Ord ( OrdTable, ordTable )
47−import Rel8.Table.Projection ( Projectable, project )
48−import Rel8.Table.Serialize ( ToExprs )
49−import Rel8.Table.Undefined ( undefined )
50−
51−
52−-- | @NullTable t@ is the table @t@, but where all the columns in @t@ have the
53−-- possibility of being 'Rel8.null'. This is very similar to
54−-- 'Rel8.MaybeTable', except that it does not use an extra tag field, so it
55−-- cannot distinguish between @Nothing@ and @Just Nothing@ if nested. In other
56−-- words, if all of the columns of the @t@ passed to @NullTable@ are already
57−-- nullable, then @NullTable@ has no effect.
58−type NullTable :: K.Context -> Type -> Type
59−newtype NullTable context a = NullTable (Nullify context a)
60−
61−
62−instance Projectable (NullTable context) where
63− project f (NullTable a) = NullTable (project f a)
64−
65−
66−instance context ~ Expr => AltTable (NullTable context) where
67− ma <|>: mb = bool ma mb (isNullTable ma)
68−
69−
70−instance context ~ Expr => AlternativeTable (NullTable context) where
71− emptyTable = nullTable
72−
73−
74−instance (Table context a, Reifiable context, context ~ context') =>
75− Table context' (NullTable context a)
76− where
77− type Columns (NullTable context a) = Columns (Nullify context a)
78− type Context (NullTable context a) = Context (Nullify context a)
79− type FromExprs (NullTable context a) = FromExprs (Nullify context a)
80− type Transpose to (NullTable context a) = NullTable to (Transpose to a)
81−
82− toColumns (NullTable a) = toColumns a
83− fromColumns = NullTable . fromColumns
84−
85− toResult = toResult @_ @(Nullify context a)
86− fromResult = fromResult @_ @(Nullify context a)
87−
88−
89−instance (EqTable a, context ~ Expr) => EqTable (NullTable context a) where
90− eqTable = eqTable @(Nullify context a)
91−
92−
93−instance (OrdTable a, context ~ Expr) => OrdTable (NullTable context a) where
94− ordTable = ordTable @(Nullify context a)
95−
96−
97−instance (ToExprs exprs a, context ~ Expr) =>
98− ToExprs (NullTable context exprs) (Maybe a)
99−
100−
101−-- | Check if any of the non-nullable fields of @a@ are 'Rel8.null' under the
102−-- 'NullTable'. Returns 'Rel8.false' if @a@ has no non-nullable fields.
103−isNullTable :: Table Expr a => NullTable Expr a -> Expr Bool
104−isNullTable (NullTable a) = isNull a
105−
106−
107−-- | The inverse of 'isNullTable'.
108−isNonNullTable :: Table Expr a => NullTable Expr a -> Expr Bool
109−isNonNullTable = not_ . isNullTable
110−
111−
112−-- | Like 'Rel8.nullable'.
113−nullableTable :: (Table Expr a, Table Expr b)
114− => b -> (a -> b) -> NullTable Expr a -> b
115−nullableTable b f ma@(NullTable a) = bool (f (extract a)) b (isNullTable ma)
116−
117−
118−-- | The null table. Like 'Rel8.null'.
119−nullTable :: Table Expr a => NullTable Expr a
120−nullTable = NullTable (pure undefined)
121−
122−
123−-- | Lift any table into 'NullTable'. Like 'Rel8.nullify'.
124−nullifyTable :: a -> NullTable Expr a
125−nullifyTable = NullTable . pure
126−
127−
128−unsafeUnnullifyTable :: NullTable Expr a -> a
129−unsafeUnnullifyTable (NullTable a) = extract a
130−
131−
132−-- | Construct a 'NullTable' in the 'Name' context. This can be useful if you
133−-- have a 'NullTable' that you are storing in a table and need to construct a
134−-- 'TableSchema'.
135−nameNullTable :: a -> NullTable Name a
136−nameNullTable = NullTable . pure
137−
138−
139−-- | Convert a 'NullTable' to a 'MaybeTable'.
140−toMaybeTable :: Table Expr a => NullTable Expr a -> MaybeTable Expr a
141−toMaybeTable = nullableTable nothingTable justTable
142−
143−
144−-- | Convert a 'MaybeTable' to a 'NullTable'. Note that if the underlying @a@
145−-- has no non-nullable fields, this is a lossy conversion.
146−toNullTable :: Table Expr a => MaybeTable Expr a -> NullTable Expr a
147−toNullTable = maybeTable nullTable nullifyTable
src/Rel8/Table/Nullify.hsdeleted−211
@@ -1,211 +0,0 @@
1−{-# language DataKinds #-}
2−{-# language FlexibleContexts #-}
3−{-# language FlexibleInstances #-}
4−{-# language LambdaCase #-}
5−{-# language MultiParamTypeClasses #-}
6−{-# language NamedFieldPuns #-}
7−{-# language ScopedTypeVariables #-}
8−{-# language StandaloneKindSignatures #-}
9−{-# language TypeApplications #-}
10−{-# language TypeFamilies #-}
11−{-# language UndecidableInstances #-}
12−
13−module Rel8.Table.Nullify
14− ( Nullify
15− , aggregateNullify
16− , guard
17− , isNull
18− )
19−where
20−
21−-- base
22−import Control.Applicative ( liftA2 )
23−import Data.Functor.Const ( Const( Const ), getConst )
24−import Data.Functor.Identity ( runIdentity )
25−import Data.Kind ( Type )
26−import Prelude
27−
28−-- comonad
29−import Control.Comonad ( Comonad, duplicate, extract, ComonadApply, (<@>) )
30−
31−-- rel8
32−import Rel8.Aggregate ( Aggregate )
33−import Rel8.Expr ( Expr )
34−import Rel8.Expr.Bool ( (||.), false )
35−import qualified Rel8.Expr.Null as Expr
36−import Rel8.Kind.Context ( Reifiable, contextSing )
37−import Rel8.Schema.Context.Nullify
38− ( Nullifiability( NAggregate, NExpr )
39− , NonNullifiability
40− , Nullifiable, nullifiability
41− , nullifiableOrNot, absurd
42− , guarder
43− , nullifier
44− , unnullifier
45− )
46−import Rel8.Schema.Dict ( Dict( Dict ) )
47−import Rel8.Schema.HTable ( HTable )
48−import Rel8.Schema.HTable.Nullify
49− ( HNullify, hnulls, hnullify, hunnullify
50− , hguard
51− , hproject
52− )
53−import qualified Rel8.Schema.Kind as K
54−import Rel8.Schema.Null ( Nullity( NotNull, Null ) )
55−import qualified Rel8.Schema.Result as R
56−import Rel8.Table
57− ( Table, Columns, Context, toColumns, fromColumns
58− , FromExprs, fromResult, toResult
59− , Transpose
60− )
61−import Rel8.Schema.Spec ( Spec( Spec, nullity ) )
62−import Rel8.Table.Eq ( EqTable, eqTable )
63−import Rel8.Table.Ord ( OrdTable, ordTable )
64−import Rel8.Table.Projection ( Projectable, apply, project )
65−
66−-- semigroupoids
67−import Data.Functor.Apply ( Apply, (<.>), liftF2 )
68−import Data.Functor.Bind ( Bind, (>>-) )
69−import Data.Functor.Extend ( Extend, duplicated )
70−
71−
72−type Nullify :: K.Context -> Type -> Type
73−data Nullify context a
74− = Table (Nullifiability context) a
75− | Fields (NonNullifiability context) (HNullify (Columns a) (Context a))
76−
77−
78−instance Projectable (Nullify context) where
79− project f = \case
80− Table nullifiable a -> Table nullifiable (fromColumns (apply f (toColumns a)))
81− Fields nonNullifiable a -> Fields nonNullifiable (hproject (apply f) a)
82−
83−
84−instance Nullifiable context => Functor (Nullify context) where
85− fmap f = \case
86− Table nullifiable a -> Table nullifiable (f a)
87− Fields notNullifiable _ -> absurd nullifiability notNullifiable
88−
89−
90−instance Nullifiable context => Foldable (Nullify context) where
91− foldMap f = \case
92− Table _ a -> f a
93− Fields notNullifiable _ -> absurd nullifiability notNullifiable
94−
95−
96−instance Nullifiable context => Traversable (Nullify context) where
97− traverse f = \case
98− Table nullifiable a -> Table nullifiable <$> f a
99− Fields notNullifiable _ -> absurd nullifiability notNullifiable
100−
101−
102−instance Nullifiable context => Apply (Nullify context) where
103− liftF2 f = \case
104− Table nullifiable a -> \case
105− Table _ b -> Table nullifiable (f a b)
106− Fields notNullifiable _ -> absurd nullifiable notNullifiable
107− Fields notNullifiable _ -> absurd nullifiability notNullifiable
108−
109−
110−instance Nullifiable context => Applicative (Nullify context) where
111− pure = Table nullifiability
112− liftA2 = liftF2
113−
114−
115−instance Nullifiable context => Bind (Nullify context) where
116− Table _ a >>- f = f a
117− Fields notNullifiable _ >>- _ = absurd nullifiability notNullifiable
118−
119−
120−instance Nullifiable context => Monad (Nullify context) where
121− (>>=) = (>>-)
122−
123−
124−instance Nullifiable context => Extend (Nullify context) where
125− duplicated = \case
126− Table nullifiable a -> Table nullifiable (Table nullifiable a)
127− Fields notNullifiable _ -> absurd nullifiability notNullifiable
128−
129−
130−instance Nullifiable context => Comonad (Nullify context) where
131− extract = \case
132− Table _ a -> a
133− Fields notNullifiable _ -> absurd nullifiability notNullifiable
134− duplicate = duplicated
135−
136−
137−instance Nullifiable context => ComonadApply (Nullify context) where
138− (<@>) = (<.>)
139−
140−
141−instance (Table context a, Reifiable context, context ~ context') =>
142− Table context' (Nullify context a)
143− where
144− type Columns (Nullify context a) = HNullify (Columns a)
145− type Context (Nullify context a) = Context a
146− type FromExprs (Nullify context a) = Maybe (FromExprs a)
147− type Transpose to (Nullify context a) = Nullify to (Transpose to a)
148−
149− fromColumns = case nullifiableOrNot contextSing of
150− Left notNullifiable -> Fields notNullifiable
151− Right nullifiable ->
152− Table nullifiable .
153− fromColumns .
154− runIdentity .
155− hunnullify (\spec -> pure . unnullifier nullifiable spec)
156−
157− toColumns = \case
158− Table nullifiable a -> hnullify (nullifier nullifiable) (toColumns a)
159− Fields _ a -> a
160−
161− fromResult = fmap (fromResult @_ @a) . hunnullify R.unnullifier
162−
163− toResult =
164− maybe (hnulls (const R.null)) (hnullify R.nullifier) .
165− fmap (toResult @_ @a)
166−
167−
168−instance (EqTable a, context ~ Expr) => EqTable (Nullify context a) where
169− eqTable = hnullify (\_ Dict -> Dict) (eqTable @a)
170−
171−
172−instance (OrdTable a, context ~ Expr) => OrdTable (Nullify context a) where
173− ordTable = hnullify (\_ Dict -> Dict) (ordTable @a)
174−
175−
176−aggregateNullify :: ()
177− => (exprs -> aggregates)
178− -> Nullify Expr exprs
179− -> Nullify Aggregate aggregates
180−aggregateNullify f = \case
181− Table _ a -> Table NAggregate (f a)
182− Fields notNullifiable _ -> absurd NExpr notNullifiable
183−
184−
185−guard :: (Reifiable context, HTable t)
186− => context tag
187− -> (tag -> Bool)
188− -> (Expr tag -> Expr Bool)
189− -> HNullify t context
190− -> HNullify t context
191−guard tag isNonNull isNonNullExpr =
192− hguard (guarder contextSing tag isNonNull isNonNullExpr)
193−
194−
195−isNull :: forall a. Table Expr a => Nullify Expr a -> Expr Bool
196−isNull =
197− maybe false getAny .
198− getConst .
199− hunnullify (\Spec {nullity} a -> Const $ case nullity of
200− NotNull -> Just $ Any $ Expr.isNull a
201− Null -> Nothing) .
202− toColumns
203−
204−
205−newtype Any = Any
206− { getAny :: Expr Bool
207− }
208−
209−
210−instance Semigroup Any where
211− Any a <> Any b = Any (a ||. b)
src/Rel8/Table/Opaleye.hs+64−78
@@ -1,150 +1,136 @@
11{-# language BlockArguments #-}
2−{-# language DataKinds #-}
32{-# language DisambiguateRecordFields #-}
43{-# language FlexibleContexts #-}
4+{-# language LambdaCase #-}
55{-# language NamedFieldPuns #-}
6−{-# language RankNTypes #-}
76{-# language TypeFamilies #-}
87{-# language ViewPatterns #-}
98
10−{-# options_ghc -Wno-deprecations #-}
11−
129module Rel8.Table.Opaleye
1310 ( aggregator
14− , attributes
1511 , binaryspec
1612 , distinctspec
17− , exprs
18− , exprsWithNames
1913 , table
2014 , tableFields
2115 , unpackspec
2216 , valuesspec
23− , view
2417 , castTable
2518 )
2619where
2720
2821-- base
29−import Data.Functor.Const ( Const( Const ), getConst )
30−import Data.List.NonEmpty ( NonEmpty )
31−import Prelude
22+import Prelude hiding ( undefined )
3223
3324-- opaleye
34−import qualified Opaleye.Adaptors as Opaleye
35−import qualified Opaleye.Field as Opaleye ( Field_ )
3625import qualified Opaleye.Internal.Aggregate as Opaleye
26+import qualified Opaleye.Internal.Binary as Opaleye
27+import qualified Opaleye.Internal.Distinct as Opaleye
3728import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
29+import qualified Opaleye.Internal.PackMap as Opaleye
30+import qualified Opaleye.Internal.Unpackspec as Opaleye
3831import qualified Opaleye.Internal.Values as Opaleye
39−import qualified Opaleye.Table as Opaleye
32+import qualified Opaleye.Internal.Table as Opaleye
4033
4134-- profunctors
4235import Data.Profunctor ( dimap, lmap )
4336
4437-- rel8
45−import Rel8.Aggregate ( Aggregate( Aggregate ), Aggregates )
46−import Rel8.Expr ( Expr )
38+import Rel8.Aggregate ( Col( A ), Aggregate( Aggregate ), Aggregates )
39+import Rel8.Expr ( Expr, Col(..) )
4740import Rel8.Expr.Opaleye
4841 ( fromPrimExpr, toPrimExpr
49− , scastExpr, traverseFieldP
42+ , traversePrimExpr
43+ , fromColumn, toColumn
44+ , scastExpr
5045 )
51−import Rel8.Schema.HTable ( htabulateA, hfield, hspecs, htabulate,
52− htraverseP, htraversePWithField )
53−import Rel8.Schema.Name ( Name( Name ), Selects, ppColumn )
54−import Rel8.Schema.Spec ( Spec(..) )
55−import Rel8.Schema.Table ( TableSchema(..), ppTable )
46+import Rel8.Schema.HTable ( htabulateA, hfield, htraverse, hspecs, htabulate )
47+import Rel8.Schema.Name ( Col( N ), Name( Name ), Selects )
48+import Rel8.Schema.Spec ( SSpec(..) )
49+import Rel8.Schema.Table ( TableSchema(..) )
5650import Rel8.Table ( Table, fromColumns, toColumns )
57−import Rel8.Type.Information ( typeName )
51+import Rel8.Table.Undefined ( undefined )
5852
5953-- semigroupoids
6054import Data.Functor.Apply ( WrappedApplicative(..) )
61−import Data.Profunctor.Product ( ProductProfunctor )
6255
6356
6457aggregator :: Aggregates aggregates exprs => Opaleye.Aggregator aggregates exprs
65−aggregator = dimap toColumns fromColumns $
66− htraverseP $
67− lmap (\(Aggregate a) -> (a, ())) Opaleye.aggregatorApply
68−
69−
70−attributes :: Selects names exprs => TableSchema names -> exprs
71−attributes schema@TableSchema {columns} = fromColumns $ htabulate $ \field ->
72− case hfield (toColumns columns) field of
73− Name column -> fromPrimExpr $ Opaleye.ConstExpr $
74− Opaleye.OtherLit $
75− show (ppTable schema) <> "." <> show (ppColumn column)
76−
77−
78−fromOpaleyespec :: (ProductProfunctor p, Table Expr a)
79− => p (Opaleye.Field_ n x) (Opaleye.Field_ n x)
80− -> p a a
81−fromOpaleyespec x =
82− dimap toColumns fromColumns (htraverseP (traverseFieldP x))
58+aggregator = Opaleye.Aggregator $ Opaleye.PackMap $ \f aggregates ->
59+ fmap fromColumns $ unwrapApplicative $ htabulateA $ \field ->
60+ WrapApplicative $ case hfield (toColumns aggregates) field of
61+ A (Aggregate (Opaleye.Aggregator (Opaleye.PackMap inner))) ->
62+ E <$> inner f ()
8363
8464
8565binaryspec :: Table Expr a => Opaleye.Binaryspec a a
86−binaryspec = fromOpaleyespec Opaleye.binaryspecField
66+binaryspec = Opaleye.Binaryspec $ Opaleye.PackMap $ \f (as, bs) ->
67+ fmap fromColumns $ unwrapApplicative $ htabulateA $ \field ->
68+ WrapApplicative $
69+ case (hfield (toColumns as) field, hfield (toColumns bs) field) of
70+ (E a, E b) -> E . fromPrimExpr <$> f (toPrimExpr a, toPrimExpr b)
8771
8872
8973distinctspec :: Table Expr a => Opaleye.Distinctspec a a
90−distinctspec = fromOpaleyespec Opaleye.distinctspecField
91−
92−
93−exprs :: Table Expr a => a -> NonEmpty Opaleye.PrimExpr
94−exprs (toColumns -> as) = getConst $ htabulateA $ \field ->
95− case hfield as field of
96− expr -> Const (pure (toPrimExpr expr))
97−
98−
99−exprsWithNames :: Selects names exprs
100− => names -> exprs -> NonEmpty (String, Opaleye.PrimExpr)
101−exprsWithNames names as = getConst $ htabulateA $ \field ->
102− case (hfield (toColumns names) field, hfield (toColumns as) field) of
103− (Name name, expr) -> Const (pure (name, toPrimExpr expr))
74+distinctspec =
75+ Opaleye.Distinctspec $ Opaleye.Aggregator $ Opaleye.PackMap $ \f ->
76+ fmap fromColumns .
77+ unwrapApplicative .
78+ htraverse
79+ (\(E a) ->
80+ WrapApplicative $ E . fromPrimExpr <$> f (Nothing, toPrimExpr a)) .
81+ toColumns
10482
10583
106−table :: Selects names exprs => TableSchema names -> Opaleye.Table exprs exprs
84+table ::Selects names exprs => TableSchema names -> Opaleye.Table exprs exprs
10785table (TableSchema name schema columns) =
10886 case schema of
109− Nothing -> Opaleye.table name (tableFields columns)
110− Just schemaName -> Opaleye.tableWithSchema schemaName name (tableFields columns)
87+ Nothing -> Opaleye.Table name (tableFields columns)
88+ Just schemaName -> Opaleye.TableWithSchema schemaName name (tableFields columns)
11189
11290
113−tableFields :: Selects names exprs
91+tableFields ::Selects names exprs
11492 => names -> Opaleye.TableFields exprs exprs
11593tableFields (toColumns -> names) = dimap toColumns fromColumns $
11694 unwrapApplicative $ htabulateA $ \field -> WrapApplicative $
11795 case hfield names field of
11896 name -> lmap (`hfield` field) (go name)
11997 where
120− go :: Name a -> Opaleye.TableFields (Expr a) (Expr a)
121− go (Name name) =
122− traverseFieldP $
123− Opaleye.requiredTableField name
98+ go :: Col Name spec -> Opaleye.TableFields (Col Expr spec) (Col Expr spec)
99+ go (N (Name name)) =
100+ lmap (\(E a) -> toColumn $ toPrimExpr a) $
101+ E . fromPrimExpr . fromColumn <$>
102+ Opaleye.requiredTableField name
124103
125104
126105unpackspec :: Table Expr a => Opaleye.Unpackspec a a
127−unpackspec = fromOpaleyespec Opaleye.unpackspecField
106+unpackspec = Opaleye.Unpackspec $ Opaleye.PackMap $ \f ->
107+ fmap fromColumns .
108+ unwrapApplicative .
109+ htraverse (\(E a) -> WrapApplicative $ E <$> traversePrimExpr f a) .
110+ toColumns
128111{-# INLINABLE unpackspec #-}
129112
130113
131−valuesspec :: Table Expr a => Opaleye.Valuesspec a a
132−valuesspec = dimap toColumns fromColumns $
133− htraversePWithField (traverseFieldP . Opaleye.valuesspecFieldType . typeName)
134− where typeName = Rel8.Type.Information.typeName . info . hfield hspecs
114+valuesspec :: Table Expr a => Opaleye.ValuesspecSafe a a
115+valuesspec = Opaleye.ValuesspecSafe (toPackMap undefined) unpackspec
135116
136117
137−view :: Selects names exprs => names -> exprs
138−view columns = fromColumns $ htabulate $ \field ->
139− case hfield (toColumns columns) field of
140− Name column -> fromPrimExpr $ Opaleye.BaseTableAttrExpr column
118+toPackMap :: Table Expr a
119+ => a -> Opaleye.PackMap Opaleye.PrimExpr Opaleye.PrimExpr () a
120+toPackMap as = Opaleye.PackMap $ \f () ->
121+ fmap fromColumns $
122+ unwrapApplicative .
123+ htraverse (\(E a) -> WrapApplicative $ E <$> traversePrimExpr f a) $
124+ toColumns as
141125
142126
143127-- | Transform a table by adding 'CAST' to all columns. This is most useful for
144128-- finalising a SELECT or RETURNING statement, guaranteed that the output
145129-- matches what is encoded in each columns TypeInformation.
146130castTable :: Table Expr a => a -> a
147−castTable (toColumns -> as) = fromColumns $ htabulate \field ->
148− case hfield hspecs field of
149− Spec {info} -> case hfield as field of
150− expr -> scastExpr info expr
131+castTable (toColumns -> as) = fromColumns $ htabulate \i ->
132+ case hfield hspecs i of
133+ SSpec{info} ->
134+ case hfield as i of
135+ E expr ->
136+ E (scastExpr info expr)
src/Rel8/Table/Ord.hs+45−16
@@ -10,6 +10,7 @@
1010{-# language TypeFamilies #-}
1111{-# language TypeOperators #-}
1212{-# language UndecidableInstances #-}
13+{-# language UndecidableSuperClasses #-}
1314{-# language ViewPatterns #-}
1415
1516module Rel8.Table.Ord
@@ -24,20 +25,29 @@ import GHC.Generics ( Rep )
2425import Prelude hiding ( seq )
2526
2627-- rel8
27−import Rel8.Expr ( Expr )
28+import Rel8.Expr ( Expr, Col( E ) )
2829import Rel8.Expr.Bool ( (||.), (&&.), false, true )
2930import Rel8.Expr.Eq ( (==.) )
3031import Rel8.Expr.Ord ( (<.), (>.) )
3132import Rel8.FCF ( Eval, Exp )
3233import Rel8.Generic.Record ( Record )
33−import Rel8.Generic.Table.Record ( GTable, GColumns, gtable )
34+import Rel8.Generic.Table
35+ ( GGTable, GGColumns, ggtable
36+ , GAlgebra
37+ )
38+import Rel8.Kind.Algebra ( KnownAlgebra )
3439import Rel8.Schema.Dict ( Dict( Dict ) )
35−import Rel8.Schema.HTable ( htabulateA, hfield )
36−import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) )
40+import Rel8.Schema.HTable
41+ ( HTable, HConstrainTable
42+ , htabulateA, hfield, hdicts
43+ )
44+import Rel8.Schema.HTable.Identity ( HIdentity( HType ) )
3745import Rel8.Schema.Null (Sql)
46+import Rel8.Schema.Spec.ConstrainDBType ( ConstrainDBType, nullifier )
3847import Rel8.Table ( Columns, toColumns, TColumns )
3948import Rel8.Table.Bool ( bool )
4049import Rel8.Table.Eq ( EqTable )
50+import Rel8.Type.Eq ( DBEq )
4151import Rel8.Type.Ord ( DBOrd )
4252
4353
@@ -46,14 +56,22 @@ import Rel8.Type.Ord ( DBOrd )
4656-- columns in a 'Table' have an instance of 'DBOrd'".
4757type OrdTable :: Type -> Constraint
4858class EqTable a => OrdTable a where
49− ordTable :: Columns a (Dict (Sql DBOrd))
59+ ordTable :: Columns a (Dict (ConstrainDBType DBOrd))
5060
5161 default ordTable ::
52− ( GTable TOrdTable TColumns (Rep (Record a))
53− , Columns a ~ GColumns TColumns (Rep (Record a))
62+ ( KnownAlgebra (GAlgebra (Rep (Record a)))
63+ , Eval (GGTable (GAlgebra (Rep (Record a))) TOrdTable TColumns (Dict (ConstrainDBType DBOrd)) (Rep (Record a)))
64+ , Columns a ~ Eval (GGColumns (GAlgebra (Rep (Record a))) TColumns (Rep (Record a)))
5465 )
55− => Columns a (Dict (Sql DBOrd))
56− ordTable = gtable @TOrdTable @TColumns @(Rep (Record a)) table
66+ => Columns a (Dict (ConstrainDBType DBOrd))
67+ ordTable =
68+ ggtable
69+ @(GAlgebra (Rep (Record a)))
70+ @TOrdTable
71+ @TColumns
72+ @(Rep (Record a))
73+ table
74+ nullifier
5775 where
5876 table (_ :: proxy x) = ordTable @x
5977
@@ -62,8 +80,19 @@ data TOrdTable :: Type -> Exp Constraint
6280type instance Eval (TOrdTable a) = OrdTable a
6381
6482
83+instance
84+ ( HTable t
85+ , f ~ Col Expr
86+ , HConstrainTable t (ConstrainDBType DBEq)
87+ , HConstrainTable t (ConstrainDBType DBOrd)
88+ )
89+ => OrdTable (t f)
90+ where
91+ ordTable = hdicts @(Columns (t f)) @(ConstrainDBType DBOrd)
92+
93+
6594instance Sql DBOrd a => OrdTable (Expr a) where
66− ordTable = HIdentity Dict
95+ ordTable = HType Dict
6796
6897
6998instance (OrdTable a, OrdTable b) => OrdTable (a, b)
@@ -98,7 +127,7 @@ instance
98127(toColumns -> as) <: (toColumns -> bs) =
99128 foldr @[] go false $ getConst $ htabulateA $ \field ->
100129 case (hfield as field, hfield bs field) of
101− (a, b) -> case hfield (ordTable @a) field of
130+ (E a, E b) -> case hfield (ordTable @a) field of
102131 Dict -> Const [(a <. b, a ==. b)]
103132 where
104133 go (lt, eq) a = lt ||. (eq &&. a)
@@ -111,7 +140,7 @@ infix 4 <:
111140(toColumns -> as) <=: (toColumns -> bs) =
112141 foldr @[] go true $ getConst $ htabulateA $ \field ->
113142 case (hfield as field, hfield bs field) of
114− (a, b) -> case hfield (ordTable @a) field of
143+ (E a, E b) -> case hfield (ordTable @a) field of
115144 Dict -> Const [(a <. b, a ==. b)]
116145 where
117146 go (lt, eq) a = lt ||. (eq &&. a)
@@ -124,7 +153,7 @@ infix 4 <=:
124153(toColumns -> as) >: (toColumns -> bs) =
125154 foldr @[] go false $ getConst $ htabulateA $ \field ->
126155 case (hfield as field, hfield bs field) of
127− (a, b) -> case hfield (ordTable @a) field of
156+ (E a, E b) -> case hfield (ordTable @a) field of
128157 Dict -> Const [(a >. b, a ==. b)]
129158 where
130159 go (gt, eq) a = gt ||. (eq &&. a)
@@ -137,7 +166,7 @@ infix 4 >:
137166(toColumns -> as) >=: (toColumns -> bs) =
138167 foldr @[] go true $ getConst $ htabulateA $ \field ->
139168 case (hfield as field, hfield bs field) of
140− (a, b) -> case hfield (ordTable @a) field of
169+ (E a, E b) -> case hfield (ordTable @a) field of
141170 Dict -> Const [(a >. b, a ==. b)]
142171 where
143172 go (gt, eq) a = gt ||. (eq &&. a)
@@ -146,9 +175,9 @@ infix 4 >=:
146175
147176-- | Given two 'Table's, return the table that sorts before the other.
148177least :: OrdTable a => a -> a -> a
149−least a b = bool b a (a <: b)
178+least a b = bool a b (a <: b)
150179
151180
152181-- | Given two 'Table's, return the table that sorts after the other.
153182greatest :: OrdTable a => a -> a -> a
154−greatest a b = bool b a (a >: b)
183+greatest a b = bool a b (a >: b)
src/Rel8/Table/Order.hs+16−13
@@ -1,5 +1,4 @@
11{-# language DataKinds #-}
2−{-# language NamedFieldPuns #-}
32{-# language ScopedTypeVariables #-}
43{-# language TypeApplications #-}
54{-# language TypeFamilies #-}
@@ -16,14 +15,16 @@ import Data.Functor.Contravariant ( (>$<), contramap )
1615import Prelude
1716
1817-- rel8
18+import Rel8.Expr ( unE )
1919import Rel8.Expr.Order ( asc, desc, nullsFirst, nullsLast )
2020import Rel8.Order ( Order )
2121import Rel8.Schema.Dict ( Dict( Dict ) )
2222import Rel8.Schema.HTable (htabulateA, hfield, hspecs)
2323import Rel8.Schema.Null ( Nullity( Null, NotNull ) )
24−import Rel8.Schema.Spec ( Spec( Spec, nullity ) )
24+import Rel8.Schema.Spec ( SSpec( SSpec ) )
25+import Rel8.Schema.Spec.ConstrainDBType ( dbTypeDict, dbTypeNullity )
2526import Rel8.Table ( Columns, toColumns )
26−import Rel8.Table.Ord ( OrdTable, ordTable )
27+import Rel8.Table.Ord
2728
2829
2930-- | Construct an 'Order' for a 'Table' by sorting all columns into ascending
@@ -31,11 +32,12 @@ import Rel8.Table.Ord ( OrdTable, ordTable )
3132ascTable :: forall a. OrdTable a => Order a
3233ascTable = contramap toColumns $ getConst $
3334 htabulateA @(Columns a) $ \field -> case hfield hspecs field of
34− Spec {nullity} -> case hfield (ordTable @a) field of
35− Dict -> Const $ (`hfield` field) >$<
36− case nullity of
37− Null -> nullsFirst asc
38− NotNull -> asc
35+ SSpec {} -> case hfield (ordTable @a) field of
36+ dict@Dict -> case dbTypeDict dict of
37+ Dict -> Const $ unE . (`hfield` field) >$<
38+ case dbTypeNullity dict of
39+ Null -> nullsFirst asc
40+ NotNull -> asc
3941
4042
4143-- | Construct an 'Order' for a 'Table' by sorting all columns into descending
@@ -43,8 +45,9 @@ ascTable = contramap toColumns $ getConst $
4345descTable :: forall a. OrdTable a => Order a
4446descTable = contramap toColumns $ getConst $
4547 htabulateA @(Columns a) $ \field -> case hfield hspecs field of
46− Spec {nullity} -> case hfield (ordTable @a) field of
47− Dict -> Const $ (`hfield` field) >$<
48− case nullity of
49− Null -> nullsLast desc
50− NotNull -> desc
48+ SSpec {} -> case hfield (ordTable @a) field of
49+ dict@Dict -> case dbTypeDict dict of
50+ Dict -> Const $ unE . (`hfield` field) >$<
51+ case dbTypeNullity dict of
52+ Null -> nullsLast desc
53+ NotNull -> desc
src/Rel8/Table/Projection.hsdeleted−73
@@ -1,73 +0,0 @@
1−{-# language FlexibleContexts #-}
2−{-# language FlexibleInstances #-}
3−{-# language MonoLocalBinds #-}
4−{-# language MultiParamTypeClasses #-}
5−{-# language StandaloneKindSignatures #-}
6−{-# language UndecidableInstances #-}
7−
8−module Rel8.Table.Projection
9− ( Projection
10− , Projectable( project )
11− , Biprojectable( biproject )
12− , Projecting
13− , apply
14− )
15−where
16−
17−-- base
18−import Data.Kind ( Constraint, Type )
19−import Prelude
20−
21−-- rel8
22−import Rel8.Schema.Field ( Field( Field ), fields )
23−import Rel8.Schema.HTable ( hfield, htabulate )
24−import Rel8.Table ( Columns, Context, Transpose, toColumns )
25−import Rel8.Table.Transpose ( Transposes )
26−
27−
28−-- | The constraint @'Projecting' a b@ ensures that @'Projection' a b@ is a
29−-- usable 'Projection'.
30−type Projecting :: Type -> Type -> Constraint
31−class
32− ( Transposes (Context a) (Field a) a (Transpose (Field a) a)
33− , Transposes (Context a) (Field a) b (Transpose (Field a) b)
34− )
35− => Projecting a b
36−instance
37− ( Transposes (Context a) (Field a) a (Transpose (Field a) a)
38− , Transposes (Context a) (Field a) b (Transpose (Field a) b)
39− )
40− => Projecting a b
41−
42−
43−-- | A @'Projection' a b@s is a special type of function @a -> b@ whereby the
44−-- resulting @b@ is guaranteed to be composed only from columns contained in
45−-- @a@.
46−type Projection :: Type -> Type -> Type
47−type Projection a b = Transpose (Field a) a -> Transpose (Field a) b
48−
49−
50−-- | @'Projectable' f@ means that @f@ is a kind of functor on 'Rel8.Table's
51−-- that allows the mapping of a 'Projection' over its underlying columns.
52−type Projectable :: (Type -> Type) -> Constraint
53−class Projectable f where
54− -- | Map a 'Projection' over @f@.
55− project :: Projecting a b
56− => Projection a b -> f a -> f b
57−
58−
59−-- | @'Biprojectable' p@ means that @p@ is a kind of bifunctor on
60−-- 'Rel8.Table's that allows the mapping of a pair of 'Projection's over its
61−-- underlying columns.
62−type Biprojectable :: (Type -> Type -> Type) -> Constraint
63−class Biprojectable p where
64− -- | Map a pair of 'Projection's over @p@.
65− biproject :: (Projecting a b, Projecting c d)
66− => Projection a b -> Projection c d -> p a c -> p b d
67−
68−
69−apply :: Projecting a b
70− => Projection a b -> Columns a context -> Columns b context
71−apply f a = case toColumns (f fields) of
72− bs -> htabulate $ \field -> case hfield bs field of
73− Field field' -> hfield a field'
src/Rel8/Table/Recontextualize.hsnew file+90
@@ -0,0 +1,90 @@
1+{-# language DataKinds #-}
2+{-# language FlexibleInstances #-}
3+{-# language FunctionalDependencies #-}
4+{-# language StandaloneKindSignatures #-}
5+{-# language TypeFamilies #-}
6+{-# language UndecidableInstances #-}
7+{-# language UndecidableSuperClasses #-}
8+
9+module Rel8.Table.Recontextualize
10+ ( Recontextualize
11+ )
12+where
13+
14+-- base
15+import Data.Functor.Identity ( Identity )
16+import Data.Kind ( Constraint, Type )
17+import Prelude ()
18+
19+-- rel8
20+import Rel8.Schema.Context ( Col )
21+import Rel8.Schema.Context.Label ( Labelable )
22+import Rel8.Schema.HTable ( HTable )
23+import qualified Rel8.Schema.Kind as K
24+import Rel8.Schema.Null ( Sql )
25+import Rel8.Schema.Result ( Result )
26+import Rel8.Table ( Table, Congruent )
27+import Rel8.Type ( DBType )
28+
29+
30+-- | @Recontextualize from to a b@ is evidence that the types @a@ and @b@ are
31+-- related, and that @b@ is the same type as @a@, but after changing the
32+-- context from the initial context @from@, to the new context @to@.
33+type Recontextualize :: K.Context -> K.Context -> Type -> Type -> Constraint
34+class
35+ ( Table from a
36+ , Table to b
37+ , Congruent a b
38+ , Recontextualize from from a a
39+ , Recontextualize to to b b
40+ , Recontextualize to from b a
41+ ) => Recontextualize from to a b
42+ | a -> from
43+ , b -> to
44+ , a to -> b
45+ , b from -> a
46+
47+
48+instance Sql DBType a => Recontextualize Result Result (Identity a) (Identity a)
49+
50+
51+instance HTable t => Recontextualize from to (t (Col from)) (t (Col to))
52+
53+
54+instance
55+ ( Recontextualize from to a1 b1
56+ , Recontextualize from to a2 b2
57+ , Labelable from
58+ , Labelable to
59+ )
60+ => Recontextualize from to (a1, a2) (b1, b2)
61+
62+
63+instance
64+ ( Recontextualize from to a1 b1
65+ , Recontextualize from to a2 b2
66+ , Recontextualize from to a3 b3
67+ , Labelable from, Labelable to
68+ )
69+ => Recontextualize from to (a1, a2, a3) (b1, b2, b3)
70+
71+
72+instance
73+ ( Recontextualize from to a1 b1
74+ , Recontextualize from to a2 b2
75+ , Recontextualize from to a3 b3
76+ , Recontextualize from to a4 b4
77+ , Labelable from, Labelable to
78+ )
79+ => Recontextualize from to (a1, a2, a3, a4) (b1, b2, b3, b4)
80+
81+
82+instance
83+ ( Recontextualize from to a1 b1
84+ , Recontextualize from to a2 b2
85+ , Recontextualize from to a3 b3
86+ , Recontextualize from to a4 b4
87+ , Recontextualize from to a5 b5
88+ , Labelable from, Labelable to
89+ )
90+ => Recontextualize from to (a1, a2, a3, a4, a5) (b1, b2, b3, b4, b5)
src/Rel8/Table/Rel8able.hs+85−29
@@ -1,6 +1,6 @@
11{-# language DataKinds #-}
22{-# language FlexibleInstances #-}
3−{-# language MultiParamTypeClasses #-}
3+{-# language FunctionalDependencies #-}
44{-# language ScopedTypeVariables #-}
55{-# language StandaloneKindSignatures #-}
66{-# language TypeApplications #-}
@@ -15,78 +15,134 @@ module Rel8.Table.Rel8able
1515where
1616
1717-- base
18−import Prelude ()
18+import Data.Kind ( Constraint, Type )
19+import Data.Type.Equality ( (:~:)( Refl ) )
20+import Prelude
1921
2022-- rel8
2123import Rel8.Expr ( Expr )
2224import qualified Rel8.Kind.Algebra as K
23−import Rel8.Kind.Context ( Reifiable, contextSing )
25+import Rel8.Kind.Context
26+ ( SContext( SReify )
27+ , Reifiable, contextSing
28+ , sLabelable, sReifiable
29+ )
2430import Rel8.Generic.Rel8able
2531 ( Rel8able, Algebra
2632 , GColumns, gfromColumns, gtoColumns
27− , GFromExprs, gfromResult, gtoResult
33+ , greify, gunreify
2834 )
35+import Rel8.Schema.Context ( Col )
36+import Rel8.Schema.Context.Label ( Labelable )
37+import Rel8.Schema.Dict ( Dict( Dict ) )
2938import qualified Rel8.Schema.Kind as K
3039import Rel8.Schema.HTable ( HConstrainTable, hdicts )
31−import Rel8.Schema.Null ( Sql )
40+import Rel8.Schema.Reify ( hreify, hunreify, UnwrapReify )
3241import Rel8.Schema.Result ( Result )
3342import Rel8.Table
34− ( Table, Columns, Context, fromColumns, toColumns
35− , FromExprs, fromResult, toResult
36− , Transpose
43+ ( Table, Columns, Context, Congruent, fromColumns, toColumns
44+ , Unreify, reify, unreify
3745 )
38−import Rel8.Table.ADT ( ADT )
46+import Rel8.Schema.Spec.ConstrainDBType ( ConstrainDBType )
47+import Rel8.Table.ADT ( ADT( ADT ), ADTable, fromADT, toADT )
3948import Rel8.Table.Eq ( EqTable, eqTable )
49+import Rel8.Table.HKD ( HKD )
4050import Rel8.Table.Ord ( OrdTable, ordTable )
41−import Rel8.Table.Serialize ( ToExprs )
51+import Rel8.Table.Recontextualize ( Recontextualize )
52+import Rel8.Table.Serialize ( FromExprs, ToExprs, fromResult, toResult )
4253import Rel8.Type.Eq ( DBEq )
4354import Rel8.Type.Ord ( DBOrd )
4455
4556
46−instance (Rel8able t, Reifiable context, context ~ context') =>
47− Table context' (t context)
57+instance (Rel8able t, Labelable context, Reifiable context) =>
58+ Table context (t context)
4859 where
4960 type Columns (t context) = GColumns t
5061 type Context (t context) = context
51− type FromExprs (t context) = GFromExprs t
52− type Transpose to (t context) = t to
62+ type Unreify (t context) = t (UnwrapReify context)
63+
64+ fromColumns = gunreify . gfromColumns . hreify
65+ toColumns = hunreify . gtoColumns . greify
66+
67+ reify Refl = case contextSing @context of
68+ SReify context -> case sLabelable context of
69+ Dict -> case sReifiable context of
70+ Dict -> greify
71+
72+ unreify Refl = case contextSing @context of
73+ SReify context -> case sLabelable context of
74+ Dict -> case sReifiable context of
75+ Dict -> gunreify
76+
5377
54− fromColumns = gfromColumns contextSing
55− toColumns = gtoColumns contextSing
56− fromResult = gfromResult @t
57− toResult = gtoResult @t
78+instance
79+ ( Rel8able t
80+ , Labelable from, Reifiable from
81+ , Labelable to, Reifiable to
82+ , Congruent (t from) (t to)
83+ )
84+ => Recontextualize from to (t from) (t to)
5885
5986
6087instance
6188 ( context ~ Expr
6289 , Rel8able t
63− , HConstrainTable (Columns (t context)) (Sql DBEq)
90+ , HConstrainTable (Columns (t context)) (ConstrainDBType DBEq)
6491 )
6592 => EqTable (t context)
6693 where
67− eqTable = hdicts @(Columns (t context)) @(Sql DBEq)
94+ eqTable = hdicts @(Columns (t context)) @(ConstrainDBType DBEq)
6895
6996
7097instance
7198 ( context ~ Expr
7299 , Rel8able t
73− , HConstrainTable (Columns (t context)) (Sql DBEq)
74− , HConstrainTable (Columns (t context)) (Sql DBOrd)
100+ , HConstrainTable (Columns (t context)) (ConstrainDBType DBEq)
101+ , HConstrainTable (Columns (t context)) (ConstrainDBType DBOrd)
75102 )
76103 => OrdTable (t context)
77104 where
78− ordTable = hdicts @(Columns (t context)) @(Sql DBOrd)
105+ ordTable = hdicts @(Columns (t context)) @(ConstrainDBType DBOrd)
106+
107+
108+type instance FromExprs (t Expr) = FromExprs' t
79109
80110
81111instance
82− ( Rel8able t', t' ~ Choose (Algebra t) t
83− , x ~ t' Expr
112+ ( x ~ t' Expr
84113 , result ~ Result
114+ , ToExprs' (Algebra t) t' t
85115 )
86116 => ToExprs x (t result)
117+ where
118+ fromResult = fromResult' @(Algebra t) @t'
119+ toResult = toResult' @(Algebra t) @t'
87120
88121
89−type Choose :: K.Algebra -> K.Rel8able -> K.Rel8able
90−type family Choose algebra t where
91− Choose 'K.Sum t = ADT t
92− Choose 'K.Product t = t
122+type FromExprs' :: K.Rel8able -> Type
123+type family FromExprs' t where
124+ FromExprs' (ADT t) = t Result
125+ FromExprs' (HKD a) = a
126+ FromExprs' t = t Result
127+
128+
129+type ToExprs' :: K.Algebra -> K.Rel8able -> K.Rel8able -> Constraint
130+class (algebra ~ Algebra t, Rel8able t') =>
131+ ToExprs' algebra t' t | algebra t -> t'
132+ where
133+ fromResult' :: GColumns t' (Col Result) -> t Result
134+ toResult' :: t Result -> GColumns t' (Col Result)
135+
136+
137+instance (Algebra t ~ 'K.Product, Rel8able t, t ~ t') =>
138+ ToExprs' 'K.Product t' t
139+ where
140+ fromResult' = fromColumns
141+ toResult' = toColumns
142+
143+
144+instance (Algebra t ~ 'K.Sum, ADTable t, t' ~ ADT t) =>
145+ ToExprs' 'K.Sum t' t
146+ where
147+ fromResult' = fromADT . ADT
148+ toResult' = (\(ADT a) -> a) . toADT
src/Rel8/Table/Serialize.hs+122−22
@@ -1,4 +1,7 @@
11{-# language AllowAmbiguousTypes #-}
2+{-# language DataKinds #-}
3+{-# language DefaultSignatures #-}
4+{-# language DisambiguateRecordFields #-}
25{-# language FlexibleContexts #-}
36{-# language FlexibleInstances #-}
47{-# language FunctionalDependencies #-}
@@ -7,31 +10,41 @@
710{-# language StandaloneKindSignatures #-}
811{-# language TypeApplications #-}
912{-# language TypeFamilies #-}
13+{-# language TypeOperators #-}
1014{-# language UndecidableInstances #-}
1115
1216module Rel8.Table.Serialize
13− ( Serializable, lit, litHTable, parse
14− , ToExprs
17+ ( Serializable, lit, parse
18+ , ToExprs(..), FromExprs
19+ , TToExprs
1520 )
1621where
1722
1823-- base
19−import Data.Functor.Identity ( Identity( Identity ) )
2024import Data.Kind ( Constraint, Type )
2125import Data.List.NonEmpty ( NonEmpty )
26+import GHC.Generics ( Generic, Rep, from, to )
2227import Prelude
2328
2429-- hasql
2530import qualified Hasql.Decoders as Hasql
2631
2732-- rel8
28−import Rel8.Expr ( Expr )
33+import Rel8.Expr ( Expr, Col( E ) )
2934import Rel8.Expr.Serialize ( slitExpr, sparseValue )
35+import Rel8.FCF ( Eval, Exp )
36+import Rel8.Generic.Record ( Record(..) )
37+import Rel8.Generic.Table
38+ ( GGToExprs, GGColumns, ggfromResult, ggtoResult
39+ , GAlgebra
40+ )
41+import Rel8.Kind.Algebra ( KnownAlgebra )
3042import Rel8.Schema.HTable ( HTable, htabulate, htabulateA, hfield, hspecs )
43+import Rel8.Schema.HTable.Identity ( HIdentity( HType ) )
3144import Rel8.Schema.Null ( NotNull, Sql )
32−import Rel8.Schema.Result ( Result )
33−import Rel8.Schema.Spec ( Spec(..) )
34−import Rel8.Table ( Table, fromColumns, FromExprs, fromResult, toResult )
45+import Rel8.Schema.Result ( Col( R ), Result )
46+import Rel8.Schema.Spec ( SSpec(..), KnownSpec )
47+import Rel8.Table ( Table, Columns, fromColumns, toColumns, TColumns )
3548import Rel8.Type ( DBType )
3649
3750-- semigroupoids
@@ -40,21 +53,71 @@ import Data.Functor.Apply ( WrappedApplicative(..) )
4053
4154-- | @ToExprs exprs a@ is evidence that the types @exprs@ and @a@ describe
4255-- essentially the same type, but @exprs@ is in the 'Expr' context, and @a@ is
43−-- a normal Haskell type.
56+-- in the 'Result' context.
4457type ToExprs :: Type -> Type -> Constraint
45−class Table Expr exprs => ToExprs exprs a
46−
47−
48−instance {-# OVERLAPPABLE #-} (Sql DBType a, x ~ Expr a) => ToExprs x a
49−
50−
51−instance (Sql DBType a, x ~ [a]) => ToExprs (Expr x) [a]
58+class Table Expr exprs => ToExprs exprs a where
59+ fromResult :: Columns exprs (Col Result) -> a
60+ toResult :: a -> Columns exprs (Col Result)
61+
62+ default fromResult ::
63+ ( Generic (Record a)
64+ , KnownAlgebra (GAlgebra (Rep (Record exprs)))
65+ , Eval (GGToExprs (GAlgebra (Rep (Record exprs))) TToExprs TColumns (Rep (Record exprs)) (Rep (Record a)))
66+ , Columns exprs ~ Eval (GGColumns (GAlgebra (Rep (Record exprs))) TColumns (Rep (Record exprs)))
67+ )
68+ => Columns exprs (Col Result) -> a
69+ fromResult =
70+ unrecord .
71+ to .
72+ ggfromResult
73+ @(GAlgebra (Rep (Record exprs)))
74+ @TToExprs
75+ @TColumns
76+ @(Rep (Record exprs))
77+ (\(_ :: proxy expr) -> fromResult @expr)
78+
79+ default toResult ::
80+ ( Generic (Record a)
81+ , KnownAlgebra (GAlgebra (Rep (Record exprs)))
82+ , Eval (GGToExprs (GAlgebra (Rep (Record exprs))) TToExprs TColumns (Rep (Record exprs)) (Rep (Record a)))
83+ , Columns exprs ~ Eval (GGColumns (GAlgebra (Rep (Record exprs))) TColumns (Rep (Record exprs)))
84+ )
85+ => a -> Columns exprs (Col Result)
86+ toResult =
87+ ggtoResult
88+ @(GAlgebra (Rep (Record exprs)))
89+ @TToExprs
90+ @TColumns
91+ @(Rep (Record exprs))
92+ (\(_ :: proxy expr) -> toResult @expr) .
93+ from .
94+ Record
95+
96+
97+data TToExprs :: Type -> Type -> Exp Constraint
98+type instance Eval (TToExprs exprs a) = ToExprs exprs a
99+
100+
101+instance {-# OVERLAPPABLE #-} (Sql DBType a, x ~ Expr a) => ToExprs x a where
102+ fromResult (HType (R a)) = a
103+ toResult = HType . R
104+
105+
106+instance (Sql DBType a, x ~ [a]) => ToExprs (Expr x) [a] where
107+ fromResult (HType (R a)) = a
108+ toResult = HType . R
52109
53110
54111instance (Sql DBType a, NotNull a, x ~ Maybe a) => ToExprs (Expr x) (Maybe a)
112+ where
113+ fromResult (HType (R a)) = a
114+ toResult = HType . R
55115
56116
57117instance (Sql DBType a, NotNull a, x ~ NonEmpty a) => ToExprs (Expr x) (NonEmpty a)
118+ where
119+ fromResult (HType (R a)) = a
120+ toResult = HType . R
58121
59122
60123instance (ToExprs exprs1 a, ToExprs exprs2 b, x ~ (exprs1, exprs2)) =>
@@ -116,6 +179,43 @@ instance
116179 => ToExprs x (a, b, c, d, e, f, g)
117180
118181
182+instance (HTable t, result ~ Col Result, x ~ t (Col Expr)) =>
183+ ToExprs x (t result)
184+ where
185+ fromResult = id
186+ toResult = id
187+
188+
189+instance (KnownSpec spec, x ~ Col Expr spec) =>
190+ ToExprs x (Col Result spec)
191+ where
192+ fromResult = fromColumns
193+ toResult = toColumns
194+
195+
196+-- | The @FromExprs@ type function maps a type in the @Expr@ context to the
197+-- corresponding type in the @Result@ context.
198+type FromExprs :: Type -> Type
199+type family FromExprs a
200+type instance FromExprs (Expr a) = a
201+type instance FromExprs (Col Expr spec) = Col Result spec
202+type instance FromExprs (a, b) = (FromExprs a, FromExprs b)
203+type instance FromExprs (a, b, c) = (FromExprs a, FromExprs b, FromExprs c)
204+type instance FromExprs (a, b, c, d) =
205+ (FromExprs a, FromExprs b, FromExprs c, FromExprs d)
206+type instance FromExprs (a, b, c, d, e) =
207+ (FromExprs a, FromExprs b, FromExprs c, FromExprs d, FromExprs e)
208+type instance FromExprs (a, b, c, d, e, f) =
209+ ( FromExprs a, FromExprs b, FromExprs c, FromExprs d, FromExprs e
210+ , FromExprs f
211+ )
212+type instance FromExprs (a, b, c, d, e, f, g) =
213+ ( FromExprs a, FromExprs b, FromExprs c, FromExprs d, FromExprs e
214+ , FromExprs f, FromExprs g
215+ )
216+type instance FromExprs (t (Col Expr)) = t (Col Result)
217+
218+
119219-- | @Serializable@ witnesses the one-to-one correspondence between the type
120220-- @sql@, which contains SQL expressions, and the type @haskell@, which
121221-- contains the Haskell decoding of rows containing @sql@ SQL expressions.
@@ -128,21 +228,21 @@ instance {-# OVERLAPPING #-} Sql DBType a => Serializable (Expr a) a
128228-- | Use @lit@ to turn literal Haskell values into expressions. @lit@ is
129229-- capable of lifting single @Expr@s to full tables.
130230lit :: forall exprs a. Serializable exprs a => a -> exprs
131−lit = fromColumns . litHTable . toResult @_ @exprs
231+lit = fromColumns . litHTable . toResult @exprs
132232
133233
134234parse :: forall exprs a. Serializable exprs a => Hasql.Row a
135−parse = fromResult @_ @exprs <$> parseHTable
235+parse = fromResult @exprs <$> parseHTable
136236
137237
138−litHTable :: HTable t => t Result -> t Expr
238+litHTable :: HTable t => t (Col Result) -> t (Col Expr)
139239litHTable as = htabulate $ \field ->
140240 case hfield hspecs field of
141− Spec {nullity, info} -> case hfield as field of
142− Identity value -> slitExpr nullity info value
241+ SSpec {nullity, info} -> case hfield as field of
242+ R value -> E (slitExpr nullity info value)
143243
144244
145−parseHTable :: HTable t => Hasql.Row (t Result)
245+parseHTable :: HTable t => Hasql.Row (t (Col Result))
146246parseHTable = unwrapApplicative $ htabulateA $ \field ->
147247 WrapApplicative $ case hfield hspecs field of
148− Spec {nullity, info} -> Identity <$> sparseValue nullity info
248+ SSpec {nullity, info} -> R <$> sparseValue nullity info
src/Rel8/Table/Tag.hsnew file+97
@@ -0,0 +1,97 @@
1+{-# language DataKinds #-}
2+{-# language FlexibleInstances #-}
3+{-# language GADTs #-}
4+{-# language InstanceSigs #-}
5+{-# language NamedFieldPuns #-}
6+{-# language ScopedTypeVariables #-}
7+{-# language StandaloneKindSignatures #-}
8+{-# language TypeApplications #-}
9+{-# language UndecidableInstances #-}
10+
11+module Rel8.Table.Tag
12+ ( Tag(..), Taggable
13+ , fromAggregate
14+ , fromExpr
15+ , fromName
16+ )
17+where
18+
19+-- base
20+import Control.Applicative ( (<|>), empty )
21+import Data.Kind ( Constraint, Type )
22+import Data.Foldable ( fold )
23+import Data.Monoid ( getFirst )
24+import Data.Proxy ( Proxy( Proxy ) )
25+import GHC.TypeLits ( KnownSymbol, Symbol, symbolVal )
26+import Prelude
27+
28+-- rel8
29+import Rel8.Aggregate ( Aggregate, Aggregator, foldInputs )
30+import Rel8.Expr ( Expr )
31+import Rel8.Expr.Opaleye ( fromPrimExpr )
32+import Rel8.Schema.Name ( Name( Name ) )
33+import Rel8.Schema.Null ( Sql )
34+import Rel8.Type.Monoid ( DBMonoid )
35+
36+
37+type Tag :: Symbol -> Type -> Type
38+data Tag label a = Tag
39+ { expr :: Expr a
40+ , aggregator :: Maybe Aggregator
41+ , name :: Name a
42+ }
43+
44+
45+type Taggable :: Type -> Constraint
46+class Taggable a where
47+ tappend :: KnownSymbol label => Tag label a -> Tag label a -> Tag label a
48+ tempty :: KnownSymbol label => Tag label a
49+
50+
51+instance Sql DBMonoid a => Taggable a where
52+ tappend :: forall label. KnownSymbol label
53+ => Tag label a -> Tag label a -> Tag label a
54+ tappend a b = Tag
55+ { expr = expr a <> expr b
56+ , aggregator = aggregator a <|> aggregator b
57+ , name = case (name a, symbolVal (Proxy @label)) of
58+ (Name x, y)
59+ | x == y -> name b
60+ | otherwise -> name a
61+ }
62+ {-# INLINABLE tappend #-}
63+
64+ tempty :: forall label. KnownSymbol label => Tag label a
65+ tempty = Tag
66+ { expr = mempty
67+ , aggregator = empty
68+ , name = Name (symbolVal (Proxy @label))
69+ }
70+ {-# INLINABLE tempty #-}
71+
72+
73+instance (KnownSymbol label, Taggable a) => Semigroup (Tag label a) where
74+ (<>) = tappend
75+
76+
77+instance (KnownSymbol label, Taggable a) => Monoid (Tag label a) where
78+ mempty = tempty
79+
80+
81+fromAggregate :: forall a label. (KnownSymbol label, Taggable a)
82+ => Aggregate a -> Tag label a
83+fromAggregate = fold . getFirst . foldInputs go
84+ where
85+ go aggregator primExpr = pure $ (tempty @a @label)
86+ { expr = fromPrimExpr primExpr
87+ , aggregator
88+ }
89+
90+
91+fromExpr :: forall label a. (KnownSymbol label, Taggable a)
92+ => Expr a -> Tag label a
93+fromExpr expr = (tempty @a @label) {expr}
94+
95+
96+fromName :: forall a label. Taggable a => Name a -> Tag label a
97+fromName name = (tempty @a @"") {name}
src/Rel8/Table/These.hs+148−164
@@ -5,6 +5,7 @@
55{-# language FlexibleInstances #-}
66{-# language MultiParamTypeClasses #-}
77{-# language NamedFieldPuns #-}
8+{-# language RecordWildCards #-}
89{-# language ScopedTypeVariables #-}
910{-# language StandaloneKindSignatures #-}
1011{-# language TupleSections #-}
@@ -20,49 +21,52 @@ module Rel8.Table.These
2021 , isThisTable, isThatTable, isThoseTable
2122 , hasHereTable, hasThereTable
2223 , justHereTable, justThereTable
23− , alignMaybeTable
24− , aggregateTheseTable
2524 , nameTheseTable
2625 )
2726where
2827
2928-- base
29+import Control.Applicative ( liftA2 )
3030import Data.Bifunctor ( Bifunctor, bimap )
31+import Data.Functor.Identity ( runIdentity )
3132import Data.Kind ( Type )
32−import Data.Maybe ( isJust )
33−import Prelude hiding ( null, undefined )
33+import Prelude hiding ( undefined )
3434
3535-- rel8
36−import Rel8.Aggregate ( Aggregate )
3736import Rel8.Expr ( Expr )
38−import Rel8.Expr.Bool ( (&&.), (||.), boolExpr, not_ )
39−import Rel8.Expr.Null ( null, isNonNull )
40−import Rel8.Kind.Context ( Reifiable )
41−import Rel8.Schema.Context.Nullify ( Nullifiable )
42−import Rel8.Schema.Dict ( Dict( Dict ) )
43−import Rel8.Schema.HTable.Label ( hlabel, hrelabel, hunlabel )
37+import Rel8.Expr.Bool ( (&&.), not_ )
38+import Rel8.Expr.Null ( isNonNull )
39+import Rel8.Schema.Context.Label
40+ ( Labelable
41+ , HLabelable, hlabeler, hunlabeler
42+ )
43+import Rel8.Schema.Context.Nullify
44+ ( Nullifiable, ConstrainTag
45+ , HNullifiable, HConstrainTag
46+ , hencodeTag, hdecodeTag
47+ , hnullifier, hunnullifier
48+ )
49+import Rel8.Schema.HTable ( HTable )
50+import Rel8.Schema.HTable.Label ( hlabel, hunlabel )
4451import Rel8.Schema.HTable.Identity ( HIdentity(..) )
45−import Rel8.Schema.HTable.Maybe ( HMaybeTable(..) )
52+import Rel8.Schema.HTable.Nullify ( hnullify, hunnullify )
4653import Rel8.Schema.HTable.These ( HTheseTable(..) )
47−import qualified Rel8.Schema.Kind as K
4854import Rel8.Schema.Name ( Name )
4955import Rel8.Table
5056 ( Table, Columns, Context, fromColumns, toColumns
51− , FromExprs, fromResult, toResult
52− , Transpose
57+ , reify, unreify
5358 )
5459import Rel8.Table.Eq ( EqTable, eqTable )
5560import Rel8.Table.Maybe
5661 ( MaybeTable(..)
5762 , maybeTable, justTable, nothingTable
5863 , isJustTable
59− , aggregateMaybeTable
6064 , nameMaybeTable
6165 )
62−import Rel8.Table.Nullify ( Nullify, guard )
6366import Rel8.Table.Ord ( OrdTable, ordTable )
64−import Rel8.Table.Projection ( Biprojectable, Projectable, biproject, project )
65−import Rel8.Table.Serialize ( ToExprs )
67+import Rel8.Table.Recontextualize ( Recontextualize )
68+import Rel8.Table.Serialize ( FromExprs, ToExprs, fromResult, toResult )
69+import Rel8.Table.Tag ( Tag(..) )
6670import Rel8.Table.Undefined ( undefined )
6771import Rel8.Type.Tag ( MaybeTag )
6872
@@ -71,8 +75,7 @@ import Data.Functor.Apply ( Apply, (<.>) )
7175import Data.Functor.Bind ( Bind, (>>-) )
7276
7377-- these
74−import Data.These ( These( This, That, These ) )
75−import Data.These.Combinators ( justHere, justThere )
78+import Data.These ( These )
7679
7780
7881-- | @TheseTable a b@ is a Rel8 table that contains either the table @a@, the
@@ -82,45 +85,32 @@ import Data.These.Combinators ( justHere, justThere )
8285--
8386-- @TheseTable@ is operationally the same as Haskell's 'These' type, but
8487-- adapted to work with Rel8.
85−type TheseTable :: K.Context -> Type -> Type -> Type
86−data TheseTable context a b = TheseTable
87− { here :: MaybeTable context a
88− , there :: MaybeTable context b
88+type TheseTable :: Type -> Type -> Type
89+data TheseTable a b = TheseTable
90+ { here :: MaybeTable a
91+ , there :: MaybeTable b
8992 }
9093 deriving stock Functor
9194
9295
93−instance Biprojectable (TheseTable context) where
94− biproject f g (TheseTable a b) = TheseTable (project f a) (project g b)
95−
96−
97−instance Nullifiable context => Bifunctor (TheseTable context) where
96+instance Bifunctor TheseTable where
9897 bimap f g (TheseTable a b) = TheseTable (fmap f a) (fmap g b)
9998
10099
101−instance Projectable (TheseTable context a) where
102− project f (TheseTable a b) = TheseTable a (project f b)
103−
104−
105−instance (context ~ Expr, Table Expr a, Semigroup a) =>
106− Apply (TheseTable context a)
107− where
100+instance (Table Expr a, Semigroup a) => Apply (TheseTable a) where
108101 fs <.> as = TheseTable
109102 { here = here fs <> here as
110103 , there = there fs <.> there as
111104 }
112105
113106
114−instance (context ~ Expr, Table Expr a, Semigroup a) =>
115− Applicative (TheseTable context a)
107+instance (Table Expr a, Semigroup a) => Applicative (TheseTable a)
116108 where
117109 pure = thatTable
118110 (<*>) = (<.>)
119111
120112
121−instance (context ~ Expr, Table Expr a, Semigroup a) =>
122− Bind (TheseTable context a)
123− where
113+instance (Table Expr a, Semigroup a) => Bind (TheseTable a) where
124114 TheseTable here1 ma >>- f = case ma >>- f' of
125115 mtb -> TheseTable
126116 { here = maybeTable here1 ((here1 <>) . fst) mtb
@@ -131,14 +121,12 @@ instance (context ~ Expr, Table Expr a, Semigroup a) =>
131121 TheseTable here2 mb -> (here2,) <$> mb
132122
133123
134−instance (context ~ Expr, Table Expr a, Semigroup a) =>
135− Monad (TheseTable context a)
136− where
124+instance (Table Expr a, Semigroup a) => Monad (TheseTable a) where
137125 (>>=) = (>>-)
138126
139127
140−instance (context ~ Expr, Table Expr a, Table Expr b, Semigroup a, Semigroup b) =>
141− Semigroup (TheseTable context a b)
128+instance (Table Expr a, Table Expr b, Semigroup a, Semigroup b) =>
129+ Semigroup (TheseTable a b)
142130 where
143131 a <> b = TheseTable
144132 { here = here a <> here b
@@ -148,175 +136,124 @@ instance (context ~ Expr, Table Expr a, Table Expr b, Semigroup a, Semigroup b)
148136
149137instance
150138 ( Table context a, Table context b
151− , Reifiable context, context ~ context'
152− )
153− => Table context' (TheseTable context a b)
139+ , Labelable context, Nullifiable context, ConstrainTag context MaybeTag
140+ ) => Table context (TheseTable a b)
154141 where
155− type Columns (TheseTable context a b) = HTheseTable (Columns a) (Columns b)
156− type Context (TheseTable context a b) = Context a
157− type FromExprs (TheseTable context a b) =
158− These (FromExprs a) (FromExprs b)
159− type Transpose to (TheseTable context a b) =
160− TheseTable to (Transpose to a) (Transpose to b)
161−
162− toColumns TheseTable {here, there} = HTheseTable
163− { hhereTag = hlabel $ HIdentity $ tag here
164− , hhere =
165− hlabel $ guard (tag here) isJust isNonNull $ toColumns $ just here
166− , hthereTag = hlabel $ HIdentity $ tag there
167− , hthere =
168− hlabel $ guard (tag there) isJust isNonNull $ toColumns $ just there
169− }
142+ type Columns (TheseTable a b) = HTheseTable (Columns a) (Columns b)
143+ type Context (TheseTable a b) = Context a
170144
171− fromColumns HTheseTable {hhereTag, hhere, hthereTag, hthere} = TheseTable
172− { here = MaybeTable
173− { tag = unHIdentity $ hunlabel hhereTag
174− , just = fromColumns $ hunlabel hhere
175− }
176− , there = MaybeTable
177− { tag = unHIdentity $ hunlabel hthereTag
178− , just = fromColumns $ hunlabel hthere
179− }
180− }
145+ toColumns = toColumns2 toColumns toColumns
146+ fromColumns = fromColumns2 fromColumns fromColumns
147+ reify = liftA2 bimap reify reify
148+ unreify = liftA2 bimap unreify unreify
181149
182− toResult tables = HTheseTable
183− { hhereTag = hrelabel hhereTag
184− , hhere = hrelabel hhere
185− , hthereTag = hrelabel hthereTag
186− , hthere = hrelabel hthere
187− }
188− where
189− HMaybeTable
190− { htag = hhereTag
191− , hjust = hhere
192− } = toResult @_ @(MaybeTable context a) (justHere tables)
193− HMaybeTable
194− { htag = hthereTag
195− , hjust = hthere
196− } = toResult @_ @(MaybeTable context b) (justThere tables)
197−
198− fromResult HTheseTable {hhereTag, hhere, hthereTag, hthere} =
199− case (here, there) of
200− (Just a, Nothing) -> This a
201− (Nothing, Just b) -> That b
202− (Just a, Just b) -> These a b
203− _ -> error "These.fromColumns: mismatch between tags and data"
204− where
205− here = fromResult @_ @(MaybeTable context a) mhere
206− there = fromResult @_ @(MaybeTable context b) mthere
207− mhere = HMaybeTable
208− { htag = hrelabel hhereTag
209− , hjust = hrelabel hhere
210− }
211− mthere = HMaybeTable
212− { htag = hrelabel hthereTag
213− , hjust = hrelabel hthere
214− }
215−
216−
217−instance (EqTable a, EqTable b, context ~ Expr) =>
218− EqTable (TheseTable context a b)
219− where
220− eqTable = HTheseTable
221− { hhereTag = hlabel (HIdentity Dict)
222− , hhere = hlabel (eqTable @(Nullify context a))
223− , hthereTag = hlabel (HIdentity Dict)
224− , hthere = hlabel (eqTable @(Nullify context b))
225− }
226150
151+instance
152+ ( Labelable from, Nullifiable from, ConstrainTag from MaybeTag
153+ , Labelable to, Nullifiable to, ConstrainTag to MaybeTag
154+ , Recontextualize from to a1 b1
155+ , Recontextualize from to a2 b2
156+ ) =>
157+ Recontextualize from to (TheseTable a1 a2) (TheseTable b1 b2)
158+
159+
160+instance (EqTable a, EqTable b) => EqTable (TheseTable a b) where
161+ eqTable = toColumns2 id id (thoseTable (eqTable @a) (eqTable @b))
227162
228−instance (OrdTable a, OrdTable b, context ~ Expr) =>
229− OrdTable (TheseTable context a b)
230− where
231− ordTable = HTheseTable
232− { hhereTag = hlabel (HIdentity Dict)
233− , hhere = hlabel (ordTable @(Nullify context a))
234− , hthereTag = hlabel (HIdentity Dict)
235− , hthere = hlabel (ordTable @(Nullify context b))
236− }
237163
164+instance (OrdTable a, OrdTable b) => OrdTable (TheseTable a b) where
165+ ordTable = toColumns2 id id (thoseTable (ordTable @a) (ordTable @b))
238166
239−instance (ToExprs exprs1 a, ToExprs exprs2 b, x ~ TheseTable Expr exprs1 exprs2) =>
167+
168+type instance FromExprs (TheseTable a b) = These (FromExprs a) (FromExprs b)
169+
170+
171+instance (ToExprs exprs1 a, ToExprs exprs2 b, x ~ TheseTable exprs1 exprs2) =>
240172 ToExprs x (These a b)
173+ where
174+ fromResult =
175+ bimap (fromResult @exprs1) (fromResult @exprs2) .
176+ fromColumns
177+ toResult =
178+ toColumns .
179+ bimap (toResult @exprs1) (toResult @exprs2)
180+
181+
182+toHereTag :: Tag "isJust" a -> Tag "hasHere" a
183+toHereTag Tag {..} = Tag {..}
184+
185+
186+toThereTag :: Tag "isJust" a -> Tag "hasThere" a
187+toThereTag Tag {..} = Tag {..}
241188
242189
243190-- | Test if a 'TheseTable' was constructed with 'thisTable'.
244191--
245192-- Corresponds to 'Data.These.Combinators.isThis'.
246−isThisTable :: TheseTable Expr a b -> Expr Bool
193+isThisTable :: TheseTable a b -> Expr Bool
247194isThisTable a = hasHereTable a &&. not_ (hasThereTable a)
248195
249196
250197-- | Test if a 'TheseTable' was constructed with 'thatTable'.
251198--
252199-- Corresponds to 'Data.These.Combinators.isThat'.
253−isThatTable :: TheseTable Expr a b -> Expr Bool
200+isThatTable :: TheseTable a b -> Expr Bool
254201isThatTable a = not_ (hasHereTable a) &&. hasThereTable a
255202
256203
257204-- | Test if a 'TheseTable' was constructed with 'thoseTable'.
258205--
259206-- Corresponds to 'Data.These.Combinators.isThese'.
260−isThoseTable :: TheseTable Expr a b -> Expr Bool
207+isThoseTable :: TheseTable a b -> Expr Bool
261208isThoseTable a = hasHereTable a &&. hasThereTable a
262209
263210
264211-- | Test if the @a@ side of @TheseTable a b@ is present.
265212--
266213-- Corresponds to 'Data.These.Combinators.hasHere'.
267−hasHereTable :: TheseTable Expr a b -> Expr Bool
214+hasHereTable :: TheseTable a b -> Expr Bool
268215hasHereTable TheseTable {here} = isJustTable here
269216
270217
271218-- | Test if the @b@ table of @TheseTable a b@ is present.
272219--
273220-- Corresponds to 'Data.These.Combinators.hasThere'.
274−hasThereTable :: TheseTable Expr a b -> Expr Bool
221+hasThereTable :: TheseTable a b -> Expr Bool
275222hasThereTable TheseTable {there} = isJustTable there
276223
277224
278225-- | Attempt to project out the @a@ table of a @TheseTable a b@.
279226--
280227-- Corresponds to 'Data.These.Combinators.justHere'.
281−justHereTable :: TheseTable context a b -> MaybeTable context a
228+justHereTable :: TheseTable a b -> MaybeTable a
282229justHereTable = here
283230
284231
285232-- | Attempt to project out the @b@ table of a @TheseTable a b@.
286233--
287234-- Corresponds to 'Data.These.Combinators.justThere'.
288−justThereTable :: TheseTable context a b -> MaybeTable context b
235+justThereTable :: TheseTable a b -> MaybeTable b
289236justThereTable = there
290237
291238
292−-- | Construct a @TheseTable@ from two 'MaybeTable's.
293−alignMaybeTable :: ()
294− => MaybeTable Expr a
295− -> MaybeTable Expr b
296− -> MaybeTable Expr (TheseTable Expr a b)
297−alignMaybeTable a b = MaybeTable tag (pure (TheseTable a b))
298− where
299− tag = boolExpr null mempty (isJustTable a ||. isJustTable b)
300−
301−
302239-- | Construct a @TheseTable@. Corresponds to 'This'.
303−thisTable :: Table Expr b => a -> TheseTable Expr a b
240+thisTable :: Table Expr b => a -> TheseTable a b
304241thisTable a = TheseTable (justTable a) nothingTable
305242
306243
307244-- | Construct a @TheseTable@. Corresponds to 'That'.
308−thatTable :: Table Expr a => b -> TheseTable Expr a b
245+thatTable :: Table Expr a => b -> TheseTable a b
309246thatTable b = TheseTable nothingTable (justTable b)
310247
311248
312249-- | Construct a @TheseTable@. Corresponds to 'These'.
313−thoseTable :: a -> b -> TheseTable Expr a b
250+thoseTable :: a -> b -> TheseTable a b
314251thoseTable a b = TheseTable (justTable a) (justTable b)
315252
316253
317254-- | Pattern match on a 'TheseTable'. Corresponds to 'these'.
318255theseTable :: Table Expr c
319− => (a -> c) -> (b -> c) -> (a -> b -> c) -> TheseTable Expr a b -> c
256+ => (a -> c) -> (b -> c) -> (a -> b -> c) -> TheseTable a b -> c
320257theseTable f g h TheseTable {here, there} =
321258 maybeTable
322259 (maybeTable undefined f here)
@@ -324,19 +261,6 @@ theseTable f g h TheseTable {here, there} =
324261 there
325262
326263
327−-- | Lift a pair of aggregating functions to operate on an 'TheseTable'.
328−-- @thisTable@s, @thatTable@s and @thoseTable@s are grouped separately.
329−aggregateTheseTable :: ()
330− => (exprs -> aggregates)
331− -> (exprs' -> aggregates')
332− -> TheseTable Expr exprs exprs'
333− -> TheseTable Aggregate aggregates aggregates'
334−aggregateTheseTable f g (TheseTable here there) = TheseTable
335− { here = aggregateMaybeTable f here
336− , there = aggregateMaybeTable g there
337− }
338−
339−
340264-- | Construct a 'TheseTable' in the 'Name' context. This can be useful if you
341265-- have a 'TheseTable' that you are storing in a table and need to construct a
342266-- 'TableSchema'.
@@ -349,9 +273,69 @@ nameTheseTable :: ()
349273 -- ^ Names of the columns in the @a@ table.
350274 -> b
351275 -- ^ Names of the columns in the @b@ table.
352− -> TheseTable Name a b
276+ -> TheseTable a b
353277nameTheseTable here there a b =
354278 TheseTable
355279 { here = nameMaybeTable here a
356280 , there = nameMaybeTable there b
357281 }
282+
283+
284+toColumns2 ::
285+ ( HTable t
286+ , HTable u
287+ , HConstrainTag context MaybeTag
288+ , HLabelable context
289+ , HNullifiable context
290+ )
291+ => (a -> t context)
292+ -> (b -> u context)
293+ -> TheseTable a b
294+ -> HTheseTable t u context
295+toColumns2 f g TheseTable {here, there} = HTheseTable
296+ { hhereTag = HIdentity $ hencodeTag (toHereTag (tag here))
297+ , hhere =
298+ hlabel hlabeler $ hnullify (hnullifier (tag here) isNonNull) $ f (just here)
299+ , hthereTag = HIdentity $ hencodeTag (toThereTag (tag there))
300+ , hthere =
301+ hlabel hlabeler $ hnullify (hnullifier (tag there) isNonNull) $ g (just there)
302+ }
303+
304+
305+fromColumns2 ::
306+ ( HTable t
307+ , HTable u
308+ , HConstrainTag context MaybeTag
309+ , HLabelable context
310+ , HNullifiable context
311+ )
312+ => (t context -> a)
313+ -> (u context -> b)
314+ -> HTheseTable t u context
315+ -> TheseTable a b
316+fromColumns2 f g HTheseTable {hhereTag, hhere, hthereTag, hthere} = TheseTable
317+ { here =
318+ let
319+ tag = hdecodeTag $ unHIdentity hhereTag
320+ in
321+ MaybeTable
322+ { tag
323+ , just = f $
324+ runIdentity $
325+ hunnullify (\a -> pure . hunnullifier a) $
326+ hunlabel hunlabeler
327+ hhere
328+ }
329+ , there =
330+ let
331+ tag = hdecodeTag $ unHIdentity hthereTag
332+ in
333+ MaybeTable
334+ { tag
335+ , just = g $
336+ runIdentity $
337+ hunnullify (\a -> pure . hunnullifier a) $
338+ hunlabel hunlabeler
339+ hthere
340+ }
341+ }
src/Rel8/Table/Transpose.hsdeleted−46
@@ -1,46 +0,0 @@
1−{-# language DataKinds #-}
2−{-# language FlexibleInstances #-}
3−{-# language FunctionalDependencies #-}
4−{-# language StandaloneKindSignatures #-}
5−{-# language TypeFamilies #-}
6−{-# language UndecidableInstances #-}
7−
8−module Rel8.Table.Transpose
9− ( Transposes
10− )
11−where
12−
13−-- base
14−import Data.Kind ( Constraint, Type )
15−import Prelude ()
16−
17−-- rel8
18−import qualified Rel8.Schema.Kind as K
19−import Rel8.Table ( Table, Transpose, Congruent )
20−
21−
22−-- | @'Transposes' from to a b@ means that @a@ and @b@ are 'Table's, in the
23−-- @from@ and @to@ contexts respectively, which share the same underlying
24−-- structure. In other words, @b@ is a version of @a@ transposed from the
25−-- @from@ context to the @to@ context (and vice versa).
26−type Transposes :: K.Context -> K.Context -> Type -> Type -> Constraint
27−class
28− ( Table from a
29− , Table to b
30− , Congruent a b
31− , b ~ Transpose to a
32− , a ~ Transpose from b
33− )
34− => Transposes from to a b
35− | a -> from
36− , b -> to
37− , a to -> b
38− , b from -> a
39−instance
40− ( Table from a
41− , Table to b
42− , Congruent a b
43− , b ~ Transpose to a
44− , a ~ Transpose from b
45− )
46− => Transposes from to a b
src/Rel8/Table/Undefined.hs+5−5
@@ -11,16 +11,16 @@ where
1111import Prelude hiding ( undefined )
1212
1313-- rel8
14−import Rel8.Expr ( Expr )
14+import Rel8.Expr ( Expr, Col( E ) )
1515import Rel8.Expr.Null ( snull, unsafeUnnullify )
1616import Rel8.Schema.HTable ( htabulate, hfield, hspecs )
1717import Rel8.Schema.Null ( Nullity( Null, NotNull ) )
18−import Rel8.Schema.Spec ( Spec(..) )
18+import Rel8.Schema.Spec ( SSpec(..) )
1919import Rel8.Table ( Table, fromColumns )
2020
2121
2222undefined :: Table Expr a => a
2323undefined = fromColumns $ htabulate $ \field -> case hfield hspecs field of
24− Spec {nullity, info} -> case nullity of
25− Null -> snull info
26− NotNull -> unsafeUnnullify (snull info)
24+ SSpec {nullity, info} -> case nullity of
25+ Null -> E (snull info)
26+ NotNull -> E (unsafeUnnullify (snull info))
src/Rel8/Table/Unreify.hsnew file+102
@@ -0,0 +1,102 @@
1+{-# language DataKinds #-}
2+{-# language FlexibleContexts #-}
3+{-# language FlexibleInstances #-}
4+{-# language GADTs #-}
5+{-# language LambdaCase #-}
6+{-# language MultiParamTypeClasses #-}
7+{-# language QuantifiedConstraints #-}
8+{-# language RankNTypes #-}
9+{-# language StandaloneKindSignatures #-}
10+{-# language TypeFamilies #-}
11+{-# language UndecidableInstances #-}
12+
13+-- | This module implements some machinery for implementing methods of the
14+-- 'Table' class for a particular special (but important) class of polymorphic
15+-- @Table@ types.
16+--
17+-- This special case is characterised by a @newtype@ wrapper around a bare
18+-- 'HTable' which is constructed by applying a type family to the polymorphic
19+-- type variable.
20+--
21+-- Examples of this class of @Table@ include @ListTable@ and @NonEmptyTable@.
22+--
23+-- The tricky part about implementing @Table@ for these types is 'reify' and
24+-- 'unreify'. There is no guarantee in general that @'Unreify' a@ is itself
25+-- a @Table@, let alone a @Table@ with the same 'Columns' as @a@
26+-- (e.g., @Unreify (AColumn Result Bool) = Bool@, and @Bool@ is not a
27+-- @Table@)
28+
29+module Rel8.Table.Unreify
30+ ( Unreifiable, Unreifiability(..), unreifiability
31+ , Unreifies
32+ )
33+where
34+
35+-- base
36+import Data.Kind ( Constraint, Type )
37+import Prelude ()
38+
39+-- rel8
40+import Rel8.Aggregate ( Aggregate )
41+import Rel8.Expr ( Expr )
42+import Rel8.Kind.Context ( SContext(..), Reifiable, sReifiable )
43+import Rel8.Schema.Dict ( Dict( Dict ) )
44+import qualified Rel8.Schema.Kind as K
45+import Rel8.Schema.Name ( Name )
46+import Rel8.Schema.Reify ( Reify )
47+import Rel8.Schema.Result ( Result )
48+import Rel8.Table ( Table, Context, Congruent, Unreify )
49+
50+
51+type Unreifies :: K.Context -> Type -> Constraint
52+type family Unreifies context a where
53+ Unreifies (Reify context) a = Unreifier context a
54+ Unreifies _ _ = ()
55+
56+
57+type Unreifiable :: Type -> Constraint
58+class
59+ ( Context a ~ Reify Aggregate => Unreifier Aggregate a
60+ , Context a ~ Reify Expr => Unreifier Expr a
61+ , Context a ~ Reify Name => Unreifier Name a
62+ , (forall ctx. (Context a ~ Reify (Reify ctx), Reifiable ctx) => Unreifier (Reify ctx) a)
63+ )
64+ => Unreifiable a
65+instance
66+ ( Context a ~ Reify Aggregate => Unreifier Aggregate a
67+ , Context a ~ Reify Expr => Unreifier Expr a
68+ , Context a ~ Reify Name => Unreifier Name a
69+ , (forall ctx. (Context a ~ Reify (Reify ctx), Reifiable ctx) => Unreifier (Reify ctx) a)
70+ )
71+ => Unreifiable a
72+
73+
74+type Unreifier :: K.Context -> Type -> Constraint
75+class
76+ ( Table context (Unreify a)
77+ , Congruent a (Unreify a)
78+ )
79+ => Unreifier context a
80+instance
81+ ( Table context (Unreify a)
82+ , Congruent a (Unreify a)
83+ )
84+ => Unreifier context a
85+
86+
87+type Unreifiability :: K.Context -> Type -> Type
88+data Unreifiability context a where
89+ UResult :: Unreifiability Result a
90+ Unreifiability :: Unreifier context a
91+ => SContext context -> Unreifiability context a
92+
93+
94+unreifiability :: (Context a ~ Reify context, Unreifiable a)
95+ => SContext context -> Unreifiability context a
96+unreifiability = \case
97+ SAggregate -> Unreifiability SAggregate
98+ SExpr -> Unreifiability SExpr
99+ SName -> Unreifiability SName
100+ SResult -> UResult
101+ SReify context -> case sReifiable context of
102+ Dict -> Unreifiability (SReify context)
src/Rel8/Table/Window.hsdeleted−43
@@ -1,43 +0,0 @@
1−{-# language MonoLocalBinds #-}
2−
3−module Rel8.Table.Window
4− ( cumulative
5− , cumulative_
6− , currentRow
7− )
8−where
9−
10−-- base
11−import Prelude
12−
13−-- opaleye
14−import qualified Opaleye.Window as Opaleye
15−
16−-- rel8
17−import Rel8.Aggregate ( Aggregates )
18−import qualified Rel8.Expr.Window as Expr
19−import Rel8.Schema.HTable ( hfield, htabulateA )
20−import Rel8.Table ( fromColumns, toColumns )
21−import Rel8.Window ( Window( Window ) )
22−
23−
24−-- | 'cumulative' allows the use of aggregation functions in 'Window'
25−-- expressions. In particular, @'cumulative' 'Rel8.sum'@
26−-- (when combined with 'Rel8.Window.orderPartitionBy') gives a running total,
27−-- also known as a \"cumulative sum\", hence the name @cumulative@.
28−cumulative :: Aggregates aggregates exprs => (a -> aggregates) -> Window a exprs
29−cumulative f =
30− fmap fromColumns $
31− htabulateA $ \field ->
32− Expr.cumulative ((`hfield` field) . toColumns . f)
33−
34−
35−-- | A version of 'cumulative' for use with nullary aggregators like
36−-- 'Rel8.Expr.Aggregate.countStar'.
37−cumulative_ :: Aggregates aggregates exprs => aggregates -> Window a exprs
38−cumulative_ = cumulative . const
39−
40−
41−-- | Return every column of the current row of a window query.
42−currentRow :: Window a a
43−currentRow = Window $ Opaleye.over (Opaleye.noWindowFunction id) mempty mempty
src/Rel8/Tabulate.hsdeleted−640
@@ -1,640 +0,0 @@
1−{-# language FlexibleContexts #-}
2−{-# language MonoLocalBinds #-}
3−{-# language ScopedTypeVariables #-}
4−{-# language StandaloneKindSignatures #-}
5−{-# language TypeApplications #-}
6−{-# language TupleSections #-}
7−{-# language UndecidableInstances #-}
8−
9−-- | "Rel8.Tabulate" provides an alternative API ('Tabulation') for writing
10−-- queries that complements the main "Rel8" API ('Query').
11−
12−module Rel8.Tabulate
13− (
14− Tabulation
15−
16− -- * Interfacing with 'Query's
17− , fromQuery
18− , toQuery
19− , liftQuery
20− , through
21− , lookup
22−
23− -- * Aggregation and Ordering
24− , aggregate
25− , distinct
26− , order
27−
28− -- ** Magic 'Tabulation's
29− -- $magic
30− , count
31− , optional
32− , many
33− , some
34− , exists
35− , present
36− , absent
37−
38− -- * Natural joins
39− , align
40− , alignWith
41− , leftAlign
42− , leftAlignWith
43− , rightAlign
44− , rightAlignWith
45− , zip
46− , zipWith
47− , similarity
48− , difference
49− )
50−where
51−
52−-- base
53−import Control.Applicative ( (<|>), empty, liftA2 )
54−import Control.Monad ( liftM2 )
55−import Data.Bifunctor ( Bifunctor, bimap, first, second )
56−import Data.Foldable ( traverse_ )
57−import Data.Function ( on )
58−import Data.Functor.Contravariant ( Contravariant, (>$<), contramap )
59−import Data.Int ( Int64 )
60−import Data.Kind ( Type )
61−import Data.Maybe ( fromJust, fromMaybe )
62−import Prelude hiding ( lookup, zip, zipWith )
63−
64−-- bifunctors
65−import Data.Bifunctor.Clown ( Clown( Clown ), runClown )
66−
67−-- comonad
68−import Control.Comonad ( extract )
69−
70−-- opaleye
71−import qualified Opaleye.Aggregate as Opaleye
72−import qualified Opaleye.Order as Opaleye ( orderBy, distinctOnExplicit )
73−
74−-- profunctors
75−import Data.Profunctor ( dimap, lmap )
76−
77−-- product-profunctors
78−import Data.Profunctor.Product
79− ( ProductProfunctor, (***!)
80− , SumProfunctor, (+++!)
81− )
82−import qualified Data.Profunctor.Product as PP
83−
84−-- rel8
85−import Rel8.Aggregate ( Aggregates )
86−import Rel8.Expr ( Expr )
87−import Rel8.Expr.Aggregate ( countStar )
88−import Rel8.Expr.Bool ( true )
89−import Rel8.Order ( Order( Order ) )
90−import Rel8.Query ( Query )
91−import qualified Rel8.Query.Exists as Q ( exists, present, absent )
92−import Rel8.Query.Filter ( where_ )
93−import Rel8.Query.List ( catNonEmptyTable )
94−import qualified Rel8.Query.Maybe as Q ( optional )
95−import Rel8.Query.Opaleye ( mapOpaleye, unsafePeekQuery )
96−import Rel8.Query.Rebind ( rebind )
97−import Rel8.Query.These ( alignBy )
98−import Rel8.Table ( Table, fromColumns, toColumns )
99−import Rel8.Table.Aggregate ( hgroupBy, listAgg, nonEmptyAgg )
100−import Rel8.Table.Alternative
101− ( AltTable, (<|>:)
102− , AlternativeTable, emptyTable
103− )
104−import Rel8.Table.Cols ( fromCols, toCols )
105−import Rel8.Table.Eq ( EqTable, (==:), eqTable )
106−import Rel8.Table.List ( ListTable( ListTable ) )
107−import Rel8.Table.Maybe ( MaybeTable( MaybeTable ), maybeTable )
108−import Rel8.Table.NonEmpty ( NonEmptyTable( NonEmptyTable ) )
109−import Rel8.Table.Opaleye ( aggregator, unpackspec )
110−import Rel8.Table.Ord ( OrdTable )
111−import Rel8.Table.Order ( ascTable )
112−import Rel8.Table.Projection
113− ( Biprojectable, biproject
114− , Projectable, project
115− , apply
116− )
117−import Rel8.Table.These ( TheseTable( TheseTable ), theseTable )
118−
119−-- semigroupoids
120−import Data.Functor.Apply ( Apply, liftF2 )
121−import Data.Functor.Bind ( Bind, (>>-) )
122−
123−
124−type Key :: Type -> Type
125−type Key = Maybe
126−
127−
128−cat :: Table Expr k => Key k -> Query k
129−cat = maybe emptyTable pure
130−
131−
132−key :: (ProductProfunctor p, SumProfunctor p)
133− => p a b -> p (Key a) (Key b)
134−key a = dimap from to (PP.empty +++! a)
135− where
136− from = maybe (Left ()) Right
137− to = either (const Nothing) Just
138−
139−
140−keyed :: (ProductProfunctor p, SumProfunctor p)
141− => p k l -> p a b -> p (Key k, a) (Key l, b)
142−keyed k a = key k ***! a
143−
144−
145−type Predicate :: Type -> Type
146−newtype Predicate a = Predicate (Maybe (a -> Expr Bool))
147−
148−
149−instance Contravariant Predicate where
150− contramap f (Predicate a) = Predicate (lmap f <$> a)
151−
152−
153−instance Semigroup (Predicate k) where
154− Predicate ma <> Predicate mb = Predicate $ ma <|> mb
155−
156−
157−instance Monoid (Predicate k) where
158− mempty = Predicate Nothing
159−
160−
161−match :: EqTable k => Key k -> Predicate k
162−match = Predicate . fmap (==:)
163−
164−
165−ensure :: Predicate k -> Key k -> Query ()
166−ensure (Predicate mp) = traverse_ (\k -> traverse_ (\p -> where_ (p k)) mp)
167−
168−
169−-- | A @'Tabulation' k a@ is like a @'Query' a@, except that each row also
170−-- has a key @k@ in addition to the value @a@. 'Tabulation's can be composed
171−-- monadically just like 'Query's, but the resulting join is more like a
172−-- @NATURAL JOIN@ (based on the common key column(s) @k@) than the
173−-- @CROSS JOIN@ given by 'Query'.
174−--
175−-- Another way to think of @'Tabulation' k a@ is as analogous to @Map k a@ in
176−-- the same way @'Query' a@ is analogous to @[a]@. However, there's nothing
177−-- stopping a 'Tabulation' from containing multiple rows with the same key, so
178−-- technically @Map k (NonEmpty a)@ is more accurate.
179−--
180−-- 'Tabulation's can be created from 'Query's with 'fromQuery' and 'liftQuery'
181−-- and converted back to 'Query's with 'lookup' and 'toQuery' (though note the
182−-- caveats that come with the latter).
183−type Tabulation :: Type -> Type -> Type
184−newtype Tabulation k a = Tabulation (Predicate k -> Query (Key k, a))
185−
186−
187−instance Biprojectable Tabulation where
188− biproject f g =
189− bimap
190− (fromColumns . apply f . toColumns)
191− (fromColumns . apply g . toColumns)
192−
193−
194−instance Bifunctor Tabulation where
195− bimap f g (Tabulation a) = Tabulation $ \p ->
196− bimap (fmap f) g <$> a (f >$< p)
197−
198−
199−instance Functor (Tabulation k) where
200− fmap = second
201−
202−
203−instance Projectable (Tabulation k) where
204− project f = fmap (fromColumns . apply f . toColumns)
205−
206−
207−-- | If @'Tabulation' k a@ is @Map k (NonEmpty a)@, then @(<.>)@ is
208−-- @intersectionWith (liftA2 (<*>))@
209−instance EqTable k => Apply (Tabulation k) where
210− liftF2 = liftA2
211−
212−
213−-- | @pure = 'liftQuery' . pure@
214−instance EqTable k => Applicative (Tabulation k) where
215− pure = liftQuery . pure
216− liftA2 = liftM2
217−
218−
219−instance EqTable k => Bind (Tabulation k) where
220− Tabulation as >>- f = Tabulation $ \p -> do
221− (k, a) <- as p
222− case f a of
223− Tabulation bs -> do
224− let p' = match k
225− (k', b) <- bs (p' <> p)
226− ensure p' k'
227− pure (k' <|> k, b)
228−
229−
230−instance EqTable k => Monad (Tabulation k) where
231− (>>=) = (>>-)
232−
233−
234−-- | If @'Tabulation' k a@ is @Map k (NonEmpty a)@, then @(<|>:)@ is
235−-- @unionWith (<>)@.
236−instance EqTable k => AltTable (Tabulation k) where
237− tas <|>: tbs = do
238− eas <- peek tas
239− ebs <- peek tbs
240− case (eas, ebs) of
241− (Left as, Left bs) -> liftQuery $ as <|>: bs
242− (Right as, Right bs) -> fromQuery $ as <|>: bs
243− _ -> catNonEmptyTable `through` ((<>) `on` some) tas tbs
244−
245−
246−instance EqTable k => AlternativeTable (Tabulation k) where
247− emptyTable = Tabulation $ const $ fmap (empty,) emptyTable
248−
249−
250−-- | If @'Tabulation' k a@ is @Map k (NonEmpty a)@, then @(<>)@ is
251−-- @unionWith (liftA2 (<>))@.
252−instance (EqTable k, Table Expr a, Semigroup a) => Semigroup (Tabulation k a)
253− where
254− (<>) = alignWith (theseTable id id (<>))
255−
256−
257−instance (EqTable k, Table Expr a, Semigroup a) => Monoid (Tabulation k a)
258− where
259− mempty = emptyTable
260−
261−
262−-- | Any 'Query' of key-value pairs @(k, a)@ can be a @'Tabulation' k a@.
263−fromQuery :: Query (k, a) -> Tabulation k a
264−fromQuery = Tabulation . const . fmap (first pure)
265−
266−
267−-- | Convert a @'Tabulation' k a@ back into a 'Query' of key-value pairs.
268−--
269−-- Note that the result of a 'toQuery' is undefined (will always return zero
270−-- rows) on 'Tabulation's constructed with 'liftQuery' or 'pure'. So while
271−-- @toQuery . fromQuery@ is always @id@, @fromQuery . toQuery@ is not.
272−--
273−-- A safer, more predictable alternative to 'toQuery' is to use 'lookup' with
274−-- an explicit set of keys:
275−--
276−-- @
277−-- do
278−-- k <- keys
279−-- a <- lookup k tabulation
280−-- pure (k, a)
281−-- @
282−--
283−-- Having said that, in practice, most legitimate uses of 'Tabulation' will
284−-- have a well-defined 'toQuery'. It would be possible in theory to encode
285−-- the necessary invariants at the type level using an indexed monad, but we
286−-- would lose the ability to use @do@-notation, which is the main benefit
287−-- of having 'Tabulation' as a monad in the first place.
288−--
289−-- In particular, @'toQuery' t@ is well-defined for any 'Tabulation' @t@
290−-- defined as @t = fromQuery _@. @'toQuery' t@ is also well-defined for any
291−-- 'Tabulation' @t@ defined as @t = t' >>= _@ or @t = t' *> _@ where
292−-- @'toQuery' t'@ is well-defined. There are other valid permutations too.
293−-- Generally, anything that uses 'fromQuery' at some point, unless wrapped in
294−-- a top-level 'present' or 'absent', will have a well-defined 'toQuery'.
295−toQuery :: Table Expr k => Tabulation k a -> Query (k, a)
296−toQuery (Tabulation f) = do
297− (mk, a) <- f mempty
298− k <- cat mk
299− pure (k, a)
300−
301−
302−-- | A @'Query' a@ can be treated as a @'Tabulation' k a@ where the given @a@
303−-- values exist at every possible key @k@.
304−liftQuery :: Query a -> Tabulation k a
305−liftQuery = Tabulation . const . fmap (empty,)
306−
307−
308−-- | Run a Kleisli arrow in the the 'Query' monad \"through\" a 'Tabulation'.
309−-- Useful for 'Rel8.filter'ing a 'Tabulation'.
310−--
311−-- @
312−-- 'Rel8.filter' ((>=. 30) . userAge) `'through'` usersById
313−-- @
314−through :: (a -> Query b) -> Tabulation k a -> Tabulation k b
315−through f (Tabulation as) = Tabulation $ \p -> do
316− (k, a) <- as p
317− b <- f a
318− pure (k, b)
319−infixr 1 `through`
320−
321−
322−-- | @'lookup' k t@ returns the value(s) at the key @k@ in the tabulation @t@.
323−lookup :: EqTable k => k -> Tabulation k a -> Query a
324−lookup k (Tabulation f) = do
325− (k', a) <- f p
326− ensure p k'
327− pure a
328− where
329− p = match (pure k)
330−
331−
332−-- | 'aggregate' aggregates the values within each key of a
333−-- 'Tabulation'. There is an implicit @GROUP BY@ on all the key columns.
334−aggregate :: forall k aggregates exprs.
335− ( EqTable k
336− , Aggregates aggregates exprs
337− )
338− => Tabulation k aggregates -> Tabulation k exprs
339−aggregate (Tabulation f) = Tabulation $
340− mapOpaleye (Opaleye.aggregate (keyed haggregator aggregator)) .
341− fmap (first (fmap (hgroupBy (eqTable @k) . toColumns))) .
342− f
343− where
344− haggregator = dimap fromColumns fromCols aggregator
345−
346−
347−-- | 'distinct' ensures a 'Tabulation' has at most one value for
348−-- each key, i.e., it drops duplicates. In general it keeps only the
349−-- \"first\" value it encounters for each key, but note that \"first\" is
350−-- undefined unless you first call 'order'.
351−distinct :: EqTable k => Tabulation k a -> Tabulation k a
352−distinct (Tabulation f) = Tabulation $
353− mapOpaleye (Opaleye.distinctOnExplicit (key unpackspec) fst) . f
354−
355−
356−-- | 'order' orders the /values/ of a 'Tabulation' within their
357−-- respective keys. This specifies a defined order for 'distinct'.
358−-- It also defines the order of the lists produced by 'many' and
359−-- 'some'.
360−order :: OrdTable k => Order a -> Tabulation k a -> Tabulation k a
361−order ordering (Tabulation f) =
362− Tabulation $ mapOpaleye (Opaleye.orderBy ordering') . f
363− where
364− Order ordering' = runClown (keyed (Clown ascTable) (Clown ordering))
365−
366−
367−-- $magic
368−--
369−-- Some of the following combinators produce \"magic\" 'Tabulation's. Let's
370−-- use 'count' as an example to demonstrate this concept. Consider
371−-- the following:
372−--
373−-- @
374−-- count $ fromQuery $ values
375−-- [ (lit 'a', lit True)
376−-- , (lit 'a', lit False)
377−-- , (lit 'b', lit True)
378−-- ]
379−-- @
380−--
381−-- You might expect this to be equivalent to the following 'Tabulation':
382−--
383−-- @
384−-- fromQuery $ values
385−-- [ (lit 'a', 2)
386−-- , (lit 'b', 1)
387−-- ]
388−-- @
389−--
390−-- However, it isn't quite. While the resulting 'Tabulation' does effectively
391−-- contain the above entries, it also behaves as though it contained the value
392−-- @0@ at every other possible key.
393−--
394−-- This means you can do:
395−--
396−-- @
397−-- do
398−-- user <- usersById
399−-- orderCount <- count ordersByUserId
400−-- @
401−--
402−-- To see how many orders a user has (getting @0@ if they have no orders).
403−
404−
405−-- | 'count' returns a count of how many entries are in the given
406−-- 'Tabulation' at each key.
407−--
408−-- The resulting 'Tabulation' is \"magic\" in that the value @0@ exists at
409−-- every possible key that wasn't in the given 'Tabulation'.
410−count :: EqTable k => Tabulation k a -> Tabulation k (Expr Int64)
411−count =
412− fmap (maybeTable 0 id) .
413− optional .
414− aggregate .
415− fmap (const countStar)
416−
417−
418−-- | 'optional' produces a \"magic\" 'Tabulation' whereby each
419−-- entry in the given 'Tabulation' is wrapped in 'Rel8.justTable', and every
420−-- other possible key contains a single 'Rel8.nothingTable'.
421−--
422−-- This is used to implement 'leftAlignWith'.
423−optional :: Tabulation k a -> Tabulation k (MaybeTable Expr a)
424−optional (Tabulation f) = Tabulation $ \p -> case p of
425− Predicate Nothing -> fmap pure <$> f p
426− _ -> fmap (\m -> (empty, snd <$> m)) $ Q.optional $ do
427− (k, a) <- f p
428− ensure p k
429− pure (k, a)
430−
431−
432−-- | 'many' aggregates each entry with a particular key into a
433−-- single entry with all of the values contained in a 'ListTable'.
434−--
435−-- 'order' can be used to give this 'ListTable' a defined order.
436−--
437−-- The resulting 'Tabulation' is \"magic\" in that the value
438−-- @'Rel8.listTable []'@ exists at every possible key that wasn't in the given
439−-- 'Tabulation'.
440−many :: (EqTable k, Table Expr a)
441− => Tabulation k a -> Tabulation k (ListTable Expr a)
442−many =
443− fmap (maybeTable mempty (\(ListTable a) -> ListTable a)) .
444− optional .
445− aggregate .
446− fmap (listAgg . toCols)
447−
448−
449−-- | 'some' aggregates each entry with a particular key into a
450−-- single entry with all of the values contained in a 'NonEmptyTable'.
451−--
452−-- 'order' can be used to give this 'NonEmptyTable' a defined order.
453−some :: (EqTable k, Table Expr a)
454− => Tabulation k a -> Tabulation k (NonEmptyTable Expr a)
455−some =
456− fmap (\(NonEmptyTable a) -> NonEmptyTable a) .
457− aggregate .
458− fmap (nonEmptyAgg . toCols)
459−
460−
461−-- | 'exists' produces a \"magic\" 'Tabulation' which contains the
462−-- value 'Rel8.true' at each key in the given 'Tabulation', and the value
463−-- 'Rel8.false' at every other possible key.
464−exists :: Tabulation k a -> Tabulation k (Expr Bool)
465−exists (Tabulation f) = Tabulation $ \p -> case p of
466− Predicate Nothing -> (true <$) <$> f p
467− _ -> fmap (empty,) $ Q.exists $ do
468− (k, _) <- f p
469− ensure p k
470−
471−
472−-- | 'present' produces a 'Tabulation' where a single @()@ row
473−-- exists for every key that was present in the given 'Tabulation'.
474−--
475−-- This is used to implement 'similarity'.
476−present :: Tabulation k a -> Tabulation k ()
477−present (Tabulation f) = Tabulation $ \p -> do
478− Q.present $ do
479− (k, _) <- f p
480− ensure p k
481− pure (empty, ())
482−
483−
484−-- | 'absent' produces a 'Tabulation' where a single @()@ row exists
485−-- at every possible key that absent from the given 'Tabulation'.
486−--
487−-- This is used to implement 'difference'.
488−absent :: Tabulation k a -> Tabulation k ()
489−absent (Tabulation f) = Tabulation $ \p -> do
490− Q.absent $ do
491− (k, _) <- f p
492− ensure p k
493− pure (empty, ())
494−
495−
496−-- | Performs a @NATURAL FULL OUTER JOIN@ based on the common key columns.
497−--
498−-- Analogous to 'Data.Semialign.align'.
499−align :: EqTable k
500− => Tabulation k a -> Tabulation k b -> Tabulation k (TheseTable Expr a b)
501−align = alignWith id
502−
503−
504−-- | Performs a @NATURAL FULL OUTER JOIN@ based on the common key columns.
505−--
506−-- Analogous to 'Data.Semialign.alignWith'.
507−alignWith :: EqTable k
508− => (TheseTable Expr a b -> c)
509− -> Tabulation k a -> Tabulation k b -> Tabulation k c
510−alignWith f (Tabulation as) (Tabulation bs) = Tabulation $ \p -> do
511− tkab <- liftF2 (alignBy condition) as bs p
512− k <- traverse (rebind "key") $ recover $ bimap fst fst tkab
513− let
514− tab = bimap snd snd tkab
515− pure (k, f tab)
516− where
517− condition (k, _) (k', _) = fromMaybe true (liftA2 (==:) k k')
518− recover (TheseTable mma@(MaybeTable _ ma) mmb@(MaybeTable _ mb)) =
519− case extract ma of
520− Nothing -> extract mb
521− Just a -> case extract mb of
522− Nothing -> extract ma
523− Just b -> case a <$ mma <|>: b <$ mmb of
524− MaybeTable _ c -> pure (extract c)
525−
526−
527−-- | Performs a @NATURAL LEFT OUTER JOIN@ based on the common key columns.
528−--
529−-- Analogous to 'Data.Semialign.rpadZip'.
530−--
531−-- Note that you can achieve the same effect with 'optional' and the
532−-- 'Applicative' instance for 'Tabulation', i.e., this is just
533−-- @\left right -> liftA2 (,) left (optional right). You can also
534−-- use @do@-notation.
535−leftAlign :: EqTable k
536− => Tabulation k a -> Tabulation k b -> Tabulation k (a, MaybeTable Expr b)
537−leftAlign = leftAlignWith (,)
538−
539−
540−-- | Performs a @NATURAL LEFT OUTER JOIN@ based on the common key columns.
541−--
542−-- Analogous to 'Data.Semialign.rpadZipWith'.
543−--
544−-- Note that you can achieve the same effect with 'optional' and the
545−-- 'Applicative' instance for 'Tabulation', i.e., this is just
546−-- @\f left right -> liftA2 f left (optional right). You can also
547−-- use @do@-notation.
548−leftAlignWith :: EqTable k
549− => (a -> MaybeTable Expr b -> c)
550− -> Tabulation k a -> Tabulation k b -> Tabulation k c
551−leftAlignWith f left right = liftA2 f left (optional right)
552−
553−
554−-- | Performs a @NATURAL RIGHT OUTER JOIN@ based on the common key columns.
555−--
556−-- Analogous to 'Data.Semialign.lpadZip'.
557−--
558−-- Note that you can achieve the same effect with 'optional' and the
559−-- 'Applicative' instance for 'Tabulation', i.e., this is just
560−-- @\left right -> liftA2 (flip (,)) right (optional left). You can
561−-- also use @do@-notation.
562−rightAlign :: EqTable k
563− => Tabulation k a -> Tabulation k b -> Tabulation k (MaybeTable Expr a, b)
564−rightAlign = rightAlignWith (,)
565−
566−
567−-- | Performs a @NATURAL RIGHT OUTER JOIN@ based on the common key columns.
568−--
569−-- Analogous to 'Data.Semialign.lpadZipWith'.
570−--
571−-- Note that you can achieve the same effect with 'optional' and the
572−-- 'Applicative' instance for 'Tabulation', i.e., this is just
573−-- @\f left right -> liftA2 (flip f) right (optional left). You can
574−-- also use @do@-notation.
575−rightAlignWith :: EqTable k
576− => (MaybeTable Expr a -> b -> c)
577− -> Tabulation k a -> Tabulation k b -> Tabulation k c
578−rightAlignWith f left right = liftA2 (flip f) right (optional left)
579−
580−
581−-- | Performs a @NATURAL INNER JOIN@ based on the common key columns.
582−--
583−-- Analagous to 'Data.Semialign.zip'.
584−--
585−-- Note that you can achieve the same effect with the 'Applicative' instance
586−-- of 'Tabulation', i.e., this is just @'liftA2 (,)'@. You can also use
587−-- @do@-notation.
588−zip :: EqTable k
589− => Tabulation k a -> Tabulation k b -> Tabulation k (a, b)
590−zip = zipWith (,)
591−
592−
593−-- | Performs a @NATURAL INNER JOIN@ based on the common key columns.
594−--
595−-- Analagous to 'Data.Semialign.zipWith'.
596−--
597−-- Note that you can achieve the same effect with the 'Applicative' instance
598−-- of 'Tabulation', i.e., this is just @'liftA2'@. You can also use
599−-- @do@-notation.
600−zipWith :: EqTable k
601− => (a -> b -> c) -> Tabulation k a -> Tabulation k b -> Tabulation k c
602−zipWith = liftA2
603−
604−
605−-- | Performs a [@NATURAL SEMI JOIN@](https://en.wikipedia.org/wiki/Relational_algebra#Semijoin_%28%E2%8B%89%29%28%E2%8B%8A%29)
606−-- based on the common key columns.
607−--
608−-- The result is a subset of the left tabulation where only entries which have
609−-- a corresponding entry in the right tabulation are kept.
610−--
611−-- Note that you can achieve a similar effect with 'present' and the
612−-- 'Applicative' instance of 'Tabulation', i.e., this is just
613−-- @\left right -> left <* present right@. You can also use
614−-- @do@-notation.
615−similarity :: EqTable k => Tabulation k a -> Tabulation k b -> Tabulation k a
616−similarity a b = a <* present b
617−
618−
619−-- | Performs a [@NATURAL ANTI JOIN@](https://en.wikipedia.org/wiki/Relational_algebra#Antijoin_%28%E2%96%B7%29)
620−-- based on the common key columns.
621−--
622−-- The result is a subset of the left tabulation where only entries which do
623−-- not have a corresponding entry in the right tabulation are kept.
624−--
625−-- Note that you can achieve a similar effect with 'absent' and the
626−-- 'Applicative' instance of 'Tabulation', i.e., this is just
627−-- @\left right -> left <* absent right@. You can also use
628−-- @do@-notation.
629−difference :: EqTable k => Tabulation k a -> Tabulation k b -> Tabulation k a
630−difference a b = a <* absent b
631−
632−
633−-- | 'Tabulation's can be produced with either 'fromQuery' or 'liftQuery', and
634−-- in some cases we might want to treat these differently. 'peek' uses
635−-- 'unsafePeekQuery' to determine which type of 'Tabulation' we have.
636−peek :: Tabulation k a -> Tabulation k (Either (Query a) (Query (k, a)))
637−peek (Tabulation f) = Tabulation $ \p ->
638− pure $ (empty,) $ case unsafePeekQuery (f p) of
639− (Nothing, _) -> Left $ fmap snd (f p)
640− (Just _, _) -> Right $ fmap (first fromJust) (f p)
src/Rel8/Type.hs+8−9
@@ -1,4 +1,3 @@
1−{-# language FlexibleContexts #-}
21{-# language FlexibleInstances #-}
32{-# language MonoLocalBinds #-}
43{-# language MultiWayIf #-}
@@ -128,10 +127,10 @@ instance DBType Int64 where
128127instance DBType Float where
129128 typeInformation = TypeInformation
130129 { encode = \x -> Opaleye.ConstExpr
131− if | x == (1 / 0) -> Opaleye.OtherLit "'Infinity'"
132− | isNaN x -> Opaleye.OtherLit "'NaN'"
133− | x == (-1 / 0) -> Opaleye.OtherLit "'-Infinity'"
134− | otherwise -> Opaleye.NumericLit $ realToFrac x
130+ if | x == (1 /0) -> Opaleye.OtherLit "'Infinity'"
131+ | isNaN x -> Opaleye.OtherLit "'NaN'"
132+ | x == (-1/0) -> Opaleye.OtherLit "'-Infinity'"
133+ | otherwise -> Opaleye.NumericLit $ realToFrac x
135134 , decode = Hasql.float4
136135 , typeName = "float4"
137136 }
@@ -141,10 +140,10 @@ instance DBType Float where
141140instance DBType Double where
142141 typeInformation = TypeInformation
143142 { encode = \x -> Opaleye.ConstExpr
144− if | x == (1 / 0) -> Opaleye.OtherLit "'Infinity'"
145− | isNaN x -> Opaleye.OtherLit "'NaN'"
146− | x == (-1 / 0) -> Opaleye.OtherLit "'-Infinity'"
147− | otherwise -> Opaleye.NumericLit $ realToFrac x
143+ if | x == (1 /0) -> Opaleye.OtherLit "'Infinity'"
144+ | isNaN x -> Opaleye.OtherLit "'NaN'"
145+ | x == (-1/0) -> Opaleye.OtherLit "'-Infinity'"
146+ | otherwise -> Opaleye.NumericLit $ realToFrac x
148147 , decode = Hasql.float8
149148 , typeName = "float8"
150149 }
src/Rel8/Type/Composite.hs+19−20
@@ -20,7 +20,6 @@ where
2020
2121-- base
2222import Data.Functor.Const ( Const( Const ), getConst )
23−import Data.Functor.Identity ( Identity( Identity ) )
2423import Data.Kind ( Constraint, Type )
2524import Prelude
2625
@@ -31,19 +30,19 @@ import qualified Hasql.Decoders as Hasql
3130import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
3231
3332-- rel8
34−import Rel8.Expr ( Expr )
33+import Rel8.Expr ( Col( E ), Expr )
3534import Rel8.Expr.Opaleye ( castExpr, fromPrimExpr, toPrimExpr )
36−import Rel8.Schema.HTable ( HTable, hfield, hspecs, htabulate, htabulateA )
37−import Rel8.Schema.Name ( Name( Name ) )
35+import Rel8.Schema.HTable ( hfield, hspecs, htabulate, htabulateA )
36+import Rel8.Schema.Name ( Col( N ), Name( Name ) )
3837import Rel8.Schema.Null ( Nullity( Null, NotNull ) )
39−import Rel8.Schema.Result ( Result )
40−import Rel8.Schema.Spec ( Spec( Spec, nullity, info ) )
41−import Rel8.Table ( fromColumns, toColumns, fromResult, toResult )
38+import Rel8.Schema.Result ( Col( R ), Result )
39+import Rel8.Schema.Spec ( SSpec( SSpec, nullity, info ) )
40+import Rel8.Table ( Table, fromColumns, toColumns )
4241import Rel8.Table.Eq ( EqTable )
43−import Rel8.Table.HKD ( HKD, HKDable )
42+import Rel8.Table.HKD ( HKD, HKDable, fromHKD, toHKD )
4443import Rel8.Table.Ord ( OrdTable )
4544import Rel8.Table.Rel8able ()
46−import Rel8.Table.Serialize ( litHTable )
45+import Rel8.Table.Serialize ( lit )
4746import Rel8.Type ( DBType, typeInformation )
4847import Rel8.Type.Eq ( DBEq )
4948import Rel8.Type.Information ( TypeInformation(..) )
@@ -68,8 +67,8 @@ newtype Composite a = Composite
6867
6968instance DBComposite a => DBType (Composite a) where
7069 typeInformation = TypeInformation
71− { decode = Hasql.composite (Composite . fromResult @_ @(HKD a Expr) <$> decoder)
72− , encode = encoder . litHTable . toResult @_ @(HKD a Expr) . unComposite
70+ { decode = Hasql.composite (Composite . fromHKD <$> decoder)
71+ , encode = encoder . lit . toColumns . toHKD . unComposite
7372 , typeName = compositeTypeName @a
7473 }
7574
@@ -104,7 +103,7 @@ class (DBType a, HKDable a) => DBComposite a where
104103-- single column expression, by combining them into a PostgreSQL composite
105104-- type.
106105compose :: DBComposite a => HKD a Expr -> Expr a
107−compose = castExpr . fromPrimExpr . encoder . toColumns
106+compose = castExpr . fromPrimExpr . encoder
108107
109108
110109-- | Expand a composite type into a 'HKD'.
@@ -113,23 +112,23 @@ compose = castExpr . fromPrimExpr . encoder . toColumns
113112decompose :: forall a. DBComposite a => Expr a -> HKD a Expr
114113decompose (toPrimExpr -> a) = fromColumns $ htabulate \field ->
115114 case hfield names field of
116− Name name -> case hfield hspecs field of
117− Spec {} -> fromPrimExpr $ Opaleye.CompositeExpr a name
115+ N (Name name) -> case hfield hspecs field of
116+ SSpec {} -> E $ fromPrimExpr $ Opaleye.CompositeExpr a name
118117 where
119118 names = toColumns (compositeFields @a)
120119
121120
122−decoder :: HTable t => Hasql.Composite (t Result)
123−decoder = unwrapApplicative $ htabulateA \field ->
121+decoder :: Table Result a => Hasql.Composite a
122+decoder = fmap fromColumns $ unwrapApplicative $ htabulateA \field ->
124123 case hfield hspecs field of
125− Spec {nullity, info} -> WrapApplicative $ Identity <$>
124+ SSpec {nullity, info} -> WrapApplicative $ R <$>
126125 case nullity of
127126 Null -> Hasql.field $ Hasql.nullable $ decode info
128127 NotNull -> Hasql.field $ Hasql.nonNullable $ decode info
129128
130129
131−encoder :: HTable t => t Expr -> Opaleye.PrimExpr
132−encoder a = Opaleye.FunExpr "ROW" exprs
130+encoder :: Table Expr a => a -> Opaleye.PrimExpr
131+encoder (toColumns -> a) = Opaleye.FunExpr "ROW" exprs
133132 where
134133 exprs = getConst $ htabulateA \field -> case hfield a field of
135− expr -> Const [toPrimExpr expr]
134+ E (toPrimExpr -> expr) -> Const [expr]
src/Rel8/Type/Enum.hs−1
@@ -106,7 +106,6 @@ class (DBType a, Enumable a) => DBEnum a where
106106
107107-- | Types that are sum types, where each constructor is unary (that is, has no
108108-- fields).
109−type Enumable :: Type -> Constraint
110109class (Generic a, GEnumable (Rep a)) => Enumable a
111110instance (Generic a, GEnumable (Rep a)) => Enumable a
112111
src/Rel8/Type/JSONBEncoded.hs−4
@@ -1,5 +1,3 @@
1−{-# language StandaloneKindSignatures #-}
2−
31module Rel8.Type.JSONBEncoded ( JSONBEncoded(..) ) where
42
53-- aeson
@@ -8,7 +6,6 @@ import Data.Aeson.Types ( parseEither )
86
97-- base
108import Data.Bifunctor ( first )
11−import Data.Kind ( Type )
129import Prelude
1310
1411-- hasql
@@ -23,7 +20,6 @@ import Data.Text ( pack )
2320
2421
2522-- | Like 'Rel8.JSONEncoded', but works for @jsonb@ columns.
26−type JSONBEncoded :: Type -> Type
2723newtype JSONBEncoded a = JSONBEncoded { fromJSONBEncoded :: a }
2824
2925
src/Rel8/Type/JSONEncoded.hs−4
@@ -1,5 +1,3 @@
1−{-# language StandaloneKindSignatures #-}
2−
31module Rel8.Type.JSONEncoded ( JSONEncoded(..) ) where
42
53-- aeson
@@ -7,7 +5,6 @@ import Data.Aeson ( FromJSON, ToJSON, parseJSON, toJSON )
75import Data.Aeson.Types ( parseEither )
86
97-- base
10−import Data.Kind ( Type )
118import Prelude
129
1310-- rel8
@@ -18,7 +15,6 @@ import Rel8.Type.Information ( parseTypeInformation )
1815-- | A deriving-via helper type for column types that store a Haskell value
1916-- using a JSON encoding described by @aeson@'s 'ToJSON' and 'FromJSON' type
2017-- classes.
21−type JSONEncoded :: Type -> Type
2218newtype JSONEncoded a = JSONEncoded { fromJSONEncoded :: a }
2319
2420
src/Rel8/Type/Num.hs+1−4
@@ -18,7 +18,6 @@ import Prelude
1818
1919-- rel8
2020import Rel8.Type ( DBType )
21−import Rel8.Type.Ord ( DBOrd )
2221
2322-- scientific
2423import Data.Scientific ( Scientific )
@@ -40,14 +39,13 @@ instance DBNum Scientific
4039-- expressions. This is a Rel8 concept, and allows us to provide
4140-- 'fromIntegral'.
4241type DBIntegral :: Type -> Constraint
43−class (DBNum a, DBOrd a) => DBIntegral a
42+class DBNum a => DBIntegral a
4443instance DBIntegral Int16
4544instance DBIntegral Int32
4645instance DBIntegral Int64
4746
4847
4948-- | The class of database types that support the @/@ operator.
50−type DBFractional :: Type -> Constraint
5149class DBNum a => DBFractional a
5250instance DBFractional Float
5351instance DBFractional Double
@@ -55,7 +53,6 @@ instance DBFractional Scientific
5553
5654
5755-- | The class of database types that support the @/@ operator.
58−type DBFloating :: Type -> Constraint
5956class DBFractional a => DBFloating a
6057instance DBFloating Float
6158instance DBFloating Double
src/Rel8/Type/Ord.hs+2
@@ -75,6 +75,7 @@ instance Sql DBOrd a => DBOrd (NonEmpty a)
7575-- | The class of database types that support the @max@ aggregation function.
7676type DBMax :: Type -> Constraint
7777class DBOrd a => DBMax a
78+instance DBMax Bool
7879instance DBMax Char
7980instance DBMax Int16
8081instance DBMax Int32
@@ -100,6 +101,7 @@ instance Sql DBMax a => DBMax (NonEmpty a)
100101-- | The class of database types that support the @min@ aggregation function.
101102type DBMin :: Type -> Constraint
102103class DBOrd a => DBMin a
104+instance DBMin Bool
103105instance DBMin Char
104106instance DBMin Int16
105107instance DBMin Int32
src/Rel8/Type/ReadShow.hs−3
@@ -1,12 +1,10 @@
11{-# language ScopedTypeVariables #-}
2−{-# language StandaloneKindSignatures #-}
32{-# language TypeApplications #-}
43{-# language ViewPatterns #-}
54
65module Rel8.Type.ReadShow ( ReadShow(..) ) where
76
87-- base
9−import Data.Kind ( Type )
108import Data.Proxy ( Proxy( Proxy ) )
119import Data.Typeable ( Typeable, typeRep )
1210import Prelude
@@ -22,7 +20,6 @@ import qualified Data.Text as Text
2220
2321-- | A deriving-via helper type for column types that store a Haskell value
2422-- using a Haskell's 'Read' and 'Show' type classes.
25−type ReadShow :: Type -> Type
2623newtype ReadShow a = ReadShow { fromReadShow :: a }
2724
2825
src/Rel8/Type/Tag.hs+1−2
@@ -21,7 +21,7 @@ import Prelude
2121import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
2222
2323-- rel8
24−import Rel8.Expr ( Expr )
24+import {-# SOURCE #-} Rel8.Expr ( Expr )
2525import Rel8.Expr.Eq ( (==.) )
2626import Rel8.Expr.Opaleye ( zipPrimExprsWith )
2727import Rel8.Expr.Serialize ( litExpr )
@@ -90,7 +90,6 @@ instance DBMonoid MaybeTag where
9090 memptyExpr = litExpr mempty
9191
9292
93−type Tag :: Type
9493newtype Tag = Tag Text
9594 deriving newtype
9695 ( Eq, Ord, Read, Show
src/Rel8/Window.hsdeleted−97
@@ -1,97 +0,0 @@
1−{-# language DerivingVia #-}
2−{-# language FlexibleContexts #-}
3−{-# language GeneralizedNewtypeDeriving #-}
4−{-# language ScopedTypeVariables #-}
5−{-# language StandaloneKindSignatures #-}
6−{-# language TypeApplications #-}
7−
8−module Rel8.Window
9− ( Window(..)
10− , Partition
11− , over
12− , partitionBy
13− , orderPartitionBy
14− )
15−where
16−
17−-- base
18−import Data.Functor.Const ( Const( Const ), getConst )
19−import Data.Functor.Contravariant ( Contravariant, contramap )
20−import Data.Kind ( Type )
21−import Prelude
22−
23−-- opaleye
24−import qualified Opaleye.Internal.Window as Opaleye
25−import qualified Opaleye.Internal.PackMap as Opaleye
26−
27−-- profunctors
28−import Data.Profunctor ( Profunctor )
29−
30−-- product-profunctors
31−import Data.Profunctor.Product ( ProductProfunctor, (****), purePP )
32−
33−-- rel8
34−import Rel8.Expr.Opaleye ( toColumn, toPrimExpr )
35−import Rel8.Order( Order( Order ) )
36−import Rel8.Schema.HTable ( hfield, htabulateA )
37−import Rel8.Table ( Columns, toColumns )
38−import Rel8.Table.Eq ( EqTable )
39−
40−-- semigroupoids
41−import Data.Functor.Apply ( Apply, WrappedApplicative(..) )
42−
43−
44−-- | 'Window' is an applicative functor that represents expressions that
45−-- contain
46−-- [window functions](https://www.postgresql.org/docs/current/tutorial-window.html).
47−-- 'Rel8.Query.Window.window' can be used to
48−-- evaluate these expressions over a particular query.
49−type Window :: Type -> Type -> Type
50−newtype Window a b = Window (Opaleye.Windows a b)
51− deriving newtype (Profunctor)
52− deriving newtype (Functor, Applicative)
53− deriving (Apply) via (WrappedApplicative (Window a))
54−
55−
56−instance ProductProfunctor Window where
57− purePP = pure
58− (****) = (<*>)
59−
60−
61−-- | In PostgreSQL, window functions must specify the \"window\" or
62−-- \"partition\" over which they operate. The syntax for this looks like:
63−-- @SUM(salary) OVER (PARTITION BY department)@. The Rel8 type 'Partition'
64−-- represents everything that comes after @OVER@.
65−--
66−-- 'Partition' is a 'Monoid', so 'Window's created with 'partitionBy' and
67−-- 'orderWindowBy' can be combined using '<>'.
68−type Partition :: Type -> Type
69−newtype Partition a = Partition (Opaleye.Window a)
70− deriving newtype (Contravariant, Semigroup, Monoid)
71−
72−
73−-- | 'over' adds a 'Partition' to a 'Window' expression.
74−--
75−-- @@@
76−-- 'Rel8.Table.Window.cumulative' ('Rel8.Expr.Aggregate.sum' . salary) `over` 'partitionBy' department <> 'orderPartitionBy' (salary >$< 'Rel8.desc')
77−-- @@@
78−over :: Window a b -> Partition a -> Window a b
79−over (Window (Opaleye.Windows (Opaleye.PackMap w))) (Partition p) =
80− Window $ Opaleye.Windows $ Opaleye.PackMap $ \f ->
81− w (\(o, p') -> f (o, p' <> p))
82−infixl 1 `over`
83−
84−
85−-- | Restricts a window function to operate only the group of rows that share
86−-- the same value(s) for the given expression(s).
87−partitionBy :: forall b a. EqTable b => (a -> b) -> Partition a
88−partitionBy f =
89− Partition $ contramap (toColumns . f) $ getConst $
90− htabulateA @(Columns b) $ \field ->
91− Const $ Opaleye.partitionBy (toColumn . toPrimExpr . (`hfield` field))
92−
93−
94−-- | Controls the order in which rows are processed by window functions. This
95−-- does not need to match the ordering of the overall query.
96−orderPartitionBy :: Order a -> Partition a
97−orderPartitionBy (Order ordering) = Partition $ Opaleye.orderPartitionBy ordering
tests/Main.hs+161−320
@@ -1,11 +1,12 @@
1−{-# language BangPatterns #-}
21{-# language BlockArguments #-}
2+{-# language DataKinds #-}
33{-# language DeriveAnyClass #-}
44{-# language DeriveGeneric #-}
55{-# language DerivingStrategies #-}
6+{-# language DisambiguateRecordFields #-}
67{-# language FlexibleContexts #-}
78{-# language FlexibleInstances #-}
8−{-# language MonoLocalBinds #-}
9+{-# language GADTs #-}
910{-# language NamedFieldPuns #-}
1011{-# language OverloadedStrings #-}
1112{-# language RecordWildCards #-}
@@ -13,15 +14,17 @@
1314{-# language StandaloneDeriving #-}
1415{-# language TypeApplications #-}
1516
17+{-# options_ghc -fno-warn-redundant-constraints #-}
18+
1619module Main
1720 ( main
1821 )
1922where
2023
2124-- base
22−import Control.Applicative ( empty, liftA2, liftA3 )
23−import Control.Exception ( bracket, throwIO )
24−import Control.Monad ( (>=>), void )
25+import Control.Applicative ( liftA2, liftA3 )
26+import Control.Monad (void)
27+import Control.Monad.IO.Class ( MonadIO, liftIO )
2528import Data.Bifunctor ( bimap )
2629import Data.Foldable ( for_ )
2730import Data.Int ( Int32, Int64 )
@@ -35,27 +38,25 @@ import GHC.Generics ( Generic )
3538import qualified Data.ByteString.Lazy
3639
3740-- case-insensitive
38−import Data.CaseInsensitive ( mk )
41+import Data.CaseInsensitive (mk)
3942
4043-- containers
41−import Data.Containers.ListUtils ( nubOrdOn )
4244import qualified Data.Map.Strict as Map
4345
4446-- hasql
45−import Hasql.Connection ( Connection, acquire, release )
47+import Hasql.Connection ( Connection, acquire, release )
4648import Hasql.Session ( sql, run )
4749
48−-- hasql-transaction
49−import Hasql.Transaction ( Transaction, condemn, statement )
50−import qualified Hasql.Transaction.Sessions as Hasql
51−
5250-- hedgehog
5351import Hedgehog ( property, (===), forAll, cover, diff, evalM, PropertyT, TestT, test, Gen )
5452import qualified Hedgehog.Gen as Gen
5553import qualified Hedgehog.Range as Range
5654
57−-- mmorph
58−import Control.Monad.Morph ( hoist )
55+-- lifted-base
56+import Control.Exception.Lifted ( bracket, throwIO, bracket_ )
57+
58+-- monad-control
59+import Control.Monad.Trans.Control ( MonadBaseControl )
5960
6061-- rel8
6162import Rel8 ( Result )
@@ -71,16 +72,12 @@ import Test.Tasty
7172import Test.Tasty.Hedgehog ( testProperty )
7273
7374-- text
74−import Data.Text ( Text, pack, unpack )
75+import Data.Text (Text, pack)
7576import qualified Data.Text.Lazy
76−import Data.Text.Encoding ( decodeUtf8 )
7777
7878-- time
7979import Data.Time
8080
81−-- transformers
82−import Control.Monad.Trans.Class ( lift )
83−
8481-- tmp-postgres
8582import qualified Database.Postgres.Temp as TmpPostgres
8683
@@ -116,17 +113,14 @@ tests =
116113 , testCatMaybeTable getTestDatabase
117114 , testCatMaybe getTestDatabase
118115 , testMaybeTable getTestDatabase
119− , testAggregateMaybeTable getTestDatabase
120116 , testNestedTables getTestDatabase
121117 , testMaybeTableApplicative getTestDatabase
122118 , testLogicalFixities getTestDatabase
123119 , testUpdate getTestDatabase
124120 , testDelete getTestDatabase
125− , testUpsert getTestDatabase
126121 , testSelectNestedPairs getTestDatabase
127122 , testSelectArray getTestDatabase
128123 , testNestedMaybeTable getTestDatabase
129− , testEvaluate getTestDatabase
130124 ]
131125
132126 where
@@ -138,29 +132,25 @@ tests =
138132 flip run conn do
139133 sql "CREATE EXTENSION citext"
140134 sql "CREATE TABLE test_table ( column1 text not null, column2 bool not null )"
141− sql "CREATE TABLE unique_table ( \"key\" text not null unique, \"value\" text not null )"
142− sql "CREATE SEQUENCE test_seq"
143135
144136 return db
145137
146138 stopTestDatabase = TmpPostgres.stop
147139
148140
149−connect :: TmpPostgres.DB -> IO Connection
150−connect = acquire . TmpPostgres.toConnectionString >=> either (maybe empty (fail . unpack . decodeUtf8)) pure
151−
152−
153141databasePropertyTest
154142 :: TestName
155− -> ((TestT Transaction () -> PropertyT IO ()) -> PropertyT IO ())
143+ -> (((Connection -> TestT IO ()) -> PropertyT IO ()) -> PropertyT IO ())
156144 -> IO TmpPostgres.DB -> TestTree
157145databasePropertyTest testName f getTestDatabase =
158− withResource (connect =<< getTestDatabase) release $ \c ->
146+ withResource connect release $ \c ->
159147 testProperty testName $ property do
160− connection <- lift c
161− f $ test . hoist \m -> do
162− e <- run (Hasql.transaction Hasql.Serializable Hasql.Write (m <* condemn)) connection
163− either throwIO pure e
148+ connection <- liftIO c
149+ f \g -> test $ rollingBack connection $ g connection
150+
151+ where
152+
153+ connect = either (error . show) return =<< acquire . TmpPostgres.toConnectionString =<< getTestDatabase
164154
165155
166156data TestTable f = TestTable
@@ -192,17 +182,18 @@ testSelectTestTable :: IO TmpPostgres.DB -> TestTree
192182testSelectTestTable = databasePropertyTest "Can SELECT TestTable" \transaction -> do
193183 rows <- forAll $ Gen.list (Range.linear 0 10) genTestTable
194184
195− transaction do
196− selected <- lift do
197− statement () $ Rel8.insert Rel8.Insert
198− { into = testTableSchema
199− , rows = Rel8.values $ map Rel8.lit rows
200− , onConflict = Rel8.DoNothing
201− , returning = pure ()
202− }
185+ transaction \connection -> do
186+ void do
187+ liftIO $ Rel8.insert connection
188+ Rel8.Insert
189+ { into = testTableSchema
190+ , rows = map Rel8.lit rows
191+ , onConflict = Rel8.DoNothing
192+ , returning = Rel8.NumberOfRowsAffected
193+ }
203194
204− statement () $ Rel8.select do
205− Rel8.each testTableSchema
195+ selected <- liftIO $ Rel8.select connection do
196+ Rel8.each testTableSchema
206197
207198 sort selected === sort rows
208199
@@ -219,12 +210,11 @@ testWhere_ = databasePropertyTest "WHERE (Rel8.where_)" \transaction -> do
219210
220211 let expected = filter (\t -> testTableColumn2 t == magicBool) rows
221212
222− transaction do
223− selected <- lift do
224− statement () $ Rel8.select do
225− t <- Rel8.values $ Rel8.lit <$> rows
226− Rel8.where_ $ testTableColumn2 t Rel8.==. Rel8.lit magicBool
227− return t
213+ transaction \connection -> do
214+ selected <- liftIO $ Rel8.select connection do
215+ t <- Rel8.values $ Rel8.lit <$> rows
216+ Rel8.where_ $ testTableColumn2 t Rel8.==. Rel8.lit magicBool
217+ return t
228218
229219 sort selected === sort expected
230220
@@ -237,12 +227,11 @@ testFilter :: IO TmpPostgres.DB -> TestTree
237227testFilter = databasePropertyTest "filter" \transaction -> do
238228 rows <- forAll $ Gen.list (Range.linear 1 10) genTestTable
239229
240− transaction do
230+ transaction \connection -> do
241231 let expected = filter testTableColumn2 rows
242232
243− selected <- lift do
244− statement () $ Rel8.select do
245− Rel8.filter testTableColumn2 =<< Rel8.values (Rel8.lit <$> rows)
233+ selected <- liftIO $ Rel8.select connection
234+ $ Rel8.filter testTableColumn2 =<< Rel8.values (Rel8.lit <$> rows)
246235
247236 sort selected === sort expected
248237
@@ -257,10 +246,9 @@ testLimit = databasePropertyTest "LIMIT (Rel8.limit)" \transaction -> do
257246
258247 n <- forAll $ Gen.integral (Range.linear 0 10)
259248
260− transaction do
261− selected <- lift do
262− statement () $ Rel8.select do
263− Rel8.limit n $ Rel8.values (Rel8.lit <$> rows)
249+ transaction \connection -> do
250+ selected <- liftIO $ Rel8.select connection do
251+ Rel8.limit n $ Rel8.values (Rel8.lit <$> rows)
264252
265253 diff (length selected) (<=) (fromIntegral n)
266254
@@ -278,10 +266,9 @@ testUnion = databasePropertyTest "UNION (Rel8.union)" \transaction -> evalM do
278266 left <- forAll $ Gen.list (Range.linear 0 10) genTestTable
279267 right <- forAll $ Gen.list (Range.linear 0 10) genTestTable
280268
281− transaction do
282− selected <- lift do
283− statement () $ Rel8.select do
284− Rel8.values (Rel8.lit <$> nub left) `Rel8.union` Rel8.values (Rel8.lit <$> nub right)
269+ transaction \connection -> do
270+ selected <- liftIO $ Rel8.select connection do
271+ Rel8.values (Rel8.lit <$> nub left) `Rel8.union` Rel8.values (Rel8.lit <$> nub right)
285272
286273 sort selected === sort (nub (left ++ right))
287274
@@ -290,11 +277,9 @@ testDistinct :: IO TmpPostgres.DB -> TestTree
290277testDistinct = databasePropertyTest "DISTINCT (Rel8.distinct)" \transaction -> do
291278 rows <- forAll $ Gen.list (Range.linear 0 10) genTestTable
292279
293− transaction do
294− selected <- lift do
295− statement () $ Rel8.select do
296− Rel8.distinct do
297− Rel8.values (Rel8.lit <$> rows)
280+ transaction \connection -> do
281+ selected <- liftIO $ Rel8.select connection $ Rel8.distinct do
282+ Rel8.values (Rel8.lit <$> rows)
298283
299284 sort selected === nub (sort rows)
300285
@@ -307,10 +292,8 @@ testExists :: IO TmpPostgres.DB -> TestTree
307292testExists = databasePropertyTest "EXISTS (Rel8.exists)" \transaction -> do
308293 rows <- forAll $ Gen.list (Range.linear 0 10) genTestTable
309294
310− transaction do
311− exists <- lift do
312− statement () $ Rel8.select do
313− Rel8.exists $ Rel8.values $ Rel8.lit <$> rows
295+ transaction \connection -> do
296+ exists <- liftIO $ Rel8.select connection $ Rel8.exists $ Rel8.values $ Rel8.lit <$> rows
314297
315298 case rows of
316299 [] -> exists === [False]
@@ -321,10 +304,9 @@ testOptional :: IO TmpPostgres.DB -> TestTree
321304testOptional = databasePropertyTest "Rel8.optional" \transaction -> do
322305 rows <- forAll $ Gen.list (Range.linear 0 10) genTestTable
323306
324− transaction do
325− selected <- lift do
326− statement () $ Rel8.select do
327− Rel8.optional $ Rel8.values (Rel8.lit <$> rows)
307+ transaction \connection -> do
308+ selected <- liftIO $ Rel8.select connection do
309+ Rel8.optional $ Rel8.values (Rel8.lit <$> rows)
328310
329311 case rows of
330312 [] -> selected === [Nothing]
@@ -335,10 +317,9 @@ testAnd :: IO TmpPostgres.DB -> TestTree
335317testAnd = databasePropertyTest "AND (&&.)" \transaction -> do
336318 (x, y) <- forAll $ liftA2 (,) Gen.bool Gen.bool
337319
338− transaction do
339− [result] <- lift do
340− statement () $ Rel8.select do
341− pure $ Rel8.lit x Rel8.&&. Rel8.lit y
320+ transaction \connection -> do
321+ [result] <- liftIO $ Rel8.select connection $ pure $
322+ Rel8.lit x Rel8.&&. Rel8.lit y
342323
343324 result === (x && y)
344325
@@ -347,10 +328,9 @@ testOr :: IO TmpPostgres.DB -> TestTree
347328testOr = databasePropertyTest "OR (||.)" \transaction -> do
348329 (x, y) <- forAll $ liftA2 (,) Gen.bool Gen.bool
349330
350− transaction do
351− [result] <- lift do
352− statement () $ Rel8.select $ pure $
353− Rel8.lit x Rel8.||. Rel8.lit y
331+ transaction \connection -> do
332+ [result] <- liftIO $ Rel8.select connection $ pure $
333+ Rel8.lit x Rel8.||. Rel8.lit y
354334
355335 result === (x || y)
356336
@@ -359,10 +339,9 @@ testLogicalFixities :: IO TmpPostgres.DB -> TestTree
359339testLogicalFixities = databasePropertyTest "Logical operator fixities" \transaction -> do
360340 (u, v, w, x) <- forAll $ (,,,) <$> Gen.bool <*> Gen.bool <*> Gen.bool <*> Gen.bool
361341
362− transaction do
363− [result] <- lift do
364− statement () $ Rel8.select do
365− pure $ Rel8.lit u Rel8.||. Rel8.lit v Rel8.&&. Rel8.lit w Rel8.==. Rel8.lit x
342+ transaction \connection -> do
343+ [result] <- liftIO $ Rel8.select connection $ pure $
344+ Rel8.lit u Rel8.||. Rel8.lit v Rel8.&&. Rel8.lit w Rel8.==. Rel8.lit x
366345
367346 result === (u || v && w == x)
368347
@@ -371,10 +350,9 @@ testNot :: IO TmpPostgres.DB -> TestTree
371350testNot = databasePropertyTest "NOT (not_)" \transaction -> do
372351 x <- forAll Gen.bool
373352
374− transaction do
375− [result] <- lift do
376− statement () $ Rel8.select do
377− pure $ Rel8.not_ $ Rel8.lit x
353+ transaction \connection -> do
354+ [result] <- liftIO $ Rel8.select connection $ pure $
355+ Rel8.not_ $ Rel8.lit x
378356
379357 result === not x
380358
@@ -383,10 +361,9 @@ testBool :: IO TmpPostgres.DB -> TestTree
383361testBool = databasePropertyTest "ifThenElse_" \transaction -> do
384362 (x, y, z) <- forAll $ liftA3 (,,) Gen.bool Gen.bool Gen.bool
385363
386− transaction do
387− [result] <- lift do
388− statement () $ Rel8.select do
389− pure $ Rel8.bool (Rel8.lit z) (Rel8.lit y) (Rel8.lit x)
364+ transaction \connection -> do
365+ [result] <- liftIO $ Rel8.select connection $ pure $
366+ Rel8.bool (Rel8.lit z) (Rel8.lit y) (Rel8.lit x)
390367
391368 result === if x then y else z
392369
@@ -398,10 +375,9 @@ testAp = databasePropertyTest "Cartesian product (<*>)" \transaction -> do
398375 (Gen.list (Range.linear 1 10) genTestTable)
399376 (Gen.list (Range.linear 1 10) genTestTable)
400377
401− transaction do
402− result <- lift do
403− statement () $ Rel8.select do
404− liftA2 (,) (Rel8.values (Rel8.lit <$> rows1)) (Rel8.values (Rel8.lit <$> rows2))
378+ transaction \connection -> do
379+ result <- liftIO $ Rel8.select connection $ do
380+ liftA2 (,) (Rel8.values (Rel8.lit <$> rows1)) (Rel8.values (Rel8.lit <$> rows2))
405381
406382 sort result === sort (liftA2 (,) rows1 rows2)
407383
@@ -430,22 +406,21 @@ testDBType getTestDatabase = testGroup "DBType instances"
430406 where
431407 dbTypeTest :: (Eq a, Show a, Rel8.DBType a) => TestName -> Gen a -> TestTree
432408 dbTypeTest name generator = testGroup name
433− [ databasePropertyTest name (t generator) getTestDatabase
434− , databasePropertyTest ("Maybe " <> name) (t (Gen.maybe generator)) getTestDatabase
409+ [ databasePropertyTest name (t (==) generator) getTestDatabase
410+ , databasePropertyTest ("Maybe " <> name) (t (==) (Gen.maybe generator)) getTestDatabase
435411 ]
436412
437− t :: forall a b. (Eq a, Show a, Rel8.Sql Rel8.DBType a)
438− => Gen a
439− -> (TestT Transaction () -> PropertyT IO b)
413+ t :: forall a b. (Show a, Rel8.Sql Rel8.DBType a)
414+ => (a -> a -> Bool)
415+ -> Gen a
416+ -> ((Connection -> TestT IO ()) -> PropertyT IO b)
440417 -> PropertyT IO b
441− t generator transaction = do
418+ t eq generator transaction = do
442419 x <- forAll generator
443420
444− transaction do
445− [res] <- lift do
446− statement () $ Rel8.select do
447− pure (Rel8.litExpr x)
448− diff res (==) x
421+ transaction \connection -> do
422+ [res] <- liftIO $ Rel8.select connection $ pure (Rel8.litExpr x)
423+ diff res eq x
449424
450425 genDay :: Gen Day
451426 genDay = do
@@ -487,38 +462,39 @@ testDBEq getTestDatabase = testGroup "DBEq instances"
487462
488463 t :: forall a. (Eq a, Show a, Rel8.Sql Rel8.DBEq a)
489464 => Gen a
490− -> (TestT Transaction () -> PropertyT IO ())
465+ -> ((Connection -> TestT IO ()) -> PropertyT IO ())
491466 -> PropertyT IO ()
492467 t generator transaction = do
493468 (x, y) <- forAll (liftA2 (,) generator generator)
494469
495− transaction do
496− [res] <- lift do
497− statement () $ Rel8.select do
498− pure $ Rel8.litExpr x Rel8.==. Rel8.litExpr y
470+ transaction \connection -> do
471+ [res] <- liftIO $ Rel8.select connection $ pure $ Rel8.litExpr x Rel8.==. Rel8.litExpr y
499472 res === (x == y)
500473
474+ cover 1 "Equal" $ x == y
475+ cover 1 "Not Equal" $ x /= y
476+
501477
502478testTableEquality :: IO TmpPostgres.DB -> TestTree
503479testTableEquality = databasePropertyTest "TestTable equality" \transaction -> do
504480 (x, y) <- forAll $ liftA2 (,) genTestTable genTestTable
505481
506− transaction do
507− [eq] <- lift do
508− statement () $ Rel8.select do
509− pure $ Rel8.lit x Rel8.==: Rel8.lit y
482+ transaction \connection -> do
483+ [eq] <- liftIO $ Rel8.select connection do
484+ pure $ Rel8.lit x Rel8.==: Rel8.lit y
510485
511486 eq === (x == y)
512487
488+ cover 1 "Equal" $ x == y
489+ cover 1 "Not Equal" $ x /= y
490+
513491
514492testFromString :: IO TmpPostgres.DB -> TestTree
515493testFromString = databasePropertyTest "FromString" \transaction -> do
516494 str <- forAll $ Gen.list (Range.linear 0 10) Gen.unicode
517495
518− transaction do
519− [result] <- lift do
520− statement () $ Rel8.select do
521− pure $ fromString str
496+ transaction \connection -> do
497+ [result] <- liftIO $ Rel8.select connection $ pure $ fromString str
522498 result === pack str
523499
524500
@@ -526,11 +502,10 @@ testCatMaybeTable :: IO TmpPostgres.DB -> TestTree
526502testCatMaybeTable = databasePropertyTest "catMaybeTable" \transaction -> do
527503 rows <- forAll $ Gen.list (Range.linear 0 10) genTestTable
528504
529− transaction do
530− selected <- lift do
531− statement () $ Rel8.select do
532− testTable <- Rel8.values $ Rel8.lit <$> rows
533− Rel8.catMaybeTable $ Rel8.bool Rel8.nothingTable (pure testTable) (testTableColumn2 testTable)
505+ transaction \connection -> do
506+ selected <- liftIO $ Rel8.select connection do
507+ testTable <- Rel8.values $ Rel8.lit <$> rows
508+ Rel8.catMaybeTable $ Rel8.bool Rel8.nothingTable (pure testTable) (testTableColumn2 testTable)
534509
535510 sort selected === sort (filter testTableColumn2 rows)
536511
@@ -539,10 +514,9 @@ testCatMaybe :: IO TmpPostgres.DB -> TestTree
539514testCatMaybe = databasePropertyTest "catMaybe" \transaction -> evalM do
540515 rows <- forAll $ Gen.list (Range.linear 0 10) $ Gen.maybe Gen.bool
541516
542− transaction do
543− selected <- lift do
544− statement () $ Rel8.select do
545− Rel8.catNull =<< Rel8.values (map Rel8.lit rows)
517+ transaction \connection -> do
518+ selected <- evalM $ liftIO $ Rel8.select connection do
519+ Rel8.catNull =<< Rel8.values (map Rel8.lit rows)
546520
547521 sort selected === sort (catMaybes rows)
548522
@@ -551,38 +525,15 @@ testMaybeTable :: IO TmpPostgres.DB -> TestTree
551525testMaybeTable = databasePropertyTest "maybeTable" \transaction -> evalM do
552526 (rows, def) <- forAll $ liftA2 (,) (Gen.list (Range.linear 0 10) genTestTable) genTestTable
553527
554− transaction do
555− selected <- lift do
556− statement () $ Rel8.select do
557− Rel8.maybeTable (Rel8.lit def) id <$> Rel8.optional (Rel8.values (Rel8.lit <$> rows))
528+ transaction \connection -> do
529+ selected <- liftIO $ Rel8.select connection $
530+ Rel8.maybeTable (Rel8.lit def) id <$> Rel8.optional (Rel8.values (Rel8.lit <$> rows))
558531
559532 case rows of
560533 [] -> selected === [def]
561534 _ -> sort selected === sort rows
562535
563536
564−testAggregateMaybeTable :: IO TmpPostgres.DB -> TestTree
565−testAggregateMaybeTable = databasePropertyTest "aggregateMaybeTable" \transaction -> evalM do
566− rows <- forAll $ Gen.list (Range.linear 0 10) (Gen.maybe (Gen.int64 (Range.linear 0 10)))
567−
568− let
569− aggregate = go 0 False False
570− where
571− go !n nothing _ (Just a : as) = go (n + a) nothing True as
572− go n _ just (Nothing : as) = go n True just as
573− go _ False False [] = []
574− go _ True False [] = [Nothing]
575− go n False True [] = [Just n]
576− go n True True [] = [Nothing, Just n]
577−
578− transaction do
579− selected <- lift do
580− statement () $ Rel8.select do
581− Rel8.aggregate $ Rel8.aggregateMaybeTable Rel8.sum <$> Rel8.values (Rel8.lit <$> rows)
582−
583− sort selected === aggregate rows
584−
585−
586537data TwoTestTables f =
587538 TwoTestTables
588539 { testTable1 :: TestTable f
@@ -603,10 +554,9 @@ testNestedTables = databasePropertyTest "Nested TestTables" \transaction -> eval
603554 Gen.list (Range.linear 0 10) $
604555 liftA2 TwoTestTables genTestTable genTestTable
605556
606− transaction do
607− selected <- lift do
608− statement () $ Rel8.select do
609− Rel8.values (Rel8.lit <$> rows)
557+ transaction \connection -> do
558+ selected <- liftIO $ Rel8.select connection do
559+ Rel8.values (Rel8.lit <$> rows)
610560
611561 sort selected === sort rows
612562
@@ -616,12 +566,11 @@ testMaybeTableApplicative = databasePropertyTest "MaybeTable (<*>)" \transaction
616566 rows1 <- genRows
617567 rows2 <- genRows
618568
619− transaction do
620− selected <- lift do
621− statement () $ Rel8.select do
622− as <- Rel8.optional (Rel8.values (Rel8.lit <$> rows1))
623− bs <- Rel8.optional (Rel8.values (Rel8.lit <$> rows2))
624− pure $ liftA2 (,) as bs
569+ transaction \connection -> do
570+ selected <- liftIO $ Rel8.select connection do
571+ as <- Rel8.optional (Rel8.values (Rel8.lit <$> rows1))
572+ bs <- Rel8.optional (Rel8.values (Rel8.lit <$> rows2))
573+ pure $ liftA2 (,) as bs
625574
626575 case (rows1, rows2) of
627576 ([], []) -> selected === [Nothing]
@@ -633,6 +582,14 @@ testMaybeTableApplicative = databasePropertyTest "MaybeTable (<*>)" \transaction
633582 genRows = forAll do
634583 Gen.list (Range.linear 0 10) $ liftA2 TestTable (Gen.text (Range.linear 0 10) Gen.unicode) (pure True)
635584
585+rollingBack
586+ :: (MonadBaseControl IO m, MonadIO m)
587+ => Connection -> m a -> m a
588+rollingBack connection =
589+ bracket_
590+ (liftIO (run (sql "BEGIN") connection))
591+ (liftIO (run (sql "ROLLBACK") connection))
592+
636593
637594genTestTable :: Gen (TestTable Result)
638595genTestTable = do
@@ -645,19 +602,19 @@ testUpdate :: IO TmpPostgres.DB -> TestTree
645602testUpdate = databasePropertyTest "Can UPDATE TestTable" \transaction -> do
646603 rows <- forAll $ Gen.map (Range.linear 0 5) $ liftA2 (,) genTestTable genTestTable
647604
648− transaction do
649− selected <- lift do
650− statement () $ Rel8.insert Rel8.Insert
605+ transaction \connection -> do
606+ void $ liftIO $ Rel8.insert connection
607+ Rel8.Insert
651608 { into = testTableSchema
652− , rows = Rel8.values $ map Rel8.lit $ Map.keys rows
609+ , rows = map Rel8.lit $ Map.keys rows
653610 , onConflict = Rel8.DoNothing
654− , returning = pure ()
611+ , returning = Rel8.NumberOfRowsAffected
655612 }
656613
657− statement () $ Rel8.update Rel8.Update
614+ void $ liftIO $ Rel8.update connection
615+ Rel8.Update
658616 { target = testTableSchema
659− , from = pure ()
660− , set = \_ r ->
617+ , set = \r ->
661618 let updates = map (bimap Rel8.lit Rel8.lit) $ Map.toList rows
662619 in
663620 foldl
@@ -671,12 +628,12 @@ testUpdate = databasePropertyTest "Can UPDATE TestTable" \transaction -> do
671628 )
672629 r
673630 updates
674− , updateWhere = \_ _ -> Rel8.lit True
675− , returning = pure ()
631+ , updateWhere = \_ -> Rel8.lit True
632+ , returning = Rel8.NumberOfRowsAffected
676633 }
677634
678− statement () $ Rel8.select do
679− Rel8.each testTableSchema
635+ selected <- liftIO $ Rel8.select connection do
636+ Rel8.each testTableSchema
680637
681638 sort selected === sort (Map.elems rows)
682639
@@ -689,96 +646,29 @@ testDelete :: IO TmpPostgres.DB -> TestTree
689646testDelete = databasePropertyTest "Can DELETE TestTable" \transaction -> do
690647 rows <- forAll $ Gen.list (Range.linear 0 5) genTestTable
691648
692− transaction do
693− (deleted, selected) <- lift do
694− statement () $ Rel8.insert Rel8.Insert
649+ transaction \connection -> do
650+ void $ liftIO $ Rel8.insert connection
651+ Rel8.Insert
695652 { into = testTableSchema
696− , rows = Rel8.values $ map Rel8.lit rows
653+ , rows = map Rel8.lit rows
697654 , onConflict = Rel8.DoNothing
698− , returning = pure ()
655+ , returning = Rel8.NumberOfRowsAffected
699656 }
700657
701− deleted <- statement () $ Rel8.delete Rel8.Delete
658+ deleted <-
659+ liftIO $ Rel8.delete connection
660+ Rel8.Delete
702661 { from = testTableSchema
703− , using = pure ()
704− , deleteWhere = const testTableColumn2
662+ , deleteWhere = testTableColumn2
705663 , returning = Rel8.Projection id
706664 }
707665
708− selected <- statement () $ Rel8.select do
709− Rel8.each testTableSchema
710−
711− pure (deleted, selected)
666+ selected <- liftIO $ Rel8.select connection do
667+ Rel8.each testTableSchema
712668
713669 sort (deleted <> selected) === sort rows
714670
715671
716−data UniqueTable f = UniqueTable
717− { uniqueTableKey :: Rel8.Column f Text
718− , uniqueTableValue :: Rel8.Column f Text
719− }
720− deriving stock Generic
721− deriving anyclass Rel8.Rel8able
722−
723−
724−deriving stock instance Eq (UniqueTable Result)
725−deriving stock instance Ord (UniqueTable Result)
726−deriving stock instance Show (UniqueTable Result)
727−
728−
729−uniqueTableSchema :: Rel8.TableSchema (UniqueTable Rel8.Name)
730−uniqueTableSchema =
731− Rel8.TableSchema
732− { name = "unique_table"
733− , schema = Nothing
734− , columns = UniqueTable
735− { uniqueTableKey = "key"
736− , uniqueTableValue = "value"
737− }
738− }
739−
740−
741−genUniqueTable :: Gen (UniqueTable Result)
742−genUniqueTable = do
743− uniqueTableKey <- Gen.text (Range.linear 0 5) Gen.alphaNum
744− uniqueTableValue <- Gen.text (Range.linear 0 5) Gen.alphaNum
745− pure UniqueTable {..}
746−
747−
748−testUpsert :: IO TmpPostgres.DB -> TestTree
749−testUpsert = databasePropertyTest "Can UPSERT UniqueTable" \transaction -> do
750− as <- unique $ forAll $ Gen.list (Range.linear 0 20) genUniqueTable
751− bs <- unique $ forAll $ Gen.list (Range.linear 0 20) genUniqueTable
752−
753− transaction do
754− selected <- lift do
755− statement () $ Rel8.insert Rel8.Insert
756− { into = uniqueTableSchema
757− , rows = Rel8.values $ Rel8.lit <$> as
758− , onConflict = Rel8.DoNothing
759− , returning = pure ()
760− }
761−
762− statement () $ Rel8.insert Rel8.Insert
763− { into = uniqueTableSchema
764− , rows = Rel8.values $ Rel8.lit <$> bs
765− , onConflict = Rel8.DoUpdate Rel8.Upsert
766− { index = uniqueTableKey
767− , set = \UniqueTable {uniqueTableValue} old -> old {uniqueTableValue}
768− , updateWhere = \_ _ -> Rel8.true
769− }
770− , returning = pure ()
771− }
772−
773− statement () $ Rel8.select do
774− Rel8.each uniqueTableSchema
775−
776− fromUniqueTables selected === fromUniqueTables bs <> fromUniqueTables as
777− where
778− unique = fmap (nubOrdOn uniqueTableKey)
779− fromUniqueTables = Map.fromList . map \(UniqueTable key value) -> (key, value)
780−
781−
782672newtype HKNestedPair f = HKNestedPair { pairOne :: (TestTable f, TestTable f) }
783673 deriving stock Generic
784674 deriving anyclass Rel8.Rel8able
@@ -792,10 +682,9 @@ testSelectNestedPairs :: IO TmpPostgres.DB -> TestTree
792682testSelectNestedPairs = databasePropertyTest "Can SELECT nested pairs" \transaction -> do
793683 rows <- forAll $ Gen.list (Range.linear 0 10) $ HKNestedPair <$> liftA2 (,) genTestTable genTestTable
794684
795− transaction do
796− selected <- lift do
797− statement () $ Rel8.select do
798− Rel8.values $ map Rel8.lit rows
685+ transaction \connection -> do
686+ selected <- liftIO $ Rel8.select connection do
687+ Rel8.values $ map Rel8.lit rows
799688
800689 sort selected === sort rows
801690
@@ -804,22 +693,16 @@ testSelectArray :: IO TmpPostgres.DB -> TestTree
804693testSelectArray = databasePropertyTest "Can SELECT Arrays (with aggregation)" \transaction -> do
805694 rows <- forAll $ Gen.list (Range.linear 1 10) Gen.bool
806695
807− transaction do
808− selected <- lift do
809− statement () $ Rel8.select do
810− Rel8.many $ Rel8.values (map Rel8.lit rows)
696+ transaction \connection -> do
697+ selected <- liftIO $ Rel8.select connection do
698+ Rel8.many $ Rel8.values (map Rel8.lit rows)
811699
812700 selected === [foldMap pure rows]
813701
814− selected' <- lift do
815− statement () $ Rel8.select do
816− a <- Rel8.catListTable =<< do
817− Rel8.many $ Rel8.values (map Rel8.lit rows)
818− b <- Rel8.catListTable =<< do
819− Rel8.many $ Rel8.values (map Rel8.lit rows)
820− pure (a, b)
702+ selected' <- liftIO $ Rel8.select connection $ Rel8.catListTable =<< do
703+ Rel8.many $ Rel8.values (map Rel8.lit rows)
821704
822− selected' === liftA2 (,) rows rows
705+ selected' === rows
823706
824707
825708data NestedMaybeTable f = NestedMaybeTable
@@ -839,51 +722,9 @@ testNestedMaybeTable :: IO TmpPostgres.DB -> TestTree
839722testNestedMaybeTable = databasePropertyTest "Can nest MaybeTable within other tables" \transaction -> do
840723 let example = NestedMaybeTable { nmt1 = True, nmt2 = Just (TestTable "Hi" True) }
841724
842− transaction do
843− selected <- lift do
844− statement () $ Rel8.select do
845− x <- Rel8.values [Rel8.lit example]
846− pure $ Rel8.maybeTable (Rel8.lit False) (\_ -> Rel8.lit True) (nmt2 x)
725+ transaction \connection -> do
726+ selected <- liftIO $ Rel8.select connection do
727+ x <- Rel8.values [Rel8.lit example]
728+ pure $ Rel8.maybeTable (Rel8.lit False) (\_ -> Rel8.lit True) (nmt2 x)
847729
848730 selected === [True]
849−
850−
851−testEvaluate :: IO TmpPostgres.DB -> TestTree
852−testEvaluate = databasePropertyTest "evaluate has the evaluation order we expect" \transaction -> do
853−
854− transaction do
855− selected <- lift do
856− statement () $ Rel8.select do
857− x <- Rel8.values (Rel8.lit <$> ['a', 'b', 'c'])
858− y <- Rel8.evaluate (Rel8.nextval "test_seq")
859− pure (x, (y, y))
860−
861− normalize selected ===
862− [ ('a', (0, 0))
863− , ('b', (1, 1))
864− , ('c', (2, 2))
865− ]
866−
867− selected' <- lift do
868− statement () $ Rel8.select do
869− x <- Rel8.values (Rel8.lit <$> ['a', 'b', 'c'])
870− y <- Rel8.values (Rel8.lit <$> ['d', 'e', 'f'])
871− z <- Rel8.evaluate (Rel8.nextval "test_seq")
872− pure ((x, y), (z, z))
873−
874− normalize selected' ===
875− [ (('a', 'd'), (0, 0))
876− , (('a', 'e'), (1, 1))
877− , (('a', 'f'), (2, 2))
878− , (('b', 'd'), (3, 3))
879− , (('b', 'e'), (4, 4))
880− , (('b', 'f'), (5, 5))
881− , (('c', 'd'), (6, 6))
882− , (('c', 'e'), (7, 7))
883− , (('c', 'f'), (8, 8))
884− ]
885−
886− where
887− normalize :: [(x, (Int64, Int64))] -> [(x, (Int64, Int64))]
888− normalize [] = []
889− normalize xs@((_, (i, _)) : _) = map (fmap (\(a, b) -> (a - i, b - i))) xs