on 22deaf2 help sign in
.github/workflows/build.yaml+3−3
since splitsnapshots
unifiedsplit
@@ -9,14 +9,14 @@ jobs:
9 persist-credentials: false
9 persist-credentials: false
10 submodules: true
10 submodules: true
11
11
12− - uses: cachix/install-nix-action@v14.1
12+ - uses: cachix/install-nix-action@v12
13 with:
13 with:
14 nix_path: nixpkgs=channel:nixos-unstable
14 nix_path: nixpkgs=channel:nixos-unstable
15 extra_nix_config: |
15 extra_nix_config: |
16 trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=
16 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
18
18
19− - uses: cachix/cachix-action@v10
19+ - uses: cachix/cachix-action@v8
20 with:
20 with:
21 name: rel8
21 name: rel8
22 authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
22 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−
157# 1.0.0.0 (2021-06-18)
1# 1.0.0.0 (2021-06-18)
158
2
159* Initial release.
3* Initial release.
README.md−4
@@ -10,8 +10,4 @@ The main objectives of Rel8 are:
10
10
11* *Familiar*: writing Rel8 queries should feel like normal Haskell programming.
11* *Familiar*: writing Rel8 queries should feel like normal Haskell programming.
12
12
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−
17For more details, check out the [official documentation](https://rel8.readthedocs.io/en/latest/).
13For more details, check out the [official documentation](https://rel8.readthedocs.io/en/latest/).
cabal.project+5
@@ -1 +1,6 @@
1packages: .
1packages: .
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 @@
2-- will interpret them as local packages, and try to build them when we cabal
2-- will interpret them as local packages, and try to build them when we cabal
3-- build. The only reason we have to specify these is for Haskell.nix to know to
3-- build. The only reason we have to specify these is for Haskell.nix to know to
4-- override these packages by fetching them rather than using Hackage.
4-- 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
2 haskellNix = import (import ./nix/sources.nix)."haskell.nix" {};
2 haskellNix = import (import ./nix/sources.nix)."haskell.nix" {};
3
3
4− nixpkgsSrc = haskellNix.sources.nixpkgs-unstable;
4+ nixpkgsSrc = haskellNix.sources.nixpkgs-2009;
5
5
6 nixpkgsArgs = haskellNix.nixpkgsArgs;
6 nixpkgsArgs = haskellNix.nixpkgsArgs;
7
7
8− compiler-nix-name = "ghc924";
8+ compiler-nix-name = "ghc8104";
9
9
10 pkgs = import nixpkgsSrc nixpkgsArgs;
10 pkgs = import nixpkgsSrc nixpkgsArgs;
11
11
12−in
12+in
13pkgs.haskell-nix.project {
13pkgs.haskell-nix.project {
14 inherit compiler-nix-name;
14 inherit compiler-nix-name;
15
15
@@ -20,7 +20,7 @@ pkgs.haskell-nix.project {
20 src = ./.;
20 src = ./.;
21 };
21 };
22
22
23− modules = [
23+ modules = [
24 { packages.rel8 = {
24 { packages.rel8 = {
25 preCheck = ''
25 preCheck = ''
26 export PATH="${pkgs.postgresql}/bin:${"$PATH"}"
26 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+7−7
@@ -138,20 +138,20 @@ a query. The above query could thus be written as::
138
138
139``where_`` and ``filter`` allow you to filter rows based on an expression, but
139``where_`` and ``filter`` allow you to filter rows based on an expression, but
140sometimes we want to filter based on another query. For this, Rel8 offers
140sometimes we want to filter based on another query. For this, Rel8 offers
141−``present`` and ``absent``. For example, if all blog posts have a
141+``whereExists`` and ``whereNotExists``. For example, if all blog posts have a
142−list of tags, we could use ``present`` to find all blog posts that have been
142+list of tags, we could use ``whereExists`` to find all blog posts that have been
143tagged as "Haskell"::
143tagged as "Haskell"::
144
144
145 blogPost <- each blogPostSchema
145 blogPost <- each blogPostSchema
146− present do
146+ whereExists do
147 filter (("Haskell" ==.) . tagName) =<< tagFromBlogPost blogPost
147 filter (("Haskell" ==.) . tagName) =<< tagFromBlogPost blogPost
148
148
149−Notice that this example uses ``present`` with a query that itself uses
149+Notice that this example uses ``whereExists`` with a query that itself uses
150−``filter``. For each blog post, ``present`` causes that row to be selected
150+``filter``. For each blog post, ``whereExists`` causes that row to be selected
151only if the associated query finds a tag for that blog post with the ``tagName``
151only if the associated query finds a tag for that blog post with the ``tagName``
152"Haskell".
152"Haskell".
153
153
154−Like ``filter`` there is also a chaining variant of ``present`` - ``with``.
154+Like ``filter`` there is also a chaining variant of ``whereExists`` - ``with``.
155We could rewrite the above query using ``with`` as::
155We could rewrite the above query using ``with`` as::
156
156
157 haskellBlogPost <-
157 haskellBlogPost <-
@@ -198,7 +198,7 @@ and this query can be written in Rel8 as::
198 each authorSchema
198 each authorSchema
199
199
200 While this is a little more code over all, in our experience this style
200 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.
202
202
203Left (outer) joins with ``optional``
203Left (outer) joins with ``optional``
204------------------------------------
204------------------------------------
docs/cookbook.rst+7−9
@@ -142,25 +142,23 @@ rows, but can also return *tree*\s.
142To understand what this means, we'll consider a small example query for blog
142To understand what this means, we'll consider a small example query for blog
143posts. We want our query to return:
143posts. We want our query to return:
144
144
145−1. The latest 5 blog posts that have at least one tag each.
145+1. The latest 5 blog posts.
1462. For each blog post, all tags.
1462. 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.
148
148
149In Rel8, we can write this query as::
149In Rel8, we can write this query as::
150
150
151 latestBlogPosts = do
151 latestBlogPosts = do
152 post <- each postSchema
152 post <- each postSchema
153
153
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
156 tag <- each tagSchema
155 tag <- each tagSchema
157 where_ (tagPostId tag ==. postId post)
156 where_ (tagPostId tag ==. postId post)
158− return (tagName tag)
157+ return (listAgg (tagName tag))
159
158
160− -- Returns a `ListTable a` which ends up as a `[a]` after the query is run
161− latestComments <-
159+ latestComments <-
162− many $
160+ many $
163− limit 3 $
161+ limit 3 $
164 orderBy (commentCreatedAt >$< desc) do
162 orderBy (commentCreatedAt >$< desc) do
165 comment <- each commentSchema
163 comment <- each commentSchema
166 where_ (commentPostId comment ==. postId post)
164 where_ (commentPostId comment ==. postId post)
docs/index.rst+1−2
@@ -30,7 +30,6 @@ The main objectives of Rel8 are:
30 concepts/expr
30 concepts/expr
31 concepts/tables
31 concepts/tables
32 concepts/queries
32 concepts/queries
33− concepts/insert
34
33
35.. toctree::
34.. toctree::
36 :caption: Tips and tricks
35 :caption: Tips and tricks
@@ -50,4 +49,4 @@ More Resources
50
49
51* If you think you've found a bug, confusing behavior, or have a feature
50* If you think you've found a bug, confusing behavior, or have a feature
52 request, please raise an issue at `Rel8's issue tracker
51 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+13−29
@@ -147,19 +147,6 @@ And likewise for ``project`` and ``Project``::
147 }
147 }
148 }
148 }
149
149
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.
163
150
164.. note::
151.. note::
165
152
@@ -204,13 +191,10 @@ Recall we defined ``projectAuthorId`` as ``Column f AuthorId``. Now we have
204We'll see more about ``Expr`` soon, but you can think of ``Expr a`` as "SQL
191We'll see more about ``Expr`` soon, but you can think of ``Expr a`` as "SQL
205expressions of type ``a``\".
192expressions of type ``a``\".
206
193
207−To execute this ``Query``, we pass it to ``select``. This function takes both a
194+To execute this ``Query``, we pass it to ``select``::
208−database connection (which can be obtained using hasql's ``acquire`` function),
209−and the ``Query`` to run::
210
195
211− >>> Right conn <- acquire "user=postgres"
212− >>> :t select conn (each projectSchema)
196+ >>> :t select c (each projectSchema)
213− select conn (each projectSchema) :: MonadIO m => m [Project Result]
197+ select c (each projectSchema) :: MonadIO m => m [Project Result]
214
198
215When we ``select`` things containing ``Expr``\s, Rel8 builds a new response
199When we ``select`` things containing ``Expr``\s, Rel8 builds a new response
216table with the ``Result`` interpretation. This means you'll get back plain
200table with the ``Result`` interpretation. This means you'll get back plain
@@ -225,7 +209,7 @@ wrappping type at all.
225
209
226Putting this all together, we can run our first query::
210Putting this all together, we can run our first query::
227
211
228− >>> select conn (each projectSchema) >>= mapM_ print
212+ >>> select c (each projectSchema) >>= mapM_ print
229 Project {projectAuthorId = 1, projectName = "rel8"}
213 Project {projectAuthorId = 1, projectName = "rel8"}
230 Project {projectAuthorId = 2, projectName = "aeson"}
214 Project {projectAuthorId = 2, projectName = "aeson"}
231 Project {projectAuthorId = 2, projectName = "text"}
215 Project {projectAuthorId = 2, projectName = "text"}
@@ -235,7 +219,7 @@ sometimes we're only interested in a subset of the columns of a table. To
235restrict the returned columns, we can specify a projection by using ``Query``\s
219restrict the returned columns, we can specify a projection by using ``Query``\s
236``Functor`` instance::
220``Functor`` instance::
237
221
238− >>> select conn $ projectName <$> each projectSchema
222+ >>> select c $ projectName <$> each projectSchema
239 ["rel8","aeson","text"]
223 ["rel8","aeson","text"]
240
224
241Joins
225Joins
@@ -251,7 +235,7 @@ We can do this by simply calling ``each`` twice, and then returning a tuple of
251their results::
235their results::
252
236
253 >>> :{
237 >>> :{
254− mapM_ print =<< select conn do
238+ mapM_ print =<< select c do
255 author <- each authorSchema
239 author <- each authorSchema
256 project <- each projectSchema
240 project <- each projectSchema
257 return (projectName project, authorName author)
241 return (projectName project, authorName author)
@@ -270,7 +254,7 @@ This isn't quite right, though, as we have ended up pairing up the wrong
270projects and authors. To fix this, we can use ``where_`` to restrict the
254projects and authors. To fix this, we can use ``where_`` to restrict the
271returned rows. We could write::
255returned rows. We could write::
272
256
273− select conn $ do
257+ select c $ do
274 author <- each authorSchema
258 author <- each authorSchema
275 project <- each projectSchema
259 project <- each projectSchema
276 where_ $ projectAuthorId project ==. authorId author
260 where_ $ projectAuthorId project ==. authorId author
@@ -287,7 +271,7 @@ for the particular joins in your database. In our case, this would be::
287Our final query is then::
271Our final query is then::
288
272
289 >>> :{
273 >>> :{
290− mapM_ print =<< select conn do
274+ mapM_ print =<< select c do
291 author <- each authorSchema
275 author <- each authorSchema
292 project <- projectsForAuthor author
276 project <- projectsForAuthor author
293 return (projectName project, authorName author)
277 return (projectName project, authorName author)
@@ -306,7 +290,7 @@ transformer to allow for the possibility of the join to fail.
306In our test database, we can see that there's another author who we haven't
290In our test database, we can see that there's another author who we haven't
307seen yet::
291seen yet::
308
292
309− >>> select conn $ authorName <$> each authorSchema
293+ >>> select c $ authorName <$> each authorSchema
310 ["Ollie","Bryan O'Sullivan","Emily Pillmore"]
294 ["Ollie","Bryan O'Sullivan","Emily Pillmore"]
311
295
312Emily wasn't returned in our earlier query because - in our database - she
296Emily wasn't returned in our earlier query because - in our database - she
@@ -314,7 +298,7 @@ doesn't have any registered projects. We can account for this partiality in our
314original query by wrapping the ``projectsForAuthor`` call with ``optional``::
298original query by wrapping the ``projectsForAuthor`` call with ``optional``::
315
299
316 >>> :{
300 >>> :{
317− mapM_ print =<< select conn do
301+ mapM_ print =<< select c do
318 author <- each authorSchema
302 author <- each authorSchema
319 mproject <- optional $ projectsForAuthor author
303 mproject <- optional $ projectsForAuthor author
320 return (authorName author, projectName <$> mproject)
304 return (authorName author, projectName <$> mproject)
@@ -359,8 +343,8 @@ focus on the /type/ of that query::
359 return (author, project)
343 return (author, project)
360 where
344 where
361
345
362− >>> :t select conn authorsAndProjects
346+ >>> :t select c authorsAndProjects
363− select conn authorsAndProjects
347+ select c authorsAndProjects
364 :: MonadIO m => m [(Author Result, Project Result)]
348 :: MonadIO m => m [(Author Result, Project Result)]
365
349
366
350
@@ -377,7 +361,7 @@ simply wrapping the call to ``projectsForAuthor`` with either ``some`` or
377author to have no projects::
361author to have no projects::
378
362
379 >>> :{
363 >>> :{
380− mapM_ print =<< select conn do
364+ mapM_ print =<< select c do
381 author <- each authorSchema
365 author <- each authorSchema
382 projectNames <- many $ projectName <$> projectsForAuthor author
366 projectNames <- many $ projectName <$> projectsForAuthor author
383 return (authorName author, projectNames)
367 return (authorName author, projectNames)
nix/sources.json+9−9
@@ -5,10 +5,10 @@
5 "homepage": "https://input-output-hk.github.io/haskell.nix",
5 "homepage": "https://input-output-hk.github.io/haskell.nix",
6 "owner": "input-output-hk",
6 "owner": "input-output-hk",
7 "repo": "haskell.nix",
7 "repo": "haskell.nix",
8− "rev": "0837156c56cb5350dd9bd84eb424187e00aa1e8a",
8+ "rev": "5b5a18e442df6e27ad8549d69b8868b62adb08c3",
9− "sha256": "1djmv5a0vfj78csw2cad6nicrs8iqrp928b9b3v4h7lwwhhzliwl",
9+ "sha256": "055mk7kmjppng66ppnb8kr96v4wwc8pcxfsls5gl77129hsz9aph",
10 "type": "tarball",
10 "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",
12 "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
12 "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
13 },
13 },
14 "niv": {
14 "niv": {
@@ -17,10 +17,10 @@
17 "homepage": "https://github.com/nmattia/niv",
17 "homepage": "https://github.com/nmattia/niv",
18 "owner": "nmattia",
18 "owner": "nmattia",
19 "repo": "niv",
19 "repo": "niv",
20− "rev": "acdb1726783637852c01e0ad639ac9cbec28d8c8",
20+ "rev": "af958e8057f345ee1aca714c1247ef3ba1c15f5e",
21− "sha256": "1wszl8q70z6wfgkh2dvgmhhry6qp9nar421qq2ifd17hq2jj9ck3",
21+ "sha256": "1qjavxabbrsh73yck5dcq8jggvh3r2jkbr6b5nlz5d9yrqm9255n",
22 "type": "tarball",
22 "type": "tarball",
23− "url": "https://github.com/nmattia/niv/archive/acdb1726783637852c01e0ad639ac9cbec28d8c8.tar.gz",
23+ "url": "https://github.com/nmattia/niv/archive/af958e8057f345ee1aca714c1247ef3ba1c15f5e.tar.gz",
24 "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
24 "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
25 },
25 },
26 "nixpkgs": {
26 "nixpkgs": {
@@ -29,10 +29,10 @@
29 "homepage": null,
29 "homepage": null,
30 "owner": "nixos",
30 "owner": "nixos",
31 "repo": "nixpkgs",
31 "repo": "nixpkgs",
32− "rev": "1c1f5649bb9c1b0d98637c8c365228f57126f361",
32+ "rev": "a6847cb5460b47241eb35d1a6588652411642066",
33− "sha256": "0f2nvdijyxfgl5kwyb4465pppd5vkhqxddx6v40k2s0z9jfhj0xl",
33+ "sha256": "0wb5hwgffr4in66qng22sfvbascwnq078q81ziwwayvmnkn73rrm",
34 "type": "tarball",
34 "type": "tarball",
35− "url": "https://github.com/nixos/nixpkgs/archive/1c1f5649bb9c1b0d98637c8c365228f57126f361.tar.gz",
35+ "url": "https://github.com/nixos/nixpkgs/archive/a6847cb5460b47241eb35d1a6588652411642066.tar.gz",
36 "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
36 "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
37 }
37 }
38}
38}
nix/sources.nix+20−66
@@ -6,53 +6,25 @@ let
6 # The fetchers. fetch_<type> fetches specs of type <type>.
6 # The fetchers. fetch_<type> fetches specs of type <type>.
7 #
7 #
8
8
9− fetch_file = pkgs: name: spec:
9+ fetch_file = pkgs: spec:
10− let
11− name' = sanitizeName name + "-src";
12− in
13− if spec.builtin or true then
10+ if spec.builtin or true then
14− builtins_fetchurl { inherit (spec) url sha256; name = name'; }
11+ builtins_fetchurl { inherit (spec) url sha256; }
15− else
12+ else
16− pkgs.fetchurl { inherit (spec) url sha256; name = name'; };
13+ pkgs.fetchurl { inherit (spec) url sha256; };
17
14
18 fetch_tarball = pkgs: name: spec:
15 fetch_tarball = pkgs: name: spec:
19 let
16 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";
21 in
20 in
22 if spec.builtin or true then
21 if spec.builtin or true then
23 builtins_fetchTarball { name = name'; inherit (spec) url sha256; }
22 builtins_fetchTarball { name = name'; inherit (spec) url sha256; }
24 else
23 else
25 pkgs.fetchzip { name = name'; inherit (spec) url sha256; };
24 pkgs.fetchzip { name = name'; inherit (spec) url sha256; };
26
25
27− fetch_git = name: spec:
26+ fetch_git = 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);
27+ builtins.fetchGit { url = spec.repo; inherit (spec) rev ref; };
56
28
57 fetch_local = spec: spec.path;
29 fetch_local = spec: spec.path;
58
30
@@ -68,21 +40,11 @@ let
68 # Various helpers
40 # Various helpers
69 #
41 #
70
42
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−
81 # The set of packages used when specs are fetched using non-builtins.
43 # The set of packages used when specs are fetched using non-builtins.
82− mkPkgs = sources: system:
44+ mkPkgs = sources:
83 let
45 let
84 sourcesNixpkgs =
46 sourcesNixpkgs =
85− import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) { inherit system; };
47+ import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) {};
86 hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath;
48 hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath;
87 hasThisAsNixpkgsPath = <nixpkgs> == ./.;
49 hasThisAsNixpkgsPath = <nixpkgs> == ./.;
88 in
50 in
@@ -102,9 +64,9 @@ let
102
64
103 if ! builtins.hasAttr "type" spec then
65 if ! builtins.hasAttr "type" spec then
104 abort "ERROR: niv spec ${name} does not have a 'type' attribute"
66 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
106 else if spec.type == "tarball" then fetch_tarball pkgs name spec
68 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
108 else if spec.type == "local" then fetch_local spec
70 else if spec.type == "local" then fetch_local spec
109 else if spec.type == "builtin-tarball" then fetch_builtin-tarball name
71 else if spec.type == "builtin-tarball" then fetch_builtin-tarball name
110 else if spec.type == "builtin-url" then fetch_builtin-url name
72 else if spec.type == "builtin-url" then fetch_builtin-url name
@@ -118,10 +80,7 @@ let
118 saneName = stringAsChars (c: if isNull (builtins.match "[a-zA-Z0-9]" c) then "_" else c) name;
80 saneName = stringAsChars (c: if isNull (builtins.match "[a-zA-Z0-9]" c) then "_" else c) name;
119 ersatz = builtins.getEnv "NIV_OVERRIDE_${saneName}";
81 ersatz = builtins.getEnv "NIV_OVERRIDE_${saneName}";
120 in
82 in
121− if ersatz == "" then drv else
83+ if ersatz == "" then drv else ersatz;
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}";
125
84
126 # Ports of functions for older nix versions
85 # Ports of functions for older nix versions
127
86
@@ -139,29 +98,25 @@ let
139
98
140 # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269
99 # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269
141 stringAsChars = f: s: concatStrings (map f (stringToCharacters s));
100 stringAsChars = f: s: concatStrings (map f (stringToCharacters s));
142− concatMapStrings = f: list: concatStrings (map f list);
143 concatStrings = builtins.concatStringsSep "";
101 concatStrings = builtins.concatStringsSep "";
144
102
145− # https://github.com/NixOS/nixpkgs/blob/8a9f58a375c401b96da862d969f66429def1d118/lib/attrsets.nix#L331
146− optionalAttrs = cond: as: if cond then as else {};
147−
148 # fetchTarball version that is compatible between all the versions of Nix
103 # 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:
150 let
105 let
151 inherit (builtins) lessThan nixVersion fetchTarball;
106 inherit (builtins) lessThan nixVersion fetchTarball;
152 in
107 in
153 if lessThan nixVersion "1.12" then
108 if lessThan nixVersion "1.12" then
154− fetchTarball ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; }))
109+ fetchTarball { inherit name url; }
155 else
110 else
156 fetchTarball attrs;
111 fetchTarball attrs;
157
112
158 # fetchurl version that is compatible between all the versions of Nix
113 # 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:
160 let
115 let
161 inherit (builtins) lessThan nixVersion fetchurl;
116 inherit (builtins) lessThan nixVersion fetchurl;
162 in
117 in
163 if lessThan nixVersion "1.12" then
118 if lessThan nixVersion "1.12" then
164− fetchurl ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; }))
119+ fetchurl { inherit url; }
165 else
120 else
166 fetchurl attrs;
121 fetchurl attrs;
167
122
@@ -180,8 +135,7 @@ let
180 mkConfig =
135 mkConfig =
181 { sourcesFile ? if builtins.pathExists ./sources.json then ./sources.json else null
136 { sourcesFile ? if builtins.pathExists ./sources.json then ./sources.json else null
182 , sources ? if isNull sourcesFile then {} else builtins.fromJSON (builtins.readFile sourcesFile)
137 , sources ? if isNull sourcesFile then {} else builtins.fromJSON (builtins.readFile sourcesFile)
183− , system ? builtins.currentSystem
184− , pkgs ? mkPkgs sources system
138+ , pkgs ? mkPkgs sources
185 }: rec {
139 }: rec {
186 # The sources, i.e. the attribute set of spec name to spec
140 # The sources, i.e. the attribute set of spec name to spec
187 inherit sources;
141 inherit sources;
rel8.cabal+18−45
@@ -1,52 +1,41 @@
1cabal-version: 2.0
1cabal-version: 2.0
2name: rel8
2name: rel8
3−version: 1.4.0.0
3+version: 1.0.0.0
4synopsis: Hey! Hey! Can u rel8?
4synopsis: Hey! Hey! Can u rel8?
5license: BSD3
5license: BSD3
6license-file: LICENSE
6license-file: LICENSE
7author: Oliver Charles
7author: Oliver Charles
8maintainer: ollie@ocharles.org.uk
8maintainer: ollie@ocharles.org.uk
9−homepage: https://github.com/circuithub/rel8
10−bug-reports: https://github.com/circuithub/rel8/issues
11build-type: Simple
9build-type: Simple
12extra-doc-files:
10extra-doc-files:
13 README.md
11 README.md
14 Changelog.md
12 Changelog.md
15
13
16−source-repository head
17− type: git
18− location: https://github.com/circuithub/rel8
19−
20library
14library
21 build-depends:
15 build-depends:
22 aeson
16 aeson
23− , base ^>= 4.14 || ^>=4.15 || ^>=4.16
17+ , base ^>= 4.14 || ^>=4.15
24− , bifunctors
25 , bytestring
18 , bytestring
26 , case-insensitive
19 , case-insensitive
27− , comonad
28 , contravariant
20 , contravariant
29− , hasql ^>= 1.4.5.1 || ^>= 1.5.0.0 || ^>= 1.6.0.0
21+ , hasql ^>= 1.4.5.1
30− , opaleye ^>= 0.9.6.1
22+ , opaleye ^>= 0.7.1.0
31− , pretty
32 , profunctors
23 , profunctors
33− , product-profunctors
34 , scientific
24 , scientific
35 , semialign
25 , semialign
36 , semigroupoids
26 , semigroupoids
37 , text
27 , text
38 , these
28 , these
39 , time
29 , time
30+ , transformers
40 , uuid
31 , uuid
41 default-language:
32 default-language:
42 Haskell2010
33 Haskell2010
43 ghc-options:
34 ghc-options:
44− -Werror=missing-methods -Werror=incomplete-patterns -Werror=missing-fields
45 -Weverything -Wno-unsafe -Wno-safe -Wno-missing-safe-haskell-mode
35 -Weverything -Wno-unsafe -Wno-safe -Wno-missing-safe-haskell-mode
46 -Wno-missing-import-lists -Wno-prepositive-qualified-module
36 -Wno-missing-import-lists -Wno-prepositive-qualified-module
47 -Wno-monomorphism-restriction
37 -Wno-monomorphism-restriction
48 -Wno-missing-local-signatures
38 -Wno-missing-local-signatures
49− -Wno-missing-kind-signatures
50 hs-source-dirs:
39 hs-source-dirs:
51 src
40 src
52 exposed-modules:
41 exposed-modules:
@@ -54,7 +43,6 @@ library
54 Rel8.Expr.Num
43 Rel8.Expr.Num
55 Rel8.Expr.Text
44 Rel8.Expr.Text
56 Rel8.Expr.Time
45 Rel8.Expr.Time
57− Rel8.Tabulate
58
46
59 other-modules:
47 other-modules:
60 Rel8.Aggregate
48 Rel8.Aggregate
@@ -66,14 +54,12 @@ library
66 Rel8.Column.List
54 Rel8.Column.List
67 Rel8.Column.Maybe
55 Rel8.Column.Maybe
68 Rel8.Column.NonEmpty
56 Rel8.Column.NonEmpty
69− Rel8.Column.Null
70 Rel8.Column.These
57 Rel8.Column.These
71
58
72 Rel8.Expr
59 Rel8.Expr
73 Rel8.Expr.Aggregate
60 Rel8.Expr.Aggregate
74 Rel8.Expr.Array
61 Rel8.Expr.Array
75 Rel8.Expr.Bool
62 Rel8.Expr.Bool
76− Rel8.Expr.Default
77 Rel8.Expr.Eq
63 Rel8.Expr.Eq
78 Rel8.Expr.Function
64 Rel8.Expr.Function
79 Rel8.Expr.Null
65 Rel8.Expr.Null
@@ -82,19 +68,21 @@ library
82 Rel8.Expr.Order
68 Rel8.Expr.Order
83 Rel8.Expr.Sequence
69 Rel8.Expr.Sequence
84 Rel8.Expr.Serialize
70 Rel8.Expr.Serialize
85− Rel8.Expr.Window
86
71
87 Rel8.FCF
72 Rel8.FCF
88
73
89 Rel8.Kind.Algebra
74 Rel8.Kind.Algebra
90 Rel8.Kind.Context
75 Rel8.Kind.Context
76+ Rel8.Kind.Labels
91
77
92 Rel8.Generic.Construction
78 Rel8.Generic.Construction
93 Rel8.Generic.Construction.ADT
79 Rel8.Generic.Construction.ADT
94 Rel8.Generic.Construction.Record
80 Rel8.Generic.Construction.Record
95 Rel8.Generic.Map
81 Rel8.Generic.Map
96 Rel8.Generic.Record
82 Rel8.Generic.Record
83+ Rel8.Generic.Reify
97 Rel8.Generic.Rel8able
84 Rel8.Generic.Rel8able
85+ Rel8.Generic.Rel8able.Test
98 Rel8.Generic.Table
86 Rel8.Generic.Table
99 Rel8.Generic.Table.ADT
87 Rel8.Generic.Table.ADT
100 Rel8.Generic.Table.Record
88 Rel8.Generic.Table.Record
@@ -109,23 +97,21 @@ library
109 Rel8.Query.Evaluate
97 Rel8.Query.Evaluate
110 Rel8.Query.Exists
98 Rel8.Query.Exists
111 Rel8.Query.Filter
99 Rel8.Query.Filter
112− Rel8.Query.Indexed
113 Rel8.Query.Limit
100 Rel8.Query.Limit
114 Rel8.Query.List
101 Rel8.Query.List
115 Rel8.Query.Maybe
102 Rel8.Query.Maybe
116 Rel8.Query.Null
103 Rel8.Query.Null
117 Rel8.Query.Opaleye
104 Rel8.Query.Opaleye
118 Rel8.Query.Order
105 Rel8.Query.Order
119− Rel8.Query.Rebind
120 Rel8.Query.Set
106 Rel8.Query.Set
121 Rel8.Query.SQL
107 Rel8.Query.SQL
122 Rel8.Query.These
108 Rel8.Query.These
123 Rel8.Query.Values
109 Rel8.Query.Values
124− Rel8.Query.Window
125
110
111+ Rel8.Schema.Context
112+ Rel8.Schema.Context.Label
126 Rel8.Schema.Context.Nullify
113 Rel8.Schema.Context.Nullify
127 Rel8.Schema.Dict
114 Rel8.Schema.Dict
128− Rel8.Schema.Field
129 Rel8.Schema.HTable
115 Rel8.Schema.HTable
130 Rel8.Schema.HTable.Either
116 Rel8.Schema.HTable.Either
131 Rel8.Schema.HTable.Identity
117 Rel8.Schema.HTable.Identity
@@ -141,28 +127,24 @@ library
141 Rel8.Schema.Kind
127 Rel8.Schema.Kind
142 Rel8.Schema.Name
128 Rel8.Schema.Name
143 Rel8.Schema.Null
129 Rel8.Schema.Null
130+ Rel8.Schema.Reify
144 Rel8.Schema.Result
131 Rel8.Schema.Result
145 Rel8.Schema.Spec
132 Rel8.Schema.Spec
133+ Rel8.Schema.Spec.ConstrainDBType
146 Rel8.Schema.Table
134 Rel8.Schema.Table
147
135
148 Rel8.Statement.Delete
136 Rel8.Statement.Delete
149 Rel8.Statement.Insert
137 Rel8.Statement.Insert
150− Rel8.Statement.OnConflict
151 Rel8.Statement.Returning
138 Rel8.Statement.Returning
152 Rel8.Statement.Select
139 Rel8.Statement.Select
153− Rel8.Statement.Set
154− Rel8.Statement.SQL
155 Rel8.Statement.Update
140 Rel8.Statement.Update
156− Rel8.Statement.Using
157 Rel8.Statement.View
141 Rel8.Statement.View
158− Rel8.Statement.Where
159
142
160 Rel8.Table
143 Rel8.Table
161 Rel8.Table.ADT
144 Rel8.Table.ADT
162 Rel8.Table.Aggregate
145 Rel8.Table.Aggregate
163 Rel8.Table.Alternative
146 Rel8.Table.Alternative
164 Rel8.Table.Bool
147 Rel8.Table.Bool
165− Rel8.Table.Cols
166 Rel8.Table.Either
148 Rel8.Table.Either
167 Rel8.Table.Eq
149 Rel8.Table.Eq
168 Rel8.Table.HKD
150 Rel8.Table.HKD
@@ -170,18 +152,16 @@ library
170 Rel8.Table.Maybe
152 Rel8.Table.Maybe
171 Rel8.Table.Name
153 Rel8.Table.Name
172 Rel8.Table.NonEmpty
154 Rel8.Table.NonEmpty
173− Rel8.Table.Null
174− Rel8.Table.Nullify
175 Rel8.Table.Opaleye
155 Rel8.Table.Opaleye
176 Rel8.Table.Ord
156 Rel8.Table.Ord
177 Rel8.Table.Order
157 Rel8.Table.Order
178− Rel8.Table.Projection
158+ Rel8.Table.Recontextualize
179 Rel8.Table.Rel8able
159 Rel8.Table.Rel8able
180 Rel8.Table.Serialize
160 Rel8.Table.Serialize
161+ Rel8.Table.Tag
181 Rel8.Table.These
162 Rel8.Table.These
182− Rel8.Table.Transpose
183 Rel8.Table.Undefined
163 Rel8.Table.Undefined
184− Rel8.Table.Window
164+ Rel8.Table.Unreify
185
165
186 Rel8.Type
166 Rel8.Type
187 Rel8.Type.Array
167 Rel8.Type.Array
@@ -200,9 +180,6 @@ library
200 Rel8.Type.Sum
180 Rel8.Type.Sum
201 Rel8.Type.Tag
181 Rel8.Type.Tag
202
182
203− Rel8.Window
204−
205−
206test-suite tests
183test-suite tests
207 type: exitcode-stdio-1.0
184 type: exitcode-stdio-1.0
208 build-depends:
185 build-depends:
@@ -211,9 +188,9 @@ test-suite tests
211 , case-insensitive
188 , case-insensitive
212 , containers
189 , containers
213 , hasql
190 , hasql
214− , hasql-transaction
215− , hedgehog ^>= 1.0 || ^>= 1.1
191+ , hedgehog ^>=1.0.2
216− , mmorph
192+ , lifted-base ^>=0.2.3.12
193+ , monad-control ^>=1.0.2.3
217 , rel8
194 , rel8
218 , scientific
195 , scientific
219 , tasty
196 , tasty
@@ -221,12 +198,8 @@ test-suite tests
221 , text
198 , text
222 , time
199 , time
223 , tmp-postgres ^>=1.34.1.0
200 , tmp-postgres ^>=1.34.1.0
224− , transformers
225 , uuid
201 , uuid
226
202
227− other-modules:
228− Rel8.Generic.Rel8able.Test
229−
230 main-is: Main.hs
203 main-is: Main.hs
231 hs-source-dirs: tests
204 hs-source-dirs: tests
232 default-language: Haskell2010
205 default-language: Haskell2010
shell.nix+1−1
@@ -1,6 +1,6 @@
1let
1let
2 sources = import ./nix/sources.nix;
2 sources = import ./nix/sources.nix;
3− pkgs = import sources.nixpkgs {overlays = [];};
3+ pkgs = import sources.nixpkgs {};
4 hsPkgs = import ./default.nix;
4 hsPkgs = import ./default.nix;
5in
5in
6 hsPkgs.shellFor {
6 hsPkgs.shellFor {
src/Rel8.hs+23−315
@@ -38,31 +38,27 @@ module Rel8
38 , HMaybe
38 , HMaybe
39 , HList
39 , HList
40 , HNonEmpty
40 , HNonEmpty
41− , HNull
42 , HThese
41 , HThese
43 , Lift
42 , Lift
44
43
45 , Table(..)
44 , Table(..)
46 , HTable
45 , HTable
47− , Transposes
46+ , Recontextualize
48 , AltTable((<|>:))
47 , AltTable((<|>:))
49 , AlternativeTable( emptyTable )
48 , AlternativeTable( emptyTable )
50− , EqTable(..), (==:), (/=:)
49+ , EqTable, (==:), (/=:)
51− , OrdTable(..), (<:), (<=:), (>:), (>=:), ascTable, descTable, greatest, least
50+ , OrdTable, ascTable, descTable
52 , lit
51 , lit
53 , bool
52 , bool
54 , case_
53 , case_
55− , castTable
56
54
57 -- ** @MaybeTable@
55 -- ** @MaybeTable@
58 , MaybeTable
56 , MaybeTable
59 , maybeTable, ($?), nothingTable, justTable
57 , maybeTable, ($?), nothingTable, justTable
60 , isNothingTable, isJustTable
58 , isNothingTable, isJustTable
61− , fromMaybeTable
62 , optional
59 , optional
63 , catMaybeTable
60 , catMaybeTable
64 , traverseMaybeTable
61 , traverseMaybeTable
65− , aggregateMaybeTable
66 , nameMaybeTable
62 , nameMaybeTable
67
63
68 -- ** @EitherTable@
64 -- ** @EitherTable@
@@ -72,7 +68,6 @@ module Rel8
72 , keepLeftTable
68 , keepLeftTable
73 , keepRightTable
69 , keepRightTable
74 , bitraverseEitherTable
70 , bitraverseEitherTable
75− , aggregateEitherTable
76 , nameEitherTable
71 , nameEitherTable
77
72
78 -- ** @TheseTable@
73 -- ** @TheseTable@
@@ -81,7 +76,6 @@ module Rel8
81 , isThisTable, isThatTable, isThoseTable
76 , isThisTable, isThatTable, isThoseTable
82 , hasHereTable, hasThereTable
77 , hasHereTable, hasThereTable
83 , justHereTable, justThereTable
78 , justHereTable, justThereTable
84− , alignMaybeTable
85 , alignBy
79 , alignBy
86 , keepHereTable, loseHereTable
80 , keepHereTable, loseHereTable
87 , keepThereTable, loseThereTable
81 , keepThereTable, loseThereTable
@@ -89,12 +83,11 @@ module Rel8
89 , keepThatTable, loseThatTable
83 , keepThatTable, loseThatTable
90 , keepThoseTable, loseThoseTable
84 , keepThoseTable, loseThoseTable
91 , bitraverseTheseTable
85 , bitraverseTheseTable
92− , aggregateTheseTable
93 , nameTheseTable
86 , nameTheseTable
94
87
95 -- ** @ListTable@
88 -- ** @ListTable@
96 , ListTable
89 , ListTable
97− , listTable, ($*)
90+ , listTable
98 , nameListTable
91 , nameListTable
99 , many
92 , many
100 , manyExpr
93 , manyExpr
@@ -103,44 +96,21 @@ module Rel8
103
96
104 -- ** @NonEmptyTable@
97 -- ** @NonEmptyTable@
105 , NonEmptyTable
98 , NonEmptyTable
106− , nonEmptyTable, ($+)
99+ , nonEmptyTable
107 , nameNonEmptyTable
100 , nameNonEmptyTable
108 , some
101 , some
109 , someExpr
102 , someExpr
110 , catNonEmptyTable
103 , catNonEmptyTable
111 , catNonEmpty
104 , catNonEmpty
112
105
113− -- ** @NullTable@
106+ -- ** @ADT@
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
127 , ADT, ADTable
107 , ADT, ADTable
128−
129− -- *** Deconstruction of ADTs
130− -- $deconstruction
131− , DeconstructADT, deconstructADT
132−
133− -- *** Construction of ADTs
134− -- $construction
135 , BuildADT, buildADT
108 , BuildADT, buildADT
136 , ConstructADT, constructADT
109 , ConstructADT, constructADT
137−
110+ , DeconstructADT, deconstructADT
138− -- *** Other ADT operations
111+ , NameADT, nameADT
139 , AggregateADT, aggregateADT
112 , AggregateADT, aggregateADT
140
113
141− -- *** Miscellaneous notes
142− -- $misc-notes
143−
144 -- ** @HKD@
114 -- ** @HKD@
145 , HKD, HKDable
115 , HKD, HKDable
146 , BuildHKD, buildHKD
116 , BuildHKD, buildHKD
@@ -165,7 +135,6 @@ module Rel8
165 -- ** @null@
135 -- ** @null@
166 , NotNull
136 , NotNull
167 , Nullable
137 , Nullable
168− , Homonullable
169 , null
138 , null
170 , nullify
139 , nullify
171 , nullable
140 , nullable
@@ -184,7 +153,6 @@ module Rel8
184 , (==.), (/=.), (==?), (/=?)
153 , (==.), (/=.), (==?), (/=?)
185 , in_
154 , in_
186 , boolExpr, caseExpr
155 , boolExpr, caseExpr
187− , like, ilike
188
156
189 -- ** Ordering
157 -- ** Ordering
190 , DBOrd
158 , DBOrd
@@ -202,13 +170,6 @@ module Rel8
202 , Query
170 , Query
203 , showQuery
171 , showQuery
204
172
205− -- ** Projection
206− , Projection
207− , Projectable( project )
208− , Biprojectable( biproject )
209− , Projecting
210− , Field
211−
212 -- ** Selecting rows
173 -- ** Selecting rows
213 , Selects
174 , Selects
214 , each
175 , each
@@ -217,8 +178,8 @@ module Rel8
217 -- ** Filtering
178 -- ** Filtering
218 , filter
179 , filter
219 , where_
180 , where_
220− , present
181+ , whereExists
221− , absent
182+ , whereNotExists
222 , distinct
183 , distinct
223 , distinctOn
184 , distinctOn
224 , distinctOnBy
185 , distinctOnBy
@@ -253,11 +214,10 @@ module Rel8
253 , countRows
214 , countRows
254 , groupBy
215 , groupBy
255 , listAgg, listAggExpr
216 , listAgg, listAggExpr
256− , mode
257 , nonEmptyAgg, nonEmptyAggExpr
217 , nonEmptyAgg, nonEmptyAggExpr
258 , DBMax, max
218 , DBMax, max
259 , DBMin, min
219 , DBMin, min
260− , DBSum, sum, sumWhere, avg
220+ , DBSum, sum, sumWhere
261 , DBString, stringAgg
221 , DBString, stringAgg
262 , count
222 , count
263 , countStar
223 , countStar
@@ -274,71 +234,45 @@ module Rel8
274 , nullsFirst
234 , nullsFirst
275 , nullsLast
235 , nullsLast
276
236
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−
303 -- * IO
237 -- * IO
304 , Serializable
238 , Serializable
305− , ToExprs
239+ , ToExprs(..)
240+ , FromExprs
306 , Result
241 , Result
307
242
308 -- * Running statements
243 -- * Running statements
309− -- $running
310−
311 -- ** @SELECT@
244 -- ** @SELECT@
312 , select
245 , select
313
246
314 -- ** @INSERT@
247 -- ** @INSERT@
315 , Insert(..)
248 , Insert(..)
316 , OnConflict(..)
249 , OnConflict(..)
317− , Upsert(..)
318 , insert
250 , insert
319− , unsafeDefault
320− , showInsert
321
251
322 -- ** @DELETE@
252 -- ** @DELETE@
323 , Delete(..)
253 , Delete(..)
324 , delete
254 , delete
325− , showDelete
326
255
327 -- ** @UPDATE@
256 -- ** @UPDATE@
328− , Update(..)
329 , update
257 , update
330− , showUpdate
258+ , Update(..)
331
259
332 -- ** @.. RETURNING@
260 -- ** @.. RETURNING@
333 , Returning(..)
261 , Returning(..)
334
262
335 -- ** @CREATE VIEW@
263 -- ** @CREATE VIEW@
336 , createView
264 , createView
337− , createOrReplaceView
338
265
339 -- ** Sequences
266 -- ** Sequences
340 , nextval
267 , nextval
268+
269+ , Evaluate
270+ , eval
341 , evaluate
271 , evaluate
272+
273+ -- * Implementation details
274+ , Labelable
275+ , HKDT(..)
342 ) where
276 ) where
343
277
344-- base
278-- base
@@ -353,12 +287,10 @@ import Rel8.Column.Lift
353import Rel8.Column.List
287import Rel8.Column.List
354import Rel8.Column.Maybe
288import Rel8.Column.Maybe
355import Rel8.Column.NonEmpty
289import Rel8.Column.NonEmpty
356−import Rel8.Column.Null
357import Rel8.Column.These
290import Rel8.Column.These
358import Rel8.Expr
291import Rel8.Expr
359import Rel8.Expr.Aggregate
292import Rel8.Expr.Aggregate
360import Rel8.Expr.Bool
293import Rel8.Expr.Bool
361−import Rel8.Expr.Default
362import Rel8.Expr.Eq
294import Rel8.Expr.Eq
363import Rel8.Expr.Function
295import Rel8.Expr.Function
364import Rel8.Expr.Null
296import Rel8.Expr.Null
@@ -367,8 +299,6 @@ import Rel8.Expr.Ord
367import Rel8.Expr.Order
299import Rel8.Expr.Order
368import Rel8.Expr.Serialize
300import Rel8.Expr.Serialize
369import Rel8.Expr.Sequence
301import Rel8.Expr.Sequence
370−import Rel8.Expr.Text ( like, ilike )
371−import Rel8.Expr.Window hiding ( cumulative )
372import Rel8.Generic.Rel8able ( KRel8able, Rel8able )
302import Rel8.Generic.Rel8able ( KRel8able, Rel8able )
373import Rel8.Order
303import Rel8.Order
374import Rel8.Query
304import Rel8.Query
@@ -379,19 +309,16 @@ import Rel8.Query.Either
379import Rel8.Query.Evaluate
309import Rel8.Query.Evaluate
380import Rel8.Query.Exists
310import Rel8.Query.Exists
381import Rel8.Query.Filter
311import Rel8.Query.Filter
382−import Rel8.Query.Indexed
383import Rel8.Query.Limit
312import Rel8.Query.Limit
384import Rel8.Query.List
313import Rel8.Query.List
385import Rel8.Query.Maybe
314import Rel8.Query.Maybe
386import Rel8.Query.Null
315import Rel8.Query.Null
387import Rel8.Query.Order
316import Rel8.Query.Order
388−import Rel8.Query.Rebind
389import Rel8.Query.SQL (showQuery)
317import Rel8.Query.SQL (showQuery)
390import Rel8.Query.Set
318import Rel8.Query.Set
391import Rel8.Query.These
319import Rel8.Query.These
392import Rel8.Query.Values
320import Rel8.Query.Values
393−import Rel8.Query.Window
321+import Rel8.Schema.Context.Label
394−import Rel8.Schema.Field
395import Rel8.Schema.HTable
322import Rel8.Schema.HTable
396import Rel8.Schema.Name
323import Rel8.Schema.Name
397import Rel8.Schema.Null hiding ( nullable )
324import Rel8.Schema.Null hiding ( nullable )
@@ -399,10 +326,8 @@ import Rel8.Schema.Result ( Result )
399import Rel8.Schema.Table
326import Rel8.Schema.Table
400import Rel8.Statement.Delete
327import Rel8.Statement.Delete
401import Rel8.Statement.Insert
328import Rel8.Statement.Insert
402−import Rel8.Statement.OnConflict
403import Rel8.Statement.Returning
329import Rel8.Statement.Returning
404import Rel8.Statement.Select
330import Rel8.Statement.Select
405−import Rel8.Statement.SQL
406import Rel8.Statement.Update
331import Rel8.Statement.Update
407import Rel8.Statement.View
332import Rel8.Statement.View
408import Rel8.Table
333import Rel8.Table
@@ -417,16 +342,12 @@ import Rel8.Table.List
417import Rel8.Table.Maybe
342import Rel8.Table.Maybe
418import Rel8.Table.Name
343import Rel8.Table.Name
419import Rel8.Table.NonEmpty
344import Rel8.Table.NonEmpty
420−import Rel8.Table.Null
421−import Rel8.Table.Opaleye ( castTable )
422import Rel8.Table.Ord
345import Rel8.Table.Ord
423import Rel8.Table.Order
346import Rel8.Table.Order
424−import Rel8.Table.Projection
347+import Rel8.Table.Recontextualize
425import Rel8.Table.Rel8able ()
348import Rel8.Table.Rel8able ()
426import Rel8.Table.Serialize
349import Rel8.Table.Serialize
427import Rel8.Table.These
350import Rel8.Table.These
428−import Rel8.Table.Transpose
429−import Rel8.Table.Window
430import Rel8.Type
351import Rel8.Type
431import Rel8.Type.Composite
352import Rel8.Type.Composite
432import Rel8.Type.Eq
353import Rel8.Type.Eq
@@ -441,216 +362,3 @@ import Rel8.Type.ReadShow
441import Rel8.Type.Semigroup
362import Rel8.Type.Semigroup
442import Rel8.Type.String
363import Rel8.Type.String
443import Rel8.Type.Sum
364import 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 @@
1{-# language DataKinds #-}
1{-# language DataKinds #-}
2+{-# language DerivingVia #-}
2{-# language FlexibleContexts #-}
3{-# language FlexibleContexts #-}
3{-# language FlexibleInstances #-}
4{-# language FlexibleInstances #-}
5+{-# language GADTs #-}
4{-# language MultiParamTypeClasses #-}
6{-# language MultiParamTypeClasses #-}
5−{-# language RankNTypes #-}
7+{-# language NamedFieldPuns #-}
8+{-# language PolyKinds #-}
6{-# language StandaloneKindSignatures #-}
9{-# language StandaloneKindSignatures #-}
7{-# language TypeFamilies #-}
10{-# language TypeFamilies #-}
8{-# language UndecidableInstances #-}
11{-# language UndecidableInstances #-}
12+{-# language UndecidableSuperClasses #-}
9
13
10module Rel8.Aggregate
14module Rel8.Aggregate
11− ( Aggregate(..), zipOutputs
15+ ( Aggregate(..), foldInputs, mapInputs
12− , unsafeMakeAggregate
16+ , Aggregator(..), unsafeMakeAggregate
13 , Aggregates
17 , Aggregates
18+ , Col( A, unA )
14 )
19 )
15where
20where
16
21
17-- base
22-- base
18−import Control.Applicative ( liftA2 )
23+import Data.Functor.Const ( Const( Const ), getConst )
19−import Data.Functor.Identity ( Identity( Identity ) )
24+import Data.Functor.Identity ( Identity )
20import Data.Kind ( Constraint, Type )
25import Data.Kind ( Constraint, Type )
21import Prelude
26import Prelude
22
27
23−-- profunctors
24−import Data.Profunctor ( dimap )
25−
26-- opaleye
28-- opaleye
27−import qualified Opaleye.Aggregate as Opaleye
29+import qualified Opaleye.Internal.Aggregate as Opaleye
28import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
30import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
29−import qualified Opaleye.Internal.Column as Opaleye
31+import qualified Opaleye.Internal.PackMap as Opaleye
30
32
31-- rel8
33-- rel8
32import Rel8.Expr ( Expr )
34import Rel8.Expr ( Expr )
35+import Rel8.Schema.Context ( Interpretation(..) )
36+import Rel8.Schema.Context.Label ( Labelable(..) )
33−import Rel8.Schema.HTable.Identity ( HIdentity(..) )
37+import Rel8.Schema.HTable.Identity ( HIdentity(..), HType )
34−import qualified Rel8.Schema.Kind as K
38+import Rel8.Schema.Name ( Name )
35import Rel8.Schema.Null ( Sql )
39import Rel8.Schema.Null ( Sql )
40+import Rel8.Schema.Reify ( notReify )
41+import Rel8.Schema.Result ( Result )
42+import Rel8.Schema.Spec ( Spec( Spec ) )
36import Rel8.Table
43import Rel8.Table
37 ( Table, Columns, Context, fromColumns, toColumns
44 ( Table, Columns, Context, fromColumns, toColumns
38− , FromExprs, fromResult, toResult
45+ , reify, unreify
39− , Transpose
40 )
46 )
41−import Rel8.Table.Transpose ( Transposes )
47+import Rel8.Table.Recontextualize ( Recontextualize )
42import Rel8.Type ( DBType )
48import Rel8.Type ( DBType )
43
49
44
50
45−-- | 'Aggregate' is a special context used by 'Rel8.aggregate'.
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.
46−type Aggregate :: K.Context
56+type Aggregate :: k -> Type
57+data Aggregate a where
47−newtype Aggregate a = Aggregate (Opaleye.Aggregator () (Expr a))
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)
48
66
49
67
50instance Sql DBType a => Table Aggregate (Aggregate a) where
68instance Sql DBType a => Table Aggregate (Aggregate a) where
51− type Columns (Aggregate a) = HIdentity a
69+ type Columns (Aggregate a) = HType a
52 type Context (Aggregate a) = Aggregate
70 type Context (Aggregate a) = Aggregate
53− type FromExprs (Aggregate a) = a
54− type Transpose to (Aggregate a) = to a
55
71
56− toColumns = HIdentity
72+ toColumns = HIdentity . A
57− fromColumns (HIdentity a) = a
73+ fromColumns (HIdentity (A a)) = a
58− toResult = HIdentity . Identity
74+
59− fromResult (HIdentity (Identity a)) = a
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)
60
93
61
94
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+
62−-- | @Aggregates a b@ means that the columns in @a@ are all 'Aggregate's
112+-- | @Aggregates a b@ means that the columns in @a@ are all 'Aggregate' 'Expr's
63−-- for the 'Expr' columns in @b@.
113+-- for the columns in @b@.
64type Aggregates :: Type -> Type -> Constraint
114type Aggregates :: Type -> Type -> Constraint
65−class Transposes Aggregate Expr aggregates exprs => Aggregates aggregates exprs
115+class Recontextualize Aggregate Expr aggregates exprs => Aggregates aggregates exprs
66−instance Transposes 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)
67
134
68
135
69−zipOutputs :: ()
136+type Aggregator :: Type
70− => (Expr a -> Expr b -> Expr c) -> Aggregate a -> Aggregate b -> Aggregate c
137+data Aggregator = Aggregator
71−zipOutputs f (Aggregate a) (Aggregate b) = Aggregate (liftA2 f a b)
138+ { operation :: Opaleye.AggrOp
139+ , ordering :: [Opaleye.OrderExpr]
140+ , distinction :: Opaleye.AggrDistinct
141+ }
72
142
73
143
74−unsafeMakeAggregate :: forall (input :: Type) (output :: Type) n n' a a'. ()
144+unsafeMakeAggregate :: ()
75 => (Expr input -> Opaleye.PrimExpr)
145 => (Expr input -> Opaleye.PrimExpr)
76 -> (Opaleye.PrimExpr -> Expr output)
146 -> (Opaleye.PrimExpr -> Expr output)
77− -> Opaleye.Aggregator (Opaleye.Field_ n a) (Opaleye.Field_ n' a')
147+ -> Maybe Aggregator
78 -> Expr input
148 -> Expr input
79 -> Aggregate output
149 -> Aggregate output
80unsafeMakeAggregate input output aggregator expr =
150unsafeMakeAggregate input output aggregator expr =
81− Aggregate $ dimap in_ out aggregator
151+ Aggregate $ Opaleye.Aggregator $ Opaleye.PackMap $ \f _ ->
82− where out = output . Opaleye.unColumn
152+ output <$> f (tuplize <$> aggregator, input expr)
83− in_ = Opaleye.Column . input . const expr
153+ where
154+ tuplize Aggregator {operation, ordering, distinction} =
155+ (operation, ordering, distinction)
src/Rel8/Column.hs+77−5
@@ -1,21 +1,38 @@
1{-# language DataKinds #-}
1{-# language DataKinds #-}
2+{-# language FlexibleContexts #-}
3+{-# language LambdaCase #-}
4+{-# language MultiParamTypeClasses #-}
2{-# language StandaloneKindSignatures #-}
5{-# language StandaloneKindSignatures #-}
3{-# language TypeFamilies #-}
6{-# language TypeFamilies #-}
4
7
5module Rel8.Column
8module Rel8.Column
6− ( Column
9+ ( Column, AColumn(..)
7 , TColumn
10 , TColumn
8 )
11 )
9where
12where
10
13
11-- base
14-- base
12import Data.Kind ( Type )
15import Data.Kind ( Type )
13−import Prelude ()
16+import Prelude
14
17
15-- rel8
18-- rel8
19+import Rel8.Aggregate ( Aggregate, Col( A ) )
20+import Rel8.Expr ( Expr, Col( E ) )
16import Rel8.FCF ( Eval, Exp )
21import Rel8.FCF ( Eval, Exp )
22+import Rel8.Kind.Context ( SContext(..), Reifiable( contextSing ) )
23+import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) )
17import qualified Rel8.Schema.Kind as K
24import 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 )
19
36
20
37
21-- | This type family is used to specify columns in 'Rel8able's. In @Column f
38-- | This type family is used to specify columns in 'Rel8able's. In @Column f
@@ -23,8 +40,63 @@ import Rel8.Schema.Result ( Result )
23-- 'Rel8able' definitions), and @a@ is the type of the column.
40-- 'Rel8able' definitions), and @a@ is the type of the column.
24type Column :: K.Context -> Type -> Type
41type Column :: K.Context -> Type -> Type
25type family Column context a where
42type family Column context a where
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
26− Column Result a = 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)
27− Column context a = context 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)
28
100
29
101
30data TColumn :: K.Context -> Type -> Exp Type
102data TColumn :: K.Context -> Type -> Exp Type
src/Rel8/Column/ADT.hs+68−3
@@ -1,23 +1,88 @@
1{-# language DataKinds #-}
1{-# language DataKinds #-}
2+{-# language LambdaCase #-}
3+{-# language MultiParamTypeClasses #-}
2{-# language StandaloneKindSignatures #-}
4{-# language StandaloneKindSignatures #-}
3{-# language TypeFamilies #-}
5{-# language TypeFamilies #-}
6+{-# language UndecidableInstances #-}
4
7
5module Rel8.Column.ADT
8module Rel8.Column.ADT
6− ( HADT
9+ ( HADT, AHADT(..)
7 )
10 )
8where
11where
9
12
10-- base
13-- base
11import Data.Kind ( Type )
14import Data.Kind ( Type )
12−import Prelude ()
15+import Prelude
13
16
14-- rel8
17-- rel8
18+import Rel8.Generic.Rel8able ( GColumns )
19+import Rel8.Kind.Context ( SContext(..), Reifiable, contextSing )
20+import Rel8.Schema.Context ( Col )
15import qualified Rel8.Schema.Kind as K
21import qualified Rel8.Schema.Kind as K
22+import Rel8.Schema.Reify ( Reify, hreify, hunreify )
16import Rel8.Schema.Result ( Result )
23import 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 )
18
30
19
31
20type HADT :: K.Context -> K.Rel8able -> Type
32type HADT :: K.Context -> K.Rel8able -> Type
21type family HADT context t where
33type family HADT context t where
34+ HADT (Reify context) t = AHADT context t
22 HADT Result t = t Result
35 HADT Result t = t Result
23 HADT context t = ADT t context
36 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 @@
1{-# language DataKinds #-}
1{-# language DataKinds #-}
2+{-# language FlexibleContexts #-}
3+{-# language LambdaCase #-}
4+{-# language MultiParamTypeClasses #-}
2{-# language StandaloneKindSignatures #-}
5{-# language StandaloneKindSignatures #-}
3−{-# language TypeFamilyDependencies #-}
6+{-# language TypeFamilies #-}
7+{-# language UndecidableInstances #-}
4
8
5module Rel8.Column.Either
9module Rel8.Column.Either
6− ( HEither
10+ ( HEither, AHEither(..)
7 )
11 )
8where
12where
9
13
10-- base
14-- base
15+import Control.Applicative ( liftA2 )
16+import Data.Bifunctor ( Bifunctor, bimap )
11import Data.Kind ( Type )
17import Data.Kind ( Type )
12import Prelude
18import Prelude
13
19
14-- rel8
20-- 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 )
15import qualified Rel8.Schema.Kind as K
26import qualified Rel8.Schema.Kind as K
27+import Rel8.Schema.Name ( Name(..) )
28+import Rel8.Schema.Reify ( Reify, hreify, hunreify )
16import Rel8.Schema.Result ( Result )
29import Rel8.Schema.Result ( Result )
30+import Rel8.Table
31+ ( Table, Columns, Context, fromColumns, toColumns
32+ , Unreify, reify, unreify
33+ )
17import Rel8.Table.Either ( EitherTable )
34import Rel8.Table.Either ( EitherTable )
35+import Rel8.Table.Recontextualize ( Recontextualize )
18
36
19
37
20-- | Nest an 'Either' value within a 'Rel8able'. @HEither f a b@ will produce a
38-- | Nest an 'Either' value within a 'Rel8able'. @HEither f a b@ will produce a
21-- 'EitherTable' @a b@ in the 'Expr' context, and a 'Either' @a b@ in the
39-- 'EitherTable' @a b@ in the 'Expr' context, and a 'Either' @a b@ in the
22-- 'Result' context.
40-- 'Result' context.
23type HEither :: K.Context -> Type -> Type -> Type
41type 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
25 HEither Result = Either
47 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 @@
1{-# language DataKinds #-}
1{-# language DataKinds #-}
2+{-# language LambdaCase #-}
3+{-# language MultiParamTypeClasses #-}
2{-# language StandaloneKindSignatures #-}
4{-# language StandaloneKindSignatures #-}
3{-# language TypeFamilies #-}
5{-# language TypeFamilies #-}
4
6
5module Rel8.Column.Lift
7module Rel8.Column.Lift
6− ( Lift
8+ ( Lift, ALift(..)
7 )
9 )
8where
10where
9
11
10-- base
12-- base
11import Data.Kind ( Type )
13import Data.Kind ( Type )
12−import Prelude ()
14+import Prelude
13
15
14-- rel8
16-- rel8
17+import Rel8.Generic.Rel8able ( GColumns )
18+import Rel8.Kind.Context ( Reifiable(..), SContext(..) )
19+import Rel8.Schema.Context ( Col )
15import qualified Rel8.Schema.Kind as K
20import qualified Rel8.Schema.Kind as K
21+import Rel8.Schema.Reify ( Reify, hreify, hunreify )
16import Rel8.Schema.Result ( Result )
22import 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 )
18
30
19
31
20type Lift :: K.Context -> Type -> Type
32type Lift :: K.Context -> Type -> Type
21type family Lift context a where
33type family Lift context a where
34+ Lift (Reify context) a = ALift context a
22 Lift Result a = a
35 Lift Result a = a
23 Lift context a = HKD a context
36 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 @@
1{-# language DataKinds #-}
1{-# language DataKinds #-}
2+{-# language FlexibleContexts #-}
3+{-# language LambdaCase #-}
4+{-# language MultiParamTypeClasses #-}
2{-# language StandaloneKindSignatures #-}
5{-# language StandaloneKindSignatures #-}
3−{-# language TypeFamilyDependencies #-}
6+{-# language TypeFamilies #-}
7+{-# language UndecidableInstances #-}
4
8
5module Rel8.Column.List
9module Rel8.Column.List
6− ( HList
10+ ( HList, AHList(..)
7 )
11 )
8where
12where
9
13
10-- base
14-- base
11import Data.Kind ( Type )
15import Data.Kind ( Type )
16+import Data.Type.Equality ( (:~:)( Refl ) )
12−import Prelude ()
17+import Prelude
13
18
14-- rel8
19-- 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 )
15import qualified Rel8.Schema.Kind as K
25import qualified Rel8.Schema.Kind as K
26+import Rel8.Schema.Name ( Name )
27+import Rel8.Schema.Reify ( Reify, hreify, hunreify )
16import Rel8.Schema.Result ( Result )
28import 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 )
18
36
19
37
20-- | Nest a list within a 'Rel8able'. @HList f a@ will produce a 'ListTable'
38-- | Nest a list within a 'Rel8able'. @HList f a@ will produce a 'ListTable'
21-- @a@ in the 'Expr' context, and a @[a]@ in the 'Result' context.
39-- @a@ in the 'Expr' context, and a @[a]@ in the 'Result' context.
22type HList :: K.Context -> Type -> Type
40type 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
24 HList Result = []
46 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 @@
1{-# language DataKinds #-}
1{-# language DataKinds #-}
2+{-# language FlexibleContexts #-}
3+{-# language GADTs #-}
4+{-# language LambdaCase #-}
5+{-# language MultiParamTypeClasses #-}
2{-# language StandaloneKindSignatures #-}
6{-# language StandaloneKindSignatures #-}
3−{-# language TypeFamilyDependencies #-}
7+{-# language TypeFamilies #-}
8+{-# language UndecidableInstances #-}
4
9
5module Rel8.Column.Maybe
10module Rel8.Column.Maybe
6− ( HMaybe
11+ ( HMaybe, AHMaybe(..)
7 )
12 )
8where
13where
9
14
@@ -12,15 +17,111 @@ import Data.Kind ( Type )
12import Prelude
17import Prelude
13
18
14-- rel8
19-- 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 )
15import qualified Rel8.Schema.Kind as K
25import qualified Rel8.Schema.Kind as K
26+import Rel8.Schema.Name ( Name )
27+import Rel8.Schema.Reify ( Reify, hreify, hunreify )
16import Rel8.Schema.Result ( Result )
28import Rel8.Schema.Result ( Result )
29+import Rel8.Table
30+ ( Table, Columns, Context, fromColumns, toColumns
31+ , Unreify, reify, unreify
32+ )
17import Rel8.Table.Maybe ( MaybeTable )
33import Rel8.Table.Maybe ( MaybeTable )
34+import Rel8.Table.Recontextualize ( Recontextualize )
18
35
19
36
20-- | Nest a 'Maybe' value within a 'Rel8able'. @HMaybe f a@ will produce a
37-- | Nest a 'Maybe' value within a 'Rel8able'. @HMaybe f a@ will produce a
21-- 'MaybeTable' @a@ in the 'Expr' context, and a 'Maybe' @a@ in the 'Result'
38-- 'MaybeTable' @a@ in the 'Expr' context, and a 'Maybe' @a@ in the 'Result'
22-- context.
39-- context.
23type HMaybe :: K.Context -> Type -> Type
40type 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
25 HMaybe Result = Maybe
46 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 @@
1{-# language DataKinds #-}
1{-# language DataKinds #-}
2+{-# language FlexibleContexts #-}
3+{-# language LambdaCase #-}
4+{-# language MultiParamTypeClasses #-}
2{-# language StandaloneKindSignatures #-}
5{-# language StandaloneKindSignatures #-}
3−{-# language TypeFamilyDependencies #-}
6+{-# language TypeFamilies #-}
7+{-# language UndecidableInstances #-}
4
8
5module Rel8.Column.NonEmpty
9module Rel8.Column.NonEmpty
6− ( HNonEmpty
10+ ( HNonEmpty, AHNonEmpty(..)
7 )
11 )
8where
12where
9
13
10-- base
14-- base
11import Data.Kind ( Type )
15import Data.Kind ( Type )
12import Data.List.NonEmpty ( NonEmpty )
16import Data.List.NonEmpty ( NonEmpty )
17+import Data.Type.Equality ( (:~:)( Refl ) )
13−import Prelude ()
18+import Prelude
14
19
15-- rel8
20-- 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 )
16import qualified Rel8.Schema.Kind as K
26import qualified Rel8.Schema.Kind as K
27+import Rel8.Schema.Name ( Name )
28+import Rel8.Schema.Reify ( Reify, hreify, hunreify )
17import Rel8.Schema.Result ( Result )
29import 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 )
19
37
20
38
21-- | Nest a 'NonEmpty' list within a 'Rel8able'. @HNonEmpty f a@ will produce a
39-- | Nest a 'NonEmpty' list within a 'Rel8able'. @HNonEmpty f a@ will produce a
22-- 'NonEmptyTable' @a@ in the 'Expr' context, and a 'NonEmpty' @a@ in the
40-- 'NonEmptyTable' @a@ in the 'Expr' context, and a 'NonEmpty' @a@ in the
23-- 'Result' context.
41-- 'Result' context.
24type HNonEmpty :: K.Context -> Type -> Type
42type 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
26 HNonEmpty Result = NonEmpty
48 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 @@
1{-# language DataKinds #-}
1{-# language DataKinds #-}
2+{-# language FlexibleContexts #-}
3+{-# language GADTs #-}
4+{-# language LambdaCase #-}
5+{-# language MultiParamTypeClasses #-}
2{-# language StandaloneKindSignatures #-}
6{-# language StandaloneKindSignatures #-}
3−{-# language TypeFamilyDependencies #-}
7+{-# language TypeFamilies #-}
8+{-# language UndecidableInstances #-}
4
9
5module Rel8.Column.These
10module Rel8.Column.These
6− ( HThese
11+ ( HThese, AHThese(..)
7 )
12 )
8where
13where
9
14
10-- base
15-- base
16+import Control.Applicative ( liftA2 )
17+import Data.Bifunctor ( Bifunctor, bimap )
11import Data.Kind ( Type )
18import Data.Kind ( Type )
12−import Prelude ()
19+import Prelude
13
20
14-- rel8
21-- 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 )
15import qualified Rel8.Schema.Kind as K
27import qualified Rel8.Schema.Kind as K
28+import Rel8.Schema.Name ( Name )
29+import Rel8.Schema.Reify ( Reify, hreify, hunreify )
16import Rel8.Schema.Result ( Result )
30import 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 )
17import Rel8.Table.These ( TheseTable )
36import Rel8.Table.These ( TheseTable )
18
37
19-- these
38-- these
@@ -24,6 +43,124 @@ import Data.These ( These )
24-- 'TheseTable' @a b@ in the 'Expr' context, and a 'These' @a b@ in the
43-- 'TheseTable' @a b@ in the 'Expr' context, and a 'These' @a b@ in the
25-- 'Result' context.
44-- 'Result' context.
26type HThese :: K.Context -> Type -> Type -> Type
45type 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
28 HThese Result = These
51 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 @@
1{-# language DataKinds #-}
1{-# language DataKinds #-}
2{-# language DerivingStrategies #-}
2{-# language DerivingStrategies #-}
3−{-# language FlexibleContexts #-}
4{-# language FlexibleInstances #-}
3{-# language FlexibleInstances #-}
4+{-# language GADTs #-}
5{-# language MultiParamTypeClasses #-}
5{-# language MultiParamTypeClasses #-}
6+{-# language PolyKinds #-}
7+{-# language RoleAnnotations #-}
6{-# language ScopedTypeVariables #-}
8{-# language ScopedTypeVariables #-}
9+{-# language StandaloneDeriving #-}
7{-# language StandaloneKindSignatures #-}
10{-# language StandaloneKindSignatures #-}
8{-# language TypeApplications #-}
11{-# language TypeApplications #-}
9{-# language TypeFamilies #-}
12{-# language TypeFamilies #-}
10{-# language UndecidableInstances #-}
13{-# language UndecidableInstances #-}
14+{-# language UndecidableSuperClasses #-}
11
15
12module Rel8.Expr
16module Rel8.Expr
13 ( Expr(..)
17 ( Expr(..)
18+ , Col( E, unE )
14 )
19 )
15where
20where
16
21
17-- base
22-- base
18−import Data.Functor.Identity ( Identity( Identity ) )
23+import Data.Functor.Identity ( Identity )
24+import Data.Kind ( Type )
19import Data.String ( IsString, fromString )
25import Data.String ( IsString, fromString )
20import Prelude hiding ( null )
26import Prelude hiding ( null )
21
27
@@ -32,14 +38,17 @@ import Rel8.Expr.Opaleye
32 , zipPrimExprsWith
38 , zipPrimExprsWith
33 )
39 )
34import Rel8.Expr.Serialize ( litExpr )
40import Rel8.Expr.Serialize ( litExpr )
41+import Rel8.Schema.Context ( Interpretation, Col )
42+import Rel8.Schema.Context.Label ( Labelable, labeler, unlabeler )
35−import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) )
43+import Rel8.Schema.HTable.Identity ( HIdentity( HType ), HType )
36−import qualified Rel8.Schema.Kind as K
37import Rel8.Schema.Null ( Nullity( Null, NotNull ), Sql, nullable )
44import 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 ) )
38import Rel8.Table
48import Rel8.Table
39− ( Table, Columns, Context, fromColumns, toColumns
49+ ( Table, Columns, Context, fromColumns, toColumns, reify, unreify
40− , FromExprs, fromResult, toResult
41− , Transpose
42 )
50 )
51+import Rel8.Table.Recontextualize ( Recontextualize )
43import Rel8.Type ( DBType )
52import Rel8.Type ( DBType )
44import Rel8.Type.Monoid ( DBMonoid, memptyExpr )
53import Rel8.Type.Monoid ( DBMonoid, memptyExpr )
45import Rel8.Type.Num ( DBFloating, DBFractional, DBNum )
54import Rel8.Type.Num ( DBFloating, DBFractional, DBNum )
@@ -47,9 +56,13 @@ import Rel8.Type.Semigroup ( DBSemigroup, (<>.) )
47
56
48
57
49-- | Typed SQL expressions.
58-- | Typed SQL expressions.
50−type Expr :: K.Context
59+type role Expr representational
60+type Expr :: k -> Type
61+data Expr a where
51−newtype Expr a = Expr Opaleye.PrimExpr
62+ Expr :: k ~ Type => !Opaleye.PrimExpr -> Expr (a :: k)
63+
64+
52− deriving stock Show
65+deriving stock instance Show (Expr a)
53
66
54
67
55instance Sql DBSemigroup a => Semigroup (Expr a) where
68instance Sql DBSemigroup a => Semigroup (Expr a) where
@@ -113,13 +126,30 @@ instance Sql DBFloating a => Floating (Expr a) where
113 atanh = function "atanh"
126 atanh = function "atanh"
114
127
115
128
129+instance Interpretation Expr where
130+ data Col Expr _spec where
131+ E :: {unE :: !(Expr a)} -> Col Expr ('Spec labels a)
132+
133+
116instance Sql DBType a => Table Expr (Expr a) where
134instance Sql DBType a => Table Expr (Expr a) where
117− type Columns (Expr a) = HIdentity a
135+ type Columns (Expr a) = HType a
118 type Context (Expr a) = Expr
136 type Context (Expr a) = Expr
119− type FromExprs (Expr a) = a
120− type Transpose to (Expr a) = to a
121
137
122− toColumns a = HIdentity a
138+ toColumns a = HType (E a)
123− fromColumns (HIdentity a) = a
139+ fromColumns (HType (E a)) = a
124− toResult a = HIdentity (Identity a)
140+ reify = notReify
125− fromResult (HIdentity (Identity a)) = a
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 #-}
2{-# language StandaloneKindSignatures #-}
4{-# language StandaloneKindSignatures #-}
3
5
4module Rel8.Expr
6module Rel8.Expr
@@ -7,14 +9,14 @@ module Rel8.Expr
7where
9where
8
10
9-- base
11-- base
12+import Data.Kind ( Type )
10import Prelude ()
13import Prelude ()
11
14
12-- opaleye
15-- opaleye
13import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
16import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
14
17
15−-- rel8
16−import Rel8.Schema.Kind ( Context )
17
18
18−
19+type role Expr representational
19−type Expr :: Context
20+type Expr :: k -> Type
21+data Expr a where
20−newtype Expr a = Expr Opaleye.PrimExpr
22+ Expr :: k ~ Type => !Opaleye.PrimExpr -> Expr (a :: k)
src/Rel8/Expr/Aggregate.hs+64−25
@@ -10,7 +10,6 @@ module Rel8.Expr.Aggregate
10 , and, or
10 , and, or
11 , min, max
11 , min, max
12 , sum, sumWhere
12 , sum, sumWhere
13− , avg
14 , stringAgg
13 , stringAgg
15 , groupByExpr
14 , groupByExpr
16 , listAggExpr, nonEmptyAggExpr
15 , listAggExpr, nonEmptyAggExpr
@@ -24,12 +23,10 @@ import Data.List.NonEmpty ( NonEmpty )
24import Prelude hiding ( and, max, min, null, or, sum )
23import Prelude hiding ( and, max, min, null, or, sum )
25
24
26-- opaleye
25-- opaleye
27−import qualified Opaleye.Internal.Aggregate as Opaleye
26+import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
28−import Opaleye.Internal.Column ( Field_( Column ) )
29−import qualified Opaleye.Aggregate as Opaleye
30
27
31-- rel8
28-- rel8
32−import Rel8.Aggregate ( Aggregate, unsafeMakeAggregate )
29+import Rel8.Aggregate ( Aggregate, Aggregator(..), unsafeMakeAggregate )
33import Rel8.Expr ( Expr )
30import Rel8.Expr ( Expr )
34import Rel8.Expr.Bool ( caseExpr )
31import Rel8.Expr.Bool ( caseExpr )
35import Rel8.Expr.Opaleye
32import Rel8.Expr.Opaleye
@@ -53,13 +50,23 @@ import Rel8.Type.Sum ( DBSum )
53
50
54-- | Count the occurances of a single column. Corresponds to @COUNT(a)@
51-- | Count the occurances of a single column. Corresponds to @COUNT(a)@
55count :: Expr a -> Aggregate Int64
52count :: 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+
57
60
58-- | Count the number of distinct occurances of a single column. Corresponds to
61-- | Count the number of distinct occurances of a single column. Corresponds to
59-- @COUNT(DISTINCT a)@
62-- @COUNT(DISTINCT a)@
60countDistinct :: Sql DBEq a => Expr a -> Aggregate Int64
63countDistinct :: Sql DBEq a => Expr a -> Aggregate Int64
61countDistinct = unsafeMakeAggregate toPrimExpr fromPrimExpr $
64countDistinct = unsafeMakeAggregate toPrimExpr fromPrimExpr $
62− Opaleye.distinctAggregator Opaleye.count
65+ Just Aggregator
66+ { operation = Opaleye.AggrCount
67+ , ordering = []
68+ , distinction = Opaleye.AggrDistinct
69+ }
63
70
64
71
65-- | Corresponds to @COUNT(*)@.
72-- | Corresponds to @COUNT(*)@.
@@ -74,39 +81,56 @@ countWhere condition = count (caseExpr [(condition, litExpr (Just True))] null)
74
81
75-- | Corresponds to @bool_and@.
82-- | Corresponds to @bool_and@.
76and :: Expr Bool -> Aggregate Bool
83and :: 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+ }
78
90
79
91
80-- | Corresponds to @bool_or@.
92-- | Corresponds to @bool_or@.
81or :: Expr Bool -> Aggregate Bool
93or :: 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+ }
83
100
84
101
85-- | Produce an aggregation for @Expr a@ using the @max@ function.
102-- | Produce an aggregation for @Expr a@ using the @max@ function.
86max :: Sql DBMax a => Expr a -> Aggregate a
103max :: 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+ }
88
110
89
111
90-- | Produce an aggregation for @Expr a@ using the @max@ function.
112-- | Produce an aggregation for @Expr a@ using the @max@ function.
91min :: Sql DBMin a => Expr a -> Aggregate a
113min :: 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+ }
93
120
94-- | Corresponds to @sum@. Note that in SQL, @sum@ is type changing - for
121-- | Corresponds to @sum@. Note that in SQL, @sum@ is type changing - for
95-- example the @sum@ of @integer@ returns a @bigint@. Rel8 doesn't support
122-- 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
97-- lead to overflows, and if you anticipate very large sums, you should upcast
124-- lead to overflows, and if you anticipate very large sums, you should upcast
98-- your input.
125-- your input.
99sum :: Sql DBSum a => Expr a -> Aggregate a
126sum :: Sql DBSum a => Expr a -> Aggregate a
100−sum = unsafeMakeAggregate toPrimExpr (castExpr . fromPrimExpr) Opaleye.unsafeSum
127+sum = unsafeMakeAggregate toPrimExpr (castExpr . fromPrimExpr) $
101−
128+ Just Aggregator
129+ { operation = Opaleye.AggrSum
130+ , ordering = []
131+ , distinction = Opaleye.AggrAll
132+ }
102
133
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
110
134
111-- | Take the sum of all expressions that satisfy a predicate.
135-- | Take the sum of all expressions that satisfy a predicate.
112sumWhere :: (Sql DBNum a, Sql DBSum a)
136sumWhere :: (Sql DBNum a, Sql DBSum a)
@@ -118,12 +142,17 @@ sumWhere condition a = sum (caseExpr [(condition, a)] 0)
118stringAgg :: Sql DBString a
142stringAgg :: Sql DBString a
119 => Expr db -> Expr a -> Aggregate a
143 => Expr db -> Expr a -> Aggregate a
120stringAgg delimiter =
144stringAgg 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+ }
122
151
123
152
124-- | Aggregate a value by grouping by it.
153-- | Aggregate a value by grouping by it.
125groupByExpr :: Sql DBEq a => Expr a -> Aggregate a
154groupByExpr :: Sql DBEq a => Expr a -> Aggregate a
126−groupByExpr = unsafeMakeAggregate toPrimExpr fromPrimExpr Opaleye.groupBy
155+groupByExpr = unsafeMakeAggregate toPrimExpr fromPrimExpr Nothing
127
156
128
157
129-- | Collect expressions values as a list.
158-- | Collect expressions values as a list.
@@ -138,13 +167,23 @@ nonEmptyAggExpr = snonEmptyAggExpr typeInformation
138
167
139slistAggExpr :: ()
168slistAggExpr :: ()
140 => TypeInformation (Unnullify a) -> Expr a -> Aggregate [a]
169 => 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+ }
142 where
176 where
143 to = encodeArrayElement info . toPrimExpr
177 to = encodeArrayElement info . toPrimExpr
144
178
145
179
146snonEmptyAggExpr :: ()
180snonEmptyAggExpr :: ()
147 => TypeInformation (Unnullify a) -> Expr a -> Aggregate (NonEmpty a)
181 => 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+ }
149 where
188 where
150 to = encodeArrayElement info . toPrimExpr
189 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
22import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
22import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
23
23
24-- rel8
24-- rel8
25−import Rel8.Expr ( Expr )
25+import {-# SOURCE #-} Rel8.Expr ( Expr )
26import Rel8.Expr.Bool ( (&&.), (||.), false, or_, coalesce )
26import Rel8.Expr.Bool ( (&&.), (||.), false, or_, coalesce )
27import Rel8.Expr.Null ( isNull, unsafeLiftOpNull )
27import Rel8.Expr.Null ( isNull, unsafeLiftOpNull )
28import Rel8.Expr.Opaleye ( fromPrimExpr, toPrimExpr, zipPrimExprsWith )
28import Rel8.Expr.Opaleye ( fromPrimExpr, toPrimExpr, zipPrimExprsWith )
src/Rel8/Expr/Num.hs+6−39
@@ -4,24 +4,18 @@
4{-# options_ghc -fno-warn-redundant-constraints #-}
4{-# options_ghc -fno-warn-redundant-constraints #-}
5
5
6module Rel8.Expr.Num
6module 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
12 )
8 )
13where
9where
14
10
15-- base
11-- base
16−import Prelude ( (+), (-), fst, negate, signum, snd )
12+import Prelude ()
17
13
18-- rel
14-- rel
19import Rel8.Expr ( Expr( Expr ) )
15import Rel8.Expr ( Expr( Expr ) )
20−import Rel8.Expr.Eq ( (==.) )
21import Rel8.Expr.Function ( function )
16import Rel8.Expr.Function ( function )
22import Rel8.Expr.Opaleye ( castExpr )
17import Rel8.Expr.Opaleye ( castExpr )
23import Rel8.Schema.Null ( Homonullable, Sql )
18import Rel8.Schema.Null ( Homonullable, Sql )
24−import Rel8.Table.Bool ( bool )
25import Rel8.Type.Num ( DBFractional, DBIntegral, DBNum )
19import Rel8.Type.Num ( DBFractional, DBIntegral, DBNum )
26
20
27
21
@@ -48,41 +42,14 @@ ceiling :: (Sql DBFractional a, Sql DBIntegral b, Homonullable a b)
48ceiling = function "ceiling"
42ceiling = function "ceiling"
49
43
50
44
51−-- | Emulates the behaviour of the Haskell function 'Prelude.div' in
45+-- | Perform integral division. Corresponds to the @div()@ function.
52−-- PostgreSQL.
53div :: Sql DBIntegral a => Expr a -> Expr a -> Expr a
46div :: Sql DBIntegral a => Expr a -> Expr a -> Expr a
54−div n d = fst (divMod n d)
47+div = function "div"
55
48
56
49
57−-- | Emulates the behaviour of the Haskell function 'Prelude.mod' in
50+-- | Corresponds to the @mod()@ function.
58−-- PostgreSQL.
59mod :: Sql DBIntegral a => Expr a -> Expr a -> Expr a
51mod :: Sql DBIntegral a => Expr a -> Expr a -> Expr a
60−mod n d = snd (divMod n d)
52+mod = function "mod"
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)
86
53
87
54
88-- | Round a 'DFractional' to a 'DBIntegral' by rounding to the nearest smaller
55-- | 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
10 , scastExpr, sunsafeCastExpr
10 , scastExpr, sunsafeCastExpr
11 , unsafeLiteral
11 , unsafeLiteral
12 , fromPrimExpr, toPrimExpr, mapPrimExpr, zipPrimExprsWith, traversePrimExpr
12 , fromPrimExpr, toPrimExpr, mapPrimExpr, zipPrimExprsWith, traversePrimExpr
13− , toColumn, fromColumn, traverseFieldP
13+ , toColumn, fromColumn
14 )
14 )
15where
15where
16
16
@@ -27,9 +27,6 @@ import Rel8.Schema.Null ( Unnullify, Sql )
27import Rel8.Type ( DBType, typeInformation )
27import Rel8.Type ( DBType, typeInformation )
28import Rel8.Type.Information ( TypeInformation(..) )
28import Rel8.Type.Information ( TypeInformation(..) )
29
29
30−-- profunctors
31−import Data.Profunctor ( Profunctor, dimap )
32−
33
30
34castExpr :: Sql DBType a => Expr a -> Expr a
31castExpr :: Sql DBType a => Expr a -> Expr a
35castExpr = scastExpr typeInformation
32castExpr = scastExpr typeInformation
@@ -83,15 +80,9 @@ traversePrimExpr :: Functor f
83traversePrimExpr f = fmap fromPrimExpr . f . toPrimExpr
80traversePrimExpr f = fmap fromPrimExpr . f . toPrimExpr
84
81
85
82
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
93toColumn = Opaleye.Column
84toColumn = Opaleye.Column
94
85
95
86
96−fromColumn :: Opaleye.Field_ n b -> Opaleye.PrimExpr
87+fromColumn :: Opaleye.Column b -> Opaleye.PrimExpr
97fromColumn (Opaleye.Column a) = a
88fromColumn (Opaleye.Column a) = a
src/Rel8/Expr/Sequence.hs+1−2
@@ -11,7 +11,6 @@ import Prelude
11import Rel8.Expr ( Expr )
11import Rel8.Expr ( Expr )
12import Rel8.Expr.Function ( function )
12import Rel8.Expr.Function ( function )
13import Rel8.Expr.Serialize ( litExpr )
13import Rel8.Expr.Serialize ( litExpr )
14−import Rel8.Expr.Text ( quoteIdent )
15
14
16-- text
15-- text
17import Data.Text ( pack )
16import Data.Text ( pack )
@@ -19,4 +18,4 @@ import Data.Text ( pack )
19
18
20-- | See https://www.postgresql.org/docs/current/functions-sequence.html
19-- | See https://www.postgresql.org/docs/current/functions-sequence.html
21nextval :: String -> Expr Int64
20nextval :: 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
17 , pgClientEncoding, quoteIdent, quoteLiteral, quoteNullable, regexpReplace
17 , pgClientEncoding, quoteIdent, quoteLiteral, quoteNullable, regexpReplace
18 , regexpSplitToArray, repeat, replace, reverse, right, rpad, rtrim
18 , regexpSplitToArray, repeat, replace, reverse, right, rpad, rtrim
19 , splitPart, strpos, substr, translate
19 , splitPart, strpos, substr, translate
20−
21− -- * @LIKE@ and @ILIKE@
22− , like, ilike
23 )
20 )
24where
21where
25
22
@@ -27,7 +24,7 @@ where
27import Data.Bool ( Bool )
24import Data.Bool ( Bool )
28import Data.Int ( Int32 )
25import Data.Int ( Int32 )
29import Data.Maybe ( Maybe( Nothing, Just ) )
26import Data.Maybe ( Maybe( Nothing, Just ) )
30−import Prelude ( flip )
27+import Prelude ()
31
28
32-- bytestring
29-- bytestring
33import Data.ByteString ( ByteString )
30import Data.ByteString ( ByteString )
@@ -52,7 +49,7 @@ infixr 6 ++.
52
49
53
50
54-- | Matches regular expression, case sensitive
51-- | Matches regular expression, case sensitive
55−--
52+--
56-- Corresponds to the @~.@ operator.
53-- Corresponds to the @~.@ operator.
57(~.) :: Expr Text -> Expr Text -> Expr Bool
54(~.) :: Expr Text -> Expr Text -> Expr Bool
58(~.) = binaryOperator "~."
55(~.) = binaryOperator "~."
@@ -276,21 +273,3 @@ substr a b Nothing = function "substr" a b
276-- | Corresponds to the @translate@ function.
273-- | Corresponds to the @translate@ function.
277translate :: Expr Text -> Expr Text -> Expr Text -> Expr Text
274translate :: Expr Text -> Expr Text -> Expr Text -> Expr Text
278translate = function "translate"
275translate = 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 @@
5
5
6module Rel8.FCF
6module Rel8.FCF
7 ( Exp, Eval
7 ( Exp, Eval
8− , Id
8+ , Compose
9 )
9 )
10where
10where
11
11
@@ -22,5 +22,5 @@ type Eval :: Exp e -> e
22type family Eval a
22type family Eval a
23
23
24
24
25−data Id :: a -> Exp a
25+data Compose :: (Type -> Type) -> (Type -> Exp Type) -> Type -> Exp Type
26−type instance Eval (Id a) = a
26+type instance Eval (Compose f g a) = f (Eval (g a))
src/Rel8/Generic/Construction.hs+165−146
@@ -2,6 +2,9 @@
2{-# language ConstraintKinds #-}
2{-# language ConstraintKinds #-}
3{-# language DataKinds #-}
3{-# language DataKinds #-}
4{-# language FlexibleContexts #-}
4{-# language FlexibleContexts #-}
5+{-# language FlexibleInstances #-}
6+{-# language LambdaCase #-}
7+{-# language MultiParamTypeClasses #-}
5{-# language NamedFieldPuns #-}
8{-# language NamedFieldPuns #-}
6{-# language ScopedTypeVariables #-}
9{-# language ScopedTypeVariables #-}
7{-# language StandaloneKindSignatures #-}
10{-# language StandaloneKindSignatures #-}
@@ -25,17 +28,18 @@ where
25import Data.Bifunctor ( first )
28import Data.Bifunctor ( first )
26import Data.Kind ( Constraint, Type )
29import Data.Kind ( Constraint, Type )
27import Data.List.NonEmpty ( NonEmpty( (:|) ) )
30import Data.List.NonEmpty ( NonEmpty( (:|) ) )
31+import Data.Type.Equality ( (:~:)( Refl ) )
28import GHC.TypeLits ( Symbol )
32import GHC.TypeLits ( Symbol )
29import Prelude
33import Prelude
30
34
31-- rel8
35-- rel8
32−import Rel8.Aggregate ( Aggregate( Aggregate ) )
36+import Rel8.Aggregate ( Col( A ), Aggregate( Aggregate ) )
33−import Rel8.Expr ( Expr )
37+import Rel8.Expr ( Col( E ), Expr )
34import Rel8.Expr.Aggregate ( groupByExpr )
38import Rel8.Expr.Aggregate ( groupByExpr )
35import Rel8.Expr.Eq ( (==.) )
39import Rel8.Expr.Eq ( (==.) )
36import Rel8.Expr.Null ( nullify, snull, unsafeUnnullify )
40import Rel8.Expr.Null ( nullify, snull, unsafeUnnullify )
37import Rel8.Expr.Serialize ( litExpr )
41import Rel8.Expr.Serialize ( litExpr )
38−import Rel8.FCF ( Eval, Exp, Id )
42+import Rel8.FCF ( Eval, Exp )
39import Rel8.Generic.Construction.ADT
43import Rel8.Generic.Construction.ADT
40 ( GConstructorADT, GMakeableADT, gmakeADT
44 ( GConstructorADT, GMakeableADT, gmakeADT
41 , GConstructableADT
45 , GConstructableADT
@@ -55,16 +59,18 @@ import Rel8.Kind.Algebra
55 , KnownAlgebra, algebraSing
59 , KnownAlgebra, algebraSing
56 )
60 )
57import qualified Rel8.Kind.Algebra as K
61import qualified Rel8.Kind.Algebra as K
58−import Rel8.Schema.Context.Nullify ( sguard, snullify )
62+import Rel8.Schema.Context.Nullify ( runTag )
59import Rel8.Schema.HTable ( HTable )
63import Rel8.Schema.HTable ( HTable )
60−import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) )
64+import Rel8.Schema.HTable.Identity ( HIdentity( HType ) )
61import qualified Rel8.Schema.Kind as K
65import qualified Rel8.Schema.Kind as K
62−import Rel8.Schema.Name ( Name( Name ) )
66+import Rel8.Schema.Name ( Col( N ), Name( Name ) )
63import Rel8.Schema.Null ( Nullity( Null, NotNull ) )
67import 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 )
65import Rel8.Table
71import Rel8.Table
66− ( TTable, TColumns
72+ ( TTable, TColumns, TUnreify
67− , Table, fromColumns, toColumns
73+ , Table, fromColumns, toColumns, reify, unreify
68 )
74 )
69import Rel8.Table.Bool ( case_ )
75import Rel8.Table.Bool ( case_ )
70import Rel8.Type.Tag ( Tag )
76import Rel8.Type.Tag ( Tag )
@@ -73,10 +79,10 @@ import Rel8.Type.Tag ( Tag )
73type GGBuildable :: K.Algebra -> Symbol -> (K.Context -> Exp (Type -> Type)) -> Constraint
79type GGBuildable :: K.Algebra -> Symbol -> (K.Context -> Exp (Type -> Type)) -> Constraint
74type GGBuildable algebra name rep =
80type GGBuildable algebra name rep =
75 ( KnownAlgebra algebra
81 ( KnownAlgebra algebra
76− , Eval (GGColumns algebra TColumns (Eval (rep Aggregate))) ~ Eval (GGColumns algebra TColumns (Eval (rep Expr)))
82+ , Eval (GGColumns algebra TColumns (Eval (rep (Reify Aggregate)))) ~ Eval (GGColumns algebra TColumns (Eval (rep (Reify Result))))
77− , Eval (GGColumns algebra TColumns (Eval (rep Expr))) ~ Eval (GGColumns algebra TColumns (Eval (rep Expr)))
83+ , Eval (GGColumns algebra TColumns (Eval (rep (Reify Expr)))) ~ Eval (GGColumns algebra TColumns (Eval (rep (Reify Result))))
78− , Eval (GGColumns algebra TColumns (Eval (rep Name))) ~ Eval (GGColumns algebra TColumns (Eval (rep Expr)))
84+ , Eval (GGColumns algebra TColumns (Eval (rep (Reify Name)))) ~ Eval (GGColumns algebra TColumns (Eval (rep (Reify Result))))
79− , HTable (Eval (GGColumns algebra TColumns (Eval (rep Expr))))
85+ , HTable (Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))))
80 , GGBuildable' algebra name rep
86 , GGBuildable' algebra name rep
81 )
87 )
82
88
@@ -84,63 +90,65 @@ type GGBuildable algebra name rep =
84type GGBuildable' :: K.Algebra -> Symbol -> (K.Context -> Exp (Type -> Type)) -> Constraint
90type GGBuildable' :: K.Algebra -> Symbol -> (K.Context -> Exp (Type -> Type)) -> Constraint
85type family GGBuildable' algebra name rep where
91type family GGBuildable' algebra name rep where
86 GGBuildable' 'K.Product name rep =
92 GGBuildable' 'K.Product name rep =
87− ( name ~ GConstructor (Eval (rep Expr))
93+ ( name ~ GConstructor (Eval (rep (Reify Expr)))
88− , Representable Id (Eval (rep Expr))
94+ , Representable TUnreify (Eval (rep (Reify Expr)))
89− , GConstructable (TTable Expr) TColumns Id Expr (Eval (rep Expr))
95+ , GConstructable (TTable (Reify Expr)) TColumns TUnreify (Col (Reify Expr)) (Eval (rep (Reify Expr)))
90 )
96 )
91 GGBuildable' 'K.Sum name rep =
97 GGBuildable' 'K.Sum name rep =
92− ( Representable Id (GConstructorADT name (Eval (rep Expr)))
98+ ( Representable TUnreify (GConstructorADT name (Eval (rep (Reify Expr))))
93− , GMakeableADT (TTable Expr) TColumns Id Expr name (Eval (rep Expr))
99+ , GMakeableADT (TTable (Reify Expr)) TColumns TUnreify (Col (Reify Expr)) name (Eval (rep (Reify Expr)))
94 )
100 )
95
101
96
102
97type GGBuild :: K.Algebra -> Symbol -> (K.Context -> Exp (Type -> Type)) -> Type -> Type
103type GGBuild :: K.Algebra -> Symbol -> (K.Context -> Exp (Type -> Type)) -> Type -> Type
98type family GGBuild algebra name rep r where
104type family GGBuild algebra name rep r where
99 GGBuild 'K.Product _name rep r =
105 GGBuild 'K.Product _name rep r =
100− GConstruct Id (Eval (rep Expr)) r
106+ GConstruct TUnreify (Eval (rep (Reify Expr))) r
101 GGBuild 'K.Sum name rep r =
107 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
103
109
104
110
105ggbuild :: forall algebra name rep a. GGBuildable algebra name rep
111ggbuild :: 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)
107 -> GGBuild algebra name rep a
113 -> GGBuild algebra name rep a
108ggbuild gfromColumns = case algebraSing @algebra of
114ggbuild gfromColumns = case algebraSing @algebra of
109 SProduct ->
115 SProduct ->
110− gtabulate @Id @(Eval (rep Expr)) @a $
116+ gtabulate @TUnreify @(Eval (rep (Reify Expr))) @a $
111 gfromColumns .
117 gfromColumns .
118+ hunreify .
112 gconstruct
119 gconstruct
113− @(TTable Expr)
120+ @(TTable (Reify Expr))
114 @TColumns
121 @TColumns
115− @Id
122+ @TUnreify
116− @Expr
123+ @(Col (Reify Expr))
117− @(Eval (rep Expr))
124+ @(Eval (rep (Reify Expr)))
118− (const toColumns)
125+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
119 SSum ->
126 SSum ->
120− gtabulate @Id @(GConstructorADT name (Eval (rep Expr))) @a $
127+ gtabulate @TUnreify @(GConstructorADT name (Eval (rep (Reify Expr)))) @a $
121 gfromColumns .
128 gfromColumns .
129+ hunreify .
122 gmakeADT
130 gmakeADT
123− @(TTable Expr)
131+ @(TTable (Reify Expr))
124 @TColumns
132 @TColumns
125− @Id
133+ @TUnreify
126− @Expr
134+ @(Col (Reify Expr))
127 @name
135 @name
128− @(Eval (rep Expr))
136+ @(Eval (rep (Reify Expr)))
129− (const toColumns)
137+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
130− (\Spec {info} -> snull info)
138+ (\SSpec {info} -> Reify (E (snull info)))
131− (\Spec {nullity} -> case nullity of
139+ (\SSpec {nullity} -> case nullity of
132 Null -> id
140 Null -> id
133− NotNull -> nullify)
141+ NotNull -> \(Reify (E a)) -> Reify (E (nullify a)))
134− (HIdentity . litExpr)
142+ (HType . Reify . E . litExpr)
135
143
136
144
137type GGConstructable :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Constraint
145type GGConstructable :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Constraint
138type GGConstructable algebra rep =
146type GGConstructable algebra rep =
139 ( KnownAlgebra algebra
147 ( KnownAlgebra algebra
140− , Eval (GGColumns algebra TColumns (Eval (rep Aggregate))) ~ Eval (GGColumns algebra TColumns (Eval (rep Expr)))
148+ , Eval (GGColumns algebra TColumns (Eval (rep (Reify Aggregate)))) ~ Eval (GGColumns algebra TColumns (Eval (rep (Reify Result))))
141− , Eval (GGColumns algebra TColumns (Eval (rep Expr))) ~ Eval (GGColumns algebra TColumns (Eval (rep Expr)))
149+ , Eval (GGColumns algebra TColumns (Eval (rep (Reify Expr)))) ~ Eval (GGColumns algebra TColumns (Eval (rep (Reify Result))))
142− , Eval (GGColumns algebra TColumns (Eval (rep Name))) ~ Eval (GGColumns algebra TColumns (Eval (rep Expr)))
150+ , Eval (GGColumns algebra TColumns (Eval (rep (Reify Name)))) ~ Eval (GGColumns algebra TColumns (Eval (rep (Reify Result))))
143− , HTable (Eval (GGColumns algebra TColumns (Eval (rep Expr))))
151+ , HTable (Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))))
144 , GGConstructable' algebra rep
152 , GGConstructable' algebra rep
145 )
153 )
146
154
@@ -148,100 +156,105 @@ type GGConstructable algebra rep =
148type GGConstructable' :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Constraint
156type GGConstructable' :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Constraint
149type family GGConstructable' algebra rep where
157type family GGConstructable' algebra rep where
150 GGConstructable' 'K.Product rep =
158 GGConstructable' 'K.Product rep =
151− ( Representable Id (Eval (rep Aggregate))
159+ ( Representable TUnreify (Eval (rep (Reify Aggregate)))
152− , Representable Id (Eval (rep Expr))
160+ , Representable TUnreify (Eval (rep (Reify Expr)))
153− , Representable Id (Eval (rep Name))
161+ , Representable TUnreify (Eval (rep (Reify Name)))
154− , GConstructable (TTable Aggregate) TColumns Id Aggregate (Eval (rep Aggregate))
162+ , GConstructable (TTable (Reify Aggregate)) TColumns TUnreify (Col (Reify Aggregate)) (Eval (rep (Reify Aggregate)))
155− , GConstructable (TTable Expr) TColumns Id Expr (Eval (rep Expr))
163+ , GConstructable (TTable (Reify Expr)) TColumns TUnreify (Col (Reify Expr)) (Eval (rep (Reify Expr)))
156− , GConstructable (TTable Name) TColumns Id Name (Eval (rep Name))
164+ , GConstructable (TTable (Reify Name)) TColumns TUnreify (Col (Reify Name)) (Eval (rep (Reify Name)))
157 )
165 )
158 GGConstructable' 'K.Sum rep =
166 GGConstructable' 'K.Sum rep =
159− ( RepresentableConstructors Id (Eval (rep Expr))
167+ ( RepresentableConstructors TUnreify (Eval (rep (Reify Expr)))
160− , RepresentableFields Id (Eval (rep Aggregate))
168+ , RepresentableFields TUnreify (Eval (rep (Reify Aggregate)))
161− , RepresentableFields Id (Eval (rep Expr))
169+ , RepresentableFields TUnreify (Eval (rep (Reify Expr)))
162− , RepresentableFields Id (Eval (rep Name))
170+ , RepresentableFields TUnreify (Eval (rep (Reify Name)))
163− , Functor (GConstructors Id (Eval (rep Expr)))
171+ , Functor (GConstructors TUnreify (Eval (rep (Reify Expr))))
164− , GConstructableADT (TTable Aggregate) TColumns Id Aggregate (Eval (rep Aggregate))
172+ , GConstructableADT (TTable (Reify Aggregate)) TColumns TUnreify (Col (Reify Aggregate)) (Eval (rep (Reify Aggregate)))
165− , GConstructableADT (TTable Expr) TColumns Id Expr (Eval (rep Expr))
173+ , GConstructableADT (TTable (Reify Expr)) TColumns TUnreify (Col (Reify Expr)) (Eval (rep (Reify Expr)))
166− , GConstructableADT (TTable Name) TColumns Id Name (Eval (rep Name))
174+ , GConstructableADT (TTable (Reify Name)) TColumns TUnreify (Col (Reify Name)) (Eval (rep (Reify Name)))
167 )
175 )
168
176
169
177
170type GGConstruct :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Type -> Type
178type GGConstruct :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Type -> Type
171type family GGConstruct algebra rep r where
179type family GGConstruct algebra rep r where
172− GGConstruct 'K.Product rep r = GConstruct 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 =
173− GGConstruct 'K.Sum rep r = GConstructADT Id (Eval (rep Expr)) r r
183+ GConstructADT TUnreify (Eval (rep (Reify Expr))) r r
174
184
175
185
176ggconstruct :: forall algebra rep a. GGConstructable algebra rep
186ggconstruct :: 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)
178 -> GGConstruct algebra rep a -> a
188 -> GGConstruct algebra rep a -> a
179ggconstruct gfromColumns f = case algebraSing @algebra of
189ggconstruct gfromColumns f = case algebraSing @algebra of
180 SProduct ->
190 SProduct ->
181 f $
191 f $
182− gtabulate @Id @(Eval (rep Expr)) @a $
192+ gtabulate @TUnreify @(Eval (rep (Reify Expr))) @a $
183 gfromColumns .
193 gfromColumns .
194+ hunreify .
184 gconstruct
195 gconstruct
185− @(TTable Expr)
196+ @(TTable (Reify Expr))
186 @TColumns
197 @TColumns
187− @Id
198+ @TUnreify
188− @Expr
199+ @(Col (Reify Expr))
189− @(Eval (rep Expr))
200+ @(Eval (rep (Reify Expr)))
190− (const toColumns)
201+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
191 SSum ->
202 SSum ->
192− gcindex @Id @(Eval (rep Expr)) @a f $
203+ gcindex @TUnreify @(Eval (rep (Reify Expr))) @a f $
193− fmap gfromColumns $
204+ fmap (gfromColumns . hunreify) $
194 gconstructADT
205 gconstructADT
195− @(TTable Expr)
206+ @(TTable (Reify Expr))
196 @TColumns
207 @TColumns
197− @Id
208+ @TUnreify
198− @Expr
209+ @(Col (Reify Expr))
199− @(Eval (rep Expr))
210+ @(Eval (rep (Reify Expr)))
200− (const toColumns)
211+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
201− (\Spec {info} -> snull info)
212+ (\SSpec {info} -> Reify (E (snull info)))
202− (\Spec {nullity} -> case nullity of
213+ (\SSpec {nullity} -> case nullity of
203 Null -> id
214 Null -> id
204− NotNull -> nullify)
215+ NotNull -> \(Reify (E a)) -> Reify (E (nullify a)))
205− (HIdentity . litExpr)
216+ (HType . Reify . E . litExpr)
206
217
207
218
208type GGDeconstruct :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Type -> Type -> Type
219type GGDeconstruct :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Type -> Type -> Type
209type family GGDeconstruct algebra rep a r where
220type family GGDeconstruct algebra rep a r where
210 GGDeconstruct 'K.Product rep a r =
221 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
212 GGDeconstruct 'K.Sum rep a r =
223 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)
214
225
215
226
216ggdeconstruct :: forall algebra rep a r. (GGConstructable algebra rep, Table Expr r)
227ggdeconstruct :: 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))
218 -> GGDeconstruct algebra rep a r
229 -> GGDeconstruct algebra rep a r
219ggdeconstruct gtoColumns = case algebraSing @algebra of
230ggdeconstruct gtoColumns = case algebraSing @algebra of
220 SProduct -> \build ->
231 SProduct -> \build ->
221− gindex @Id @(Eval (rep Expr)) @r build .
232+ gindex @TUnreify @(Eval (rep (Reify Expr))) @r build .
222 gdeconstruct
233 gdeconstruct
223− @(TTable Expr)
234+ @(TTable (Reify Expr))
224 @TColumns
235 @TColumns
225− @Id
236+ @TUnreify
226− @Expr
237+ @(Col (Reify Expr))
227− @(Eval (rep Expr))
238+ @(Eval (rep (Reify Expr)))
228− (const fromColumns) .
239+ (\(_ :: proxy x) -> unreify @_ @x Refl . fromColumns) .
240+ hreify .
229 gtoColumns
241 gtoColumns
230 SSum ->
242 SSum ->
231− gctabulate @Id @(Eval (rep Expr)) @r @(a -> r) $ \constructors as ->
243+ gctabulate @TUnreify @(Eval (rep (Reify Expr))) @r @(a -> r) $ \constructors as ->
232 let
244 let
233− (HIdentity tag, cases) =
245+ (HType (Reify (E tag)), cases) =
234 gdeconstructADT
246 gdeconstructADT
235− @(TTable Expr)
247+ @(TTable (Reify Expr))
236 @TColumns
248 @TColumns
237− @Id
249+ @TUnreify
238− @Expr
250+ @(Col (Reify Expr))
239− @(Eval (rep Expr))
251+ @(Eval (rep (Reify Expr)))
240− (const fromColumns)
252+ (\(_ :: proxy x) -> unreify @_ @x Refl . fromColumns)
241− (\Spec {nullity} -> case nullity of
253+ (\SSpec {nullity} -> case nullity of
242 Null -> id
254 Null -> id
243− NotNull -> unsafeUnnullify)
255+ NotNull -> \(Reify (E a)) -> Reify (E (unsafeUnnullify a)))
244 constructors $
256 constructors $
257+ hreify $
245 gtoColumns as
258 gtoColumns as
246 in
259 in
247 case cases of
260 case cases of
@@ -251,102 +264,108 @@ ggdeconstruct gtoColumns = case algebraSing @algebra of
251
264
252type GGName :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Type -> Type
265type GGName :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Type -> Type
253type family GGName algebra rep a where
266type family GGName algebra rep a where
254− GGName 'K.Product rep a = GConstruct Id (Eval (rep Name)) a
267+ GGName 'K.Product rep a = GConstruct TUnreify (Eval (rep (Reify Name))) a
255− GGName 'K.Sum rep a = Name Tag -> GBuildADT Id (Eval (rep Name)) a
268+ GGName 'K.Sum rep a = Name Tag -> GBuildADT TUnreify (Eval (rep (Reify Name))) a
256
269
257
270
258ggname :: forall algebra rep a. GGConstructable algebra rep
271ggname :: 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)
260 -> GGName algebra rep a
273 -> GGName algebra rep a
261ggname gfromColumns = case algebraSing @algebra of
274ggname gfromColumns = case algebraSing @algebra of
262 SProduct ->
275 SProduct ->
263− gtabulate @Id @(Eval (rep Name)) @a $
276+ gtabulate @TUnreify @(Eval (rep (Reify Name))) @a $
264 gfromColumns .
277 gfromColumns .
278+ hunreify .
265 gconstruct
279 gconstruct
266− @(TTable Name)
280+ @(TTable (Reify Name))
267 @TColumns
281 @TColumns
268− @Id
282+ @TUnreify
269− @Name
283+ @(Col (Reify Name))
270− @(Eval (rep Name))
284+ @(Eval (rep (Reify Name)))
271− (const toColumns)
285+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
272 SSum -> \tag ->
286 SSum -> \tag ->
273− gftabulate @Id @(Eval (rep Name)) @a $
287+ gftabulate @TUnreify @(Eval (rep (Reify Name))) @a $
274 gfromColumns .
288 gfromColumns .
289+ hunreify .
275 gbuildADT
290 gbuildADT
276− @(TTable Name)
291+ @(TTable (Reify Name))
277 @TColumns
292 @TColumns
278− @Id
293+ @TUnreify
279− @Name
294+ @(Col (Reify Name))
280− @(Eval (rep Name))
295+ @(Eval (rep (Reify Name)))
281− (const toColumns)
296+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
282− (\_ _ (Name a) -> Name a)
297+ (\_ _ (Reify (N (Name a))) -> Reify (N (Name a)))
283− (HIdentity tag)
298+ (HType (Reify (N tag)))
284
299
285
300
286type GGAggregate :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Type -> Type
301type GGAggregate :: K.Algebra -> (K.Context -> Exp (Type -> Type)) -> Type -> Type
287type family GGAggregate algebra rep r where
302type family GGAggregate algebra rep r where
288 GGAggregate 'K.Product rep r =
303 GGAggregate 'K.Product rep r =
289− GConstruct Id (Eval (rep Aggregate)) r ->
304+ GConstruct TUnreify (Eval (rep (Reify Aggregate))) r ->
290− GConstruct Id (Eval (rep Expr)) r
305+ GConstruct TUnreify (Eval (rep (Reify Expr))) r
291 GGAggregate 'K.Sum rep r =
306 GGAggregate 'K.Sum rep r =
292− GBuildADT Id (Eval (rep Aggregate)) r ->
307+ GBuildADT TUnreify (Eval (rep (Reify Aggregate))) r ->
293− GBuildADT Id (Eval (rep Expr)) r
308+ GBuildADT TUnreify (Eval (rep (Reify Expr))) r
294
309
295
310
296ggaggregate :: forall algebra rep exprs agg. GGConstructable algebra rep
311ggaggregate :: forall algebra rep exprs agg. GGConstructable algebra rep
297− => (Eval (GGColumns algebra TColumns (Eval (rep Expr))) Aggregate -> agg)
312+ => (Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))) (Col Aggregate) -> agg)
298− -> (exprs -> Eval (GGColumns algebra TColumns (Eval (rep Expr))) Expr)
313+ -> (exprs -> Eval (GGColumns algebra TColumns (Eval (rep (Reify Result)))) (Col Expr))
299 -> GGAggregate algebra rep agg -> exprs -> agg
314 -> GGAggregate algebra rep agg -> exprs -> agg
300ggaggregate gfromColumns gtoColumns agg es = case algebraSing @algebra of
315ggaggregate gfromColumns gtoColumns agg es = case algebraSing @algebra of
301 SProduct -> flip f exprs $
316 SProduct -> flip f exprs $
302 gfromColumns .
317 gfromColumns .
318+ hunreify .
303 gconstruct
319 gconstruct
304− @(TTable Aggregate)
320+ @(TTable (Reify Aggregate))
305 @TColumns
321 @TColumns
306− @Id
322+ @TUnreify
307− @Aggregate
323+ @(Col (Reify Aggregate))
308− @(Eval (rep Aggregate))
324+ @(Eval (rep (Reify Aggregate)))
309− (const toColumns)
325+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
310 where
326 where
311 f =
327 f =
312− gindex @Id @(Eval (rep Expr)) @agg .
328+ gindex @TUnreify @(Eval (rep (Reify Expr))) @agg .
313 agg .
329 agg .
314− gtabulate @Id @(Eval (rep Aggregate)) @agg
330+ gtabulate @TUnreify @(Eval (rep (Reify Aggregate))) @agg
315 exprs =
331 exprs =
316 gdeconstruct
332 gdeconstruct
317− @(TTable Expr)
333+ @(TTable (Reify Expr))
318 @TColumns
334 @TColumns
319− @Id
335+ @TUnreify
320− @Expr
336+ @(Col (Reify Expr))
321− @(Eval (rep Expr))
337+ @(Eval (rep (Reify Expr)))
322− (const fromColumns) $
338+ (\(_ :: proxy x) -> unreify @_ @x Refl . fromColumns) $
339+ hreify $
323 gtoColumns es
340 gtoColumns es
324 SSum -> flip f exprs $
341 SSum -> flip f exprs $
325 gfromColumns .
342 gfromColumns .
343+ hunreify .
326 gbuildADT
344 gbuildADT
327− @(TTable Aggregate)
345+ @(TTable (Reify Aggregate))
328 @TColumns
346 @TColumns
329− @Id
347+ @TUnreify
330− @Aggregate
348+ @(Col (Reify Aggregate))
331− @(Eval (rep Aggregate))
349+ @(Eval (rep (Reify Aggregate)))
332− (const toColumns)
350+ (\(_ :: proxy x) -> toColumns . reify @_ @x Refl)
333− (\tag' Spec {nullity} (Aggregate a) ->
351+ (\tag' SSpec {nullity} (Reify (A (Aggregate a))) ->
334− Aggregate $ sguard (tag ==. litExpr tag') . snullify nullity <$> a)
352+ Reify $ A $ Aggregate $ runTag nullity (tag ==. litExpr tag') <$> a)
335− (HIdentity (groupByExpr tag))
353+ (HType (Reify (A (groupByExpr tag))))
336 where
354 where
337 f =
355 f =
338− gfindex @Id @(Eval (rep Expr)) @agg .
356+ gfindex @TUnreify @(Eval (rep (Reify Expr))) @agg .
339 agg .
357 agg .
340− gftabulate @Id @(Eval (rep Aggregate)) @agg
358+ gftabulate @TUnreify @(Eval (rep (Reify Aggregate))) @agg
341− (HIdentity tag, exprs) =
359+ (HType (Reify (E tag)), exprs) =
342 gunbuildADT
360 gunbuildADT
343− @(TTable Expr)
361+ @(TTable (Reify Expr))
344 @TColumns
362 @TColumns
345− @Id
363+ @TUnreify
346− @Expr
364+ @(Col (Reify Expr))
347− @(Eval (rep Expr))
365+ @(Eval (rep (Reify Expr)))
348− (const fromColumns)
366+ (\(_ :: proxy x) -> unreify @_ @x Refl . fromColumns)
349− (\Spec {nullity} -> case nullity of
367+ (\SSpec {nullity} -> case nullity of
350 Null -> id
368 Null -> id
351− NotNull -> unsafeUnnullify) $
369+ NotNull -> \(Reify (E a)) -> Reify (E (unsafeUnnullify a))) $
370+ hreify $
352 gtoColumns es
371 gtoColumns es
src/Rel8/Generic/Construction/ADT.hs+47−32
@@ -1,7 +1,6 @@
1{-# language AllowAmbiguousTypes #-}
1{-# language AllowAmbiguousTypes #-}
2{-# language BlockArguments #-}
2{-# language BlockArguments #-}
3{-# language DataKinds #-}
3{-# language DataKinds #-}
4−{-# language FlexibleContexts #-}
5{-# language FlexibleInstances #-}
4{-# language FlexibleInstances #-}
6{-# language MultiParamTypeClasses #-}
5{-# language MultiParamTypeClasses #-}
7{-# language RankNTypes #-}
6{-# language RankNTypes #-}
@@ -49,13 +48,14 @@ import Rel8.Generic.Construction.Record
49 )
48 )
50import Rel8.Generic.Table.ADT ( GColumnsADT, GColumnsADT' )
49import Rel8.Generic.Table.ADT ( GColumnsADT, GColumnsADT' )
51import Rel8.Generic.Table.Record ( GColumns )
50import Rel8.Generic.Table.Record ( GColumns )
51+import Rel8.Schema.Context.Label ( HLabelable, hlabeler, hunlabeler )
52import Rel8.Schema.HTable ( HTable )
52import Rel8.Schema.HTable ( HTable )
53−import Rel8.Schema.HTable.Identity ( HIdentity )
53+import Rel8.Schema.HTable.Identity ( HType )
54import Rel8.Schema.HTable.Label ( HLabel, hlabel, hunlabel )
54import Rel8.Schema.HTable.Label ( HLabel, hlabel, hunlabel )
55import Rel8.Schema.HTable.Nullify ( HNullify, hnulls, hnullify, hunnullify )
55import Rel8.Schema.HTable.Nullify ( HNullify, hnulls, hnullify, hunnullify )
56import Rel8.Schema.HTable.Product ( HProduct( HProduct ) )
56import Rel8.Schema.HTable.Product ( HProduct( HProduct ) )
57import Rel8.Schema.Null ( Nullify )
57import Rel8.Schema.Null ( Nullify )
58−import Rel8.Schema.Spec ( Spec )
58+import Rel8.Schema.Spec ( Spec( Spec ), SSpec )
59import qualified Rel8.Schema.Kind as K
59import qualified Rel8.Schema.Kind as K
60import Rel8.Type.Tag ( Tag( Tag ) )
60import Rel8.Type.Tag ( Tag( Tag ) )
61
61
@@ -63,16 +63,24 @@ import Rel8.Type.Tag ( Tag( Tag ) )
63import Data.Text ( pack )
63import Data.Text ( pack )
64
64
65
65
66−type Null :: K.Context -> Type
66+type Null :: K.HContext -> Type
67−type Null context = forall a. Spec a -> context (Nullify a)
67+type Null context = forall labels a. ()
68+ => SSpec ('Spec labels a)
69+ -> context ('Spec labels (Nullify a))
68
70
69
71
70−type Nullifier :: K.Context -> Type
72+type Nullifier :: K.HContext -> Type
71−type Nullifier context = forall a. Spec a -> context a -> context (Nullify a)
73+type Nullifier context = forall labels a. ()
74+ => SSpec ('Spec labels a)
75+ -> context ('Spec labels a)
76+ -> context ('Spec labels (Nullify a))
72
77
73
78
74−type Unnullifier :: K.Context -> Type
79+type Unnullifier :: K.HContext -> Type
75−type Unnullifier context = forall a. Spec a -> context (Nullify a) -> context a
80+type Unnullifier context = forall labels a. ()
81+ => SSpec ('Spec labels a)
82+ -> context ('Spec labels (Nullify a))
83+ -> context ('Spec labels a)
76
84
77
85
78type NoConstructor :: Symbol -> Symbol -> ErrorMessage
86type NoConstructor :: Symbol -> Symbol -> ErrorMessage
@@ -181,12 +189,12 @@ type GConstructableADT
181 :: (Type -> Exp Constraint)
189 :: (Type -> Exp Constraint)
182 -> (Type -> Exp K.HTable)
190 -> (Type -> Exp K.HTable)
183 -> (Type -> Exp Type)
191 -> (Type -> Exp Type)
184− -> K.Context -> (Type -> Type) -> Constraint
192+ -> K.HContext -> (Type -> Type) -> Constraint
185class GConstructableADT _Table _Columns f context rep where
193class GConstructableADT _Table _Columns f context rep where
186 gbuildADT :: ()
194 gbuildADT :: ()
187 => ToColumns _Table _Columns f context
195 => ToColumns _Table _Columns f context
188 -> (Tag -> Nullifier context)
196 -> (Tag -> Nullifier context)
189− -> HIdentity Tag context
197+ -> HType Tag context
190 -> GFieldsADT f rep
198 -> GFieldsADT f rep
191 -> GColumnsADT _Columns rep context
199 -> GColumnsADT _Columns rep context
192
200
@@ -194,13 +202,13 @@ class GConstructableADT _Table _Columns f context rep where
194 => FromColumns _Table _Columns f context
202 => FromColumns _Table _Columns f context
195 -> Unnullifier context
203 -> Unnullifier context
196 -> GColumnsADT _Columns rep context
204 -> GColumnsADT _Columns rep context
197− -> (HIdentity Tag context, GFieldsADT f rep)
205+ -> (HType Tag context, GFieldsADT f rep)
198
206
199 gconstructADT :: ()
207 gconstructADT :: ()
200 => ToColumns _Table _Columns f context
208 => ToColumns _Table _Columns f context
201 -> Null context
209 -> Null context
202 -> Nullifier context
210 -> Nullifier context
203− -> (Tag -> HIdentity Tag context)
211+ -> (Tag -> HType Tag context)
204 -> GConstructors f rep (GColumnsADT _Columns rep context)
212 -> GConstructors f rep (GColumnsADT _Columns rep context)
205
213
206 gdeconstructADT :: ()
214 gdeconstructADT :: ()
@@ -208,29 +216,30 @@ class GConstructableADT _Table _Columns f context rep where
208 -> Unnullifier context
216 -> Unnullifier context
209 -> GConstructors f rep r
217 -> GConstructors f rep r
210 -> GColumnsADT _Columns rep context
218 -> GColumnsADT _Columns rep context
211− -> (HIdentity Tag context, NonEmpty (Tag, r))
219+ -> (HType Tag context, NonEmpty (Tag, r))
212
220
213
221
214instance
222instance
215− ( htable ~ HLabel "tag" (HIdentity Tag)
223+ ( htable ~ HLabel "tag" (HType Tag)
216 , GConstructableADT' _Table _Columns f context htable rep
224 , GConstructableADT' _Table _Columns f context htable rep
225+ , HLabelable context
217 )
226 )
218 => GConstructableADT _Table _Columns f context (M1 D meta rep)
227 => GConstructableADT _Table _Columns f context (M1 D meta rep)
219 where
228 where
220 gbuildADT toColumns nullifier =
229 gbuildADT toColumns nullifier =
221 gbuildADT' @_Table @_Columns @f @context @htable @rep toColumns nullifier .
230 gbuildADT' @_Table @_Columns @f @context @htable @rep toColumns nullifier .
222− hlabel
231+ hlabel hlabeler
223
232
224 gunbuildADT fromColumns unnullifier =
233 gunbuildADT fromColumns unnullifier =
225− first hunlabel .
234+ first (hunlabel hunlabeler) .
226 gunbuildADT' @_Table @_Columns @f @context @htable @rep fromColumns unnullifier
235 gunbuildADT' @_Table @_Columns @f @context @htable @rep fromColumns unnullifier
227
236
228 gconstructADT toColumns null nullifier mk =
237 gconstructADT toColumns null nullifier mk =
229 gconstructADT' @_Table @_Columns @f @context @htable @rep toColumns null nullifier
238 gconstructADT' @_Table @_Columns @f @context @htable @rep toColumns null nullifier
230− (hlabel . mk)
239+ (hlabel hlabeler . mk)
231
240
232 gdeconstructADT fromColumns unnullifier cases =
241 gdeconstructADT fromColumns unnullifier cases =
233− first hunlabel .
242+ first (hunlabel hunlabeler) .
234 gdeconstructADT' @_Table @_Columns @f @context @htable @rep fromColumns unnullifier cases
243 gdeconstructADT' @_Table @_Columns @f @context @htable @rep fromColumns unnullifier cases
235
244
236
245
@@ -238,7 +247,7 @@ type GConstructableADT'
238 :: (Type -> Exp Constraint)
247 :: (Type -> Exp Constraint)
239 -> (Type -> Exp K.HTable)
248 -> (Type -> Exp K.HTable)
240 -> (Type -> Exp Type)
249 -> (Type -> Exp Type)
241− -> K.Context -> K.HTable -> (Type -> Type) -> Constraint
250+ -> K.HContext -> K.HTable -> (Type -> Type) -> Constraint
242class GConstructableADT' _Table _Columns f context htable rep where
251class GConstructableADT' _Table _Columns f context htable rep where
243 gbuildADT' :: ()
252 gbuildADT' :: ()
244 => ToColumns _Table _Columns f context
253 => ToColumns _Table _Columns f context
@@ -325,6 +334,7 @@ instance {-# OVERLAPPABLE #-}
325 ( HTable (GColumns _Columns rep)
334 ( HTable (GColumns _Columns rep)
326 , KnownSymbol label
335 , KnownSymbol label
327 , meta ~ 'MetaCons label _fixity _isRecord
336 , meta ~ 'MetaCons label _fixity _isRecord
337+ , HLabelable context
328 , GConstructable _Table _Columns f context rep
338 , GConstructable _Table _Columns f context rep
329 , GColumnsADT' _Columns htable (M1 C meta rep) ~
339 , GColumnsADT' _Columns htable (M1 C meta rep) ~
330 HProduct htable (HLabel label (HNullify (GColumns _Columns rep)))
340 HProduct htable (HLabel label (HNullify (GColumns _Columns rep)))
@@ -333,7 +343,7 @@ instance {-# OVERLAPPABLE #-}
333 where
343 where
334 gbuildADT' toColumns nullifier htable =
344 gbuildADT' toColumns nullifier htable =
335 HProduct htable .
345 HProduct htable .
336− hlabel .
346+ hlabel hlabeler .
337 hnullify (nullifier tag) .
347 hnullify (nullifier tag) .
338 gconstruct @_Table @_Columns @f @context @rep toColumns
348 gconstruct @_Table @_Columns @f @context @rep toColumns
339 where
349 where
@@ -344,13 +354,13 @@ instance {-# OVERLAPPABLE #-}
344 , gdeconstruct @_Table @_Columns @f @context @rep fromColumns $
354 , gdeconstruct @_Table @_Columns @f @context @rep fromColumns $
345 runIdentity $
355 runIdentity $
346 hunnullify (\spec -> pure . unnullifier spec) $
356 hunnullify (\spec -> pure . unnullifier spec) $
347− hunlabel
357+ hunlabel hunlabeler
348 a
358 a
349 )
359 )
350
360
351 gconstructADT' toColumns _ nullifier mk =
361 gconstructADT' toColumns _ nullifier mk =
352 HProduct htable .
362 HProduct htable .
353− hlabel .
363+ hlabel hlabeler .
354 hnullify nullifier .
364 hnullify nullifier .
355 gconstruct @_Table @_Columns @f @context @rep toColumns
365 gconstruct @_Table @_Columns @f @context @rep toColumns
356 where
366 where
@@ -365,34 +375,35 @@ instance {-# OVERLAPPABLE #-}
365 a = gdeconstruct @_Table @_Columns @f @context @rep fromColumns $
375 a = gdeconstruct @_Table @_Columns @f @context @rep fromColumns $
366 runIdentity $
376 runIdentity $
367 hunnullify (\spec -> pure . unnullifier spec) $
377 hunnullify (\spec -> pure . unnullifier spec) $
368− hunlabel
378+ hunlabel hunlabeler
369 columns
379 columns
370 tag = Tag $ pack $ symbolVal (Proxy @label)
380 tag = Tag $ pack $ symbolVal (Proxy @label)
371
381
372− gfill null htable = HProduct htable (hlabel (hnulls null))
382+ gfill null htable = HProduct htable (hlabel hlabeler (hnulls null))
373
383
374
384
375type GMakeableADT
385type GMakeableADT
376 :: (Type -> Exp Constraint)
386 :: (Type -> Exp Constraint)
377 -> (Type -> Exp K.HTable)
387 -> (Type -> Exp K.HTable)
378 -> (Type -> Exp Type)
388 -> (Type -> Exp Type)
379− -> K.Context -> Symbol -> (Type -> Type) -> Constraint
389+ -> K.HContext -> Symbol -> (Type -> Type) -> Constraint
380class GMakeableADT _Table _Columns f context name rep where
390class GMakeableADT _Table _Columns f context name rep where
381 gmakeADT :: ()
391 gmakeADT :: ()
382 => ToColumns _Table _Columns f context
392 => ToColumns _Table _Columns f context
383 -> Null context
393 -> Null context
384 -> Nullifier context
394 -> Nullifier context
385− -> (Tag -> HIdentity Tag context)
395+ -> (Tag -> HType Tag context)
386 -> GFields f (GConstructorADT name rep)
396 -> GFields f (GConstructorADT name rep)
387 -> GColumnsADT _Columns rep context
397 -> GColumnsADT _Columns rep context
388
398
389
399
390instance
400instance
391− ( htable ~ HLabel "tag" (HIdentity Tag)
401+ ( htable ~ HLabel "tag" (HType Tag)
392 , meta ~ 'MetaData datatype _module _package _newtype
402 , meta ~ 'MetaData datatype _module _package _newtype
393 , fallback ~ TypeError (NoConstructor datatype name)
403 , fallback ~ TypeError (NoConstructor datatype name)
394 , fields ~ GFields f (GConstructorADT' name rep fallback)
404 , fields ~ GFields f (GConstructorADT' name rep fallback)
395 , GMakeableADT' _Table _Columns f context htable name rep fields
405 , GMakeableADT' _Table _Columns f context htable name rep fields
406+ , HLabelable context
396 , KnownSymbol name
407 , KnownSymbol name
397 )
408 )
398 => GMakeableADT _Table _Columns f context name (M1 D meta rep)
409 => GMakeableADT _Table _Columns f context name (M1 D meta rep)
@@ -403,14 +414,14 @@ instance
403 toColumns null nullifier htable
414 toColumns null nullifier htable
404 where
415 where
405 tag = Tag $ pack $ symbolVal (Proxy @name)
416 tag = Tag $ pack $ symbolVal (Proxy @name)
406− htable = hlabel (wrap tag)
417+ htable = hlabel hlabeler (wrap tag)
407
418
408
419
409type GMakeableADT'
420type GMakeableADT'
410 :: (Type -> Exp Constraint)
421 :: (Type -> Exp Constraint)
411 -> (Type -> Exp K.HTable)
422 -> (Type -> Exp K.HTable)
412 -> (Type -> Exp Type)
423 -> (Type -> Exp Type)
413− -> K.Context -> K.HTable -> Symbol -> (Type -> Type) -> Type -> Constraint
424+ -> K.HContext -> K.HTable -> Symbol -> (Type -> Type) -> Type -> Constraint
414class GMakeableADT' _Table _Columns f context htable name rep fields where
425class GMakeableADT' _Table _Columns f context htable name rep fields where
415 gmakeADT' :: ()
426 gmakeADT' :: ()
416 => ToColumns _Table _Columns f context
427 => ToColumns _Table _Columns f context
@@ -451,6 +462,8 @@ instance {-# OVERLAPS #-}
451
462
452instance {-# OVERLAPS #-}
463instance {-# OVERLAPS #-}
453 ( HTable (GColumns _Columns rep)
464 ( HTable (GColumns _Columns rep)
465+ , KnownSymbol name
466+ , HLabelable context
454 , GConstructable _Table _Columns f context rep
467 , GConstructable _Table _Columns f context rep
455 , fields ~ GFields f rep
468 , fields ~ GFields f rep
456 , GColumnsADT' _Columns htable (M1 C ('MetaCons name _fixity _isRecord) rep) ~
469 , GColumnsADT' _Columns htable (M1 C ('MetaCons name _fixity _isRecord) rep) ~
@@ -460,13 +473,15 @@ instance {-# OVERLAPS #-}
460 where
473 where
461 gmakeADT' toColumns _ nullifier htable =
474 gmakeADT' toColumns _ nullifier htable =
462 HProduct htable .
475 HProduct htable .
463− hlabel .
476+ hlabel hlabeler .
464 hnullify nullifier .
477 hnullify nullifier .
465 gconstruct @_Table @_Columns @f @context @rep toColumns
478 gconstruct @_Table @_Columns @f @context @rep toColumns
466
479
467
480
468instance {-# OVERLAPPABLE #-}
481instance {-# OVERLAPPABLE #-}
469 ( HTable (GColumns _Columns rep)
482 ( HTable (GColumns _Columns rep)
483+ , KnownSymbol label
484+ , HLabelable context
470 , GColumnsADT' _Columns htable (M1 C ('MetaCons label _fixity _isRecord) rep) ~
485 , GColumnsADT' _Columns htable (M1 C ('MetaCons label _fixity _isRecord) rep) ~
471 HProduct htable (HLabel label (HNullify (GColumns _Columns rep)))
486 HProduct htable (HLabel label (HNullify (GColumns _Columns rep)))
472 )
487 )
@@ -474,5 +489,5 @@ instance {-# OVERLAPPABLE #-}
474 where
489 where
475 gmakeADT' _ null _ htable _ =
490 gmakeADT' _ null _ htable _ =
476 HProduct htable $
491 HProduct htable $
477− hlabel $
492+ hlabel hlabeler $
478 hnulls null
493 hnulls null
src/Rel8/Generic/Construction/Record.hs+11−6
@@ -27,13 +27,15 @@ import GHC.Generics
27 )
27 )
28import GHC.TypeLits
28import GHC.TypeLits
29 ( ErrorMessage( (:<>:), Text ), TypeError
29 ( ErrorMessage( (:<>:), Text ), TypeError
30− , Symbol
30+ , Symbol, KnownSymbol
31 )
31 )
32import Prelude
32import Prelude
33
33
34-- rel8
34-- rel8
35import Rel8.FCF ( Eval, Exp )
35import Rel8.FCF ( Eval, Exp )
36import Rel8.Generic.Table.Record ( GColumns )
36import Rel8.Generic.Table.Record ( GColumns )
37+import Rel8.Schema.Context.Label ( HLabelable, hlabeler, hunlabeler )
38+import Rel8.Schema.HTable ( HTable )
37import Rel8.Schema.HTable.Label ( hlabel, hunlabel )
39import Rel8.Schema.HTable.Label ( hlabel, hunlabel )
38import Rel8.Schema.HTable.Product ( HProduct( HProduct ) )
40import Rel8.Schema.HTable.Product ( HProduct( HProduct ) )
39import qualified Rel8.Schema.Kind as K
41import qualified Rel8.Schema.Kind as K
@@ -43,7 +45,7 @@ type FromColumns
43 :: (Type -> Exp Constraint)
45 :: (Type -> Exp Constraint)
44 -> (Type -> Exp K.HTable)
46 -> (Type -> Exp K.HTable)
45 -> (Type -> Exp Type)
47 -> (Type -> Exp Type)
46− -> K.Context
48+ -> K.HContext
47 -> Type
49 -> Type
48type FromColumns _Table _Columns f context = forall proxy x.
50type FromColumns _Table _Columns f context = forall proxy x.
49 Eval (_Table x) => proxy x -> Eval (_Columns x) context -> Eval (f x)
51 Eval (_Table x) => proxy x -> Eval (_Columns x) context -> Eval (f x)
@@ -53,7 +55,7 @@ type ToColumns
53 :: (Type -> Exp Constraint)
55 :: (Type -> Exp Constraint)
54 -> (Type -> Exp K.HTable)
56 -> (Type -> Exp K.HTable)
55 -> (Type -> Exp Type)
57 -> (Type -> Exp Type)
56− -> K.Context
58+ -> K.HContext
57 -> Type
59 -> Type
58type ToColumns _Table _Columns f context = forall proxy x.
60type ToColumns _Table _Columns f context = forall proxy x.
59 Eval (_Table x) => proxy x -> Eval (f x) -> Eval (_Columns x) context
61 Eval (_Table x) => proxy x -> Eval (f x) -> Eval (_Columns x) context
@@ -117,7 +119,7 @@ type GConstructable
117 :: (Type -> Exp Constraint)
119 :: (Type -> Exp Constraint)
118 -> (Type -> Exp K.HTable)
120 -> (Type -> Exp K.HTable)
119 -> (Type -> Exp Type)
121 -> (Type -> Exp Type)
120− -> K.Context -> (Type -> Type) -> Constraint
122+ -> K.HContext -> (Type -> Type) -> Constraint
121class GConstructable _Table _Columns f context rep where
123class GConstructable _Table _Columns f context rep where
122 gconstruct :: ()
124 gconstruct :: ()
123 => ToColumns _Table _Columns f context
125 => ToColumns _Table _Columns f context
@@ -160,9 +162,12 @@ instance
160
162
161instance
163instance
162 ( Eval (_Table a)
164 ( Eval (_Table a)
165+ , HTable (Eval (_Columns a))
166+ , HLabelable context
167+ , KnownSymbol label
163 , meta ~ 'MetaSel ('Just label) _su _ss _ds
168 , meta ~ 'MetaSel ('Just label) _su _ss _ds
164 )
169 )
165 => GConstructable _Table _Columns f context (M1 S meta (K1 i a))
170 => GConstructable _Table _Columns f context (M1 S meta (K1 i a))
166 where
171 where
167− gconstruct toColumns = hlabel . toColumns (Proxy @a)
172+ gconstruct toColumns = hlabel hlabeler . toColumns (Proxy @a)
168− gdeconstruct fromColumns = fromColumns (Proxy @a) . hunlabel
173+ gdeconstruct fromColumns = fromColumns (Proxy @a) . hunlabel hunlabeler
src/Rel8/Generic/Map.hs+64−20
@@ -1,19 +1,26 @@
1+{-# language AllowAmbiguousTypes #-}
1{-# language DataKinds #-}
2{-# language DataKinds #-}
3+{-# language EmptyCase #-}
4+{-# language FlexibleInstances #-}
5+{-# language LambdaCase #-}
6+{-# language MultiParamTypeClasses #-}
7+{-# language RankNTypes #-}
8+{-# language ScopedTypeVariables #-}
2{-# language StandaloneKindSignatures #-}
9{-# language StandaloneKindSignatures #-}
10+{-# language TypeApplications #-}
3{-# language TypeFamilies #-}
11{-# language TypeFamilies #-}
4{-# language TypeOperators #-}
12{-# language TypeOperators #-}
5{-# language UndecidableInstances #-}
13{-# language UndecidableInstances #-}
6
14
7module Rel8.Generic.Map
15module Rel8.Generic.Map
8− ( GMap
16+ ( GMap, GMappable (gmap, gunmap)
9− , Map
10 )
17 )
11where
18where
12
19
13-- base
20-- base
14−import Data.Kind ( Type )
21+import Data.Kind ( Constraint, Type )
15import GHC.Generics
22import GHC.Generics
16− ( (:+:), (:*:), K1, M1, U1, V1
23+ ( (:+:)( L1, R1 ), (:*:)( (:*:) ), K1( K1 ), M1( M1 ), U1( U1 ), V1
17 )
24 )
18import Prelude ()
25import Prelude ()
19
26
@@ -31,19 +38,56 @@ type family GMap f rep where
31 GMap f (K1 i a) = K1 i (Eval (f a))
38 GMap f (K1 i a) = K1 i (Eval (f a))
32
39
33
40
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
41+type GMappable :: (Type -> Exp Constraint) -> (Type -> Type) -> Constraint
37−type family Map f a where
42+class GMappable constraint rep where
38− Map p (t a b c d e f g) =
43+ gmap :: ()
39− t (Eval (p a)) (Eval (p b)) (Eval (p c)) (Eval (p d)) (Eval (p e))
44+ => proxy f
40− (Eval (p f)) (Eval (p g))
45+ -> (forall a. Eval (constraint a) => a -> Eval (f a))
41− Map p (t a b c d e f) =
46+ -> rep x
42− t (Eval (p a)) (Eval (p b)) (Eval (p c)) (Eval (p d)) (Eval (p e))
47+ -> GMap f rep x
43− (Eval (p f))
48+
44− Map p (t a b c d e) =
49+ gunmap :: ()
45− t (Eval (p a)) (Eval (p b)) (Eval (p c)) (Eval (p d)) (Eval (p e))
50+ => proxy f
46− Map p (t a b c d) = t (Eval (p a)) (Eval (p b)) (Eval (p c)) (Eval (p d))
51+ -> (forall a. Eval (constraint a) => Eval (f a) -> a)
47− Map p (t a b c) = t (Eval (p a)) (Eval (p b)) (Eval (p c))
52+ -> GMap f rep x
48− Map p (t a b) = t (Eval (p a)) (Eval (p b))
53+ -> rep x
49− Map p (t a) = t (Eval (p a))
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 @@
1{-# language AllowAmbiguousTypes #-}
1{-# language AllowAmbiguousTypes #-}
2{-# language DataKinds #-}
2{-# language DataKinds #-}
3−{-# language FlexibleContexts #-}
4{-# language FlexibleInstances #-}
3{-# language FlexibleInstances #-}
5{-# language MultiParamTypeClasses #-}
4{-# language MultiParamTypeClasses #-}
6{-# language PolyKinds #-}
5{-# language PolyKinds #-}
@@ -142,7 +141,6 @@ instance {-# OVERLAPPABLE #-} Snd (Count n rep) ~ rep => Countable n rep where
142 uncount = id
141 uncount = id
143
142
144
143
145−type Record :: Type -> Type
146newtype Record a = Record
144newtype Record a = Record
147 { unrecord :: a
145 { unrecord :: a
148 }
146 }
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 @@
1{-# language AllowAmbiguousTypes #-}
1{-# language AllowAmbiguousTypes #-}
2−{-# language ConstraintKinds #-}
3{-# language DataKinds #-}
2{-# language DataKinds #-}
4{-# language DefaultSignatures #-}
3{-# language DefaultSignatures #-}
5{-# language FlexibleContexts #-}
4{-# language FlexibleContexts #-}
6{-# language FlexibleInstances #-}
5{-# language FlexibleInstances #-}
7−{-# language LambdaCase #-}
8{-# language MultiParamTypeClasses #-}
6{-# language MultiParamTypeClasses #-}
9−{-# language PolyKinds #-}
10−{-# language QuantifiedConstraints #-}
11{-# language ScopedTypeVariables #-}
7{-# language ScopedTypeVariables #-}
12{-# language StandaloneKindSignatures #-}
8{-# language StandaloneKindSignatures #-}
13{-# language TypeApplications #-}
9{-# language TypeApplications #-}
14−{-# language TypeFamilyDependencies #-}
10+{-# language TypeFamilies #-}
15−{-# language TypeOperators #-}
16{-# language UndecidableInstances #-}
11{-# language UndecidableInstances #-}
17
12
18module Rel8.Generic.Rel8able
13module Rel8.Generic.Rel8able
@@ -20,39 +15,36 @@ module Rel8.Generic.Rel8able
20 , Algebra
15 , Algebra
21 , GRep
16 , GRep
22 , GColumns, gfromColumns, gtoColumns
17 , GColumns, gfromColumns, gtoColumns
23− , GFromExprs, gfromResult, gtoResult
18+ , greify, gunreify
24− , TSerialize, serialize, deserialize
19+ , TUnreifyContext
25 )
20 )
26where
21where
27
22
28-- base
23-- base
29−import Data.Functor.Identity ( Identity )
30import Data.Kind ( Constraint, Type )
24import Data.Kind ( Constraint, Type )
31−import Data.Type.Bool ( type (&&) )
25+import Data.Proxy ( Proxy( Proxy ) )
26+import Data.Type.Equality ( (:~:)( Refl ) )
32import GHC.Generics ( Generic, Rep, from, to )
27import GHC.Generics ( Generic, Rep, from, to )
33import Prelude
28import Prelude
34
29
35-- rel8
30-- rel8
36−import Rel8.Aggregate ( Aggregate )
31+import Rel8.Kind.Context ( Reifiable )
37−import Rel8.Expr ( Expr )
38−import Rel8.FCF ( Exp, Eval )
32+import Rel8.FCF ( Eval, Exp )
33+import Rel8.Generic.Map ( GMap, GMappable, gmap, gunmap )
39import Rel8.Generic.Record ( Record(..) )
34import Rel8.Generic.Record ( Record(..) )
40import Rel8.Generic.Table ( GAlgebra )
35import Rel8.Generic.Table ( GAlgebra )
41import qualified Rel8.Generic.Table.Record as G
36import qualified Rel8.Generic.Table.Record as G
42import qualified Rel8.Kind.Algebra as K ( Algebra(..) )
37import qualified Rel8.Kind.Algebra as K ( Algebra(..) )
43−import Rel8.Kind.Context ( SContext(..) )
38+import Rel8.Schema.Context ( Col )
44−import Rel8.Schema.Field ( Field )
39+import Rel8.Schema.Context.Label ( Labelable )
45import Rel8.Schema.HTable ( HTable )
40import Rel8.Schema.HTable ( HTable )
46import qualified Rel8.Schema.Kind as K
41import qualified Rel8.Schema.Kind as K
47−import Rel8.Schema.Name ( Name )
42+import Rel8.Schema.Reify ( Reify, UnwrapReify )
48import Rel8.Schema.Result ( Result )
43import Rel8.Schema.Result ( Result )
49import Rel8.Table
44import Rel8.Table
50− ( Table, Columns, Context, fromColumns, toColumns
45+ ( fromColumns, toColumns, reify, unreify
51− , FromExprs, fromResult, toResult
52− , Transpose
53− , TTable, TColumns
46+ , TTable, TColumns, TContext, TUnreify
54 )
47 )
55−import Rel8.Table.Transpose ( Transposes )
56
48
57
49
58-- | The kind of 'Rel8able' types
50-- | The kind of 'Rel8able' types
@@ -60,59 +52,6 @@ type KRel8able :: Type
60type KRel8able = K.Rel8able
52type KRel8able = K.Rel8able
61
53
62
54
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−
116-- | This type class allows you to define custom 'Table's using higher-kinded
55-- | This type class allows you to define custom 'Table's using higher-kinded
117-- data types. Higher-kinded data types are data types of the pattern:
56-- data types. Higher-kinded data types are data types of the pattern:
118--
57--
@@ -146,7 +85,7 @@ type instance Eval (TSerialize expr a) =
146-- [Supplying @Rel8able@ instances]
85-- [Supplying @Rel8able@ instances]
147--
86--
148-- This type class should be derived generically for all table types in your
87-- 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
150-- extensions:
89-- extensions:
151--
90--
152-- @
91-- @
@@ -158,124 +97,79 @@ type instance Eval (TSerialize expr a) =
158type Rel8able :: K.Rel8able -> Constraint
97type Rel8able :: K.Rel8able -> Constraint
159class HTable (GColumns t) => Rel8able t where
98class HTable (GColumns t) => Rel8able t where
160 type GColumns t :: K.HTable
99 type GColumns t :: K.HTable
161− type GFromExprs t :: Type
162
100
101+ gfromColumns :: (Labelable context, Reifiable context)
163− gfromColumns :: SContext context -> GColumns t context -> t context
102+ => GColumns t (Col (Reify context)) -> t (Reify context)
103+
104+ gtoColumns :: (Labelable context, Reifiable context)
164− gtoColumns :: SContext context -> t context -> GColumns t context
105+ => t (Reify context) -> GColumns t (Col (Reify context))
165
106
166− gfromResult :: GColumns t Result -> GFromExprs t
107+ greify :: (Labelable context, Reifiable context)
167− gtoResult :: GFromExprs t -> GColumns t Result
108+ => t context -> t (Reify context)
168
109
110+ gunreify :: (Labelable context, Reifiable context)
111+ => t (Reify context) -> t context
112+
169− type GColumns t = G.GColumns TColumns (GRep t Expr)
113+ type GColumns t = G.GColumns TColumns (GRep t (Reify Result))
170− type GFromExprs t = t Result
171
114
172 default gfromColumns :: forall context.
115 default gfromColumns :: forall context.
173− ( SRel8able t Aggregate
116+ ( Generic (Record (t (Reify context)))
174− , SRel8able t Expr
117+ , G.GTable (TTable (Reify context)) TColumns (Col (Reify context)) (GRep t (Reify context))
175− , forall table. SRel8able t (Field table)
118+ , G.GColumns TColumns (GRep t (Reify context)) ~ GColumns t
176− , SRel8able t Name
177− , SSerialize t
178 )
119 )
179− => SContext context -> GColumns t context -> t context
120+ => GColumns t (Col (Reify context)) -> t (Reify context)
180− gfromColumns = \case
121+ gfromColumns =
181− SAggregate -> sfromColumns
122+ unrecord .
182− SExpr -> sfromColumns
123+ to .
183− SField -> sfromColumns
124+ G.gfromColumns @(TTable (Reify context)) @TColumns fromColumns
184− SName -> sfromColumns
185− SResult -> sfromResult
186
125
187 default gtoColumns :: forall context.
126 default gtoColumns :: forall context.
188− ( SRel8able t Aggregate
127+ ( Generic (Record (t (Reify context)))
189− , SRel8able t Expr
128+ , G.GTable (TTable (Reify context)) TColumns (Col (Reify context)) (GRep t (Reify context))
190− , forall table. SRel8able t (Field table)
129+ , G.GColumns TColumns (GRep t (Reify context)) ~ GColumns t
191− , SRel8able t Name
192− , SSerialize t
193 )
130 )
194− => SContext context -> t context -> GColumns t context
131+ => t (Reify context) -> GColumns t (Col (Reify context))
195− gtoColumns = \case
132+ gtoColumns =
196− SAggregate -> stoColumns
133+ G.gtoColumns @(TTable (Reify context)) @TColumns toColumns .
197− SExpr -> stoColumns
134+ from .
198− SField -> stoColumns
135+ Record
199− SName -> stoColumns
136+
200− SResult -> stoResult
137+ default greify :: forall context.
201−
138+ ( Generic (Record (t context))
202− default gfromResult :: (SSerialize t, GFromExprs t ~ t Result)
139+ , Generic (Record (t (Reify context)))
203− => GColumns t Result -> GFromExprs t
140+ , GMappable (TTable (Reify context)) (GRep t (Reify context))
204− gfromResult = sfromResult
141+ , GRep t context ~ GMap TUnreify (GRep t (Reify context))
205−
142+ )
206− default gtoResult :: (SSerialize t, GFromExprs t ~ t Result)
143+ => t context -> t (Reify context)
207− => GFromExprs t -> GColumns t Result
144+ greify =
208− gtoResult = stoResult
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
209
164
210
165
211type Algebra :: K.Rel8able -> K.Algebra
166type Algebra :: K.Rel8able -> K.Algebra
212−type Algebra t = GAlgebra (GRep t Expr)
167+type Algebra t = GAlgebra (GRep t (Reify Result))
213
168
214
169
215type GRep :: K.Rel8able -> K.Context -> Type -> Type
170type GRep :: K.Rel8able -> K.Context -> Type -> Type
216type GRep t context = Rep (Record (t context))
171type GRep t context = Rep (Record (t context))
217
172
218
173
219−type SRel8able :: K.Rel8able -> K.Context -> Constraint
174+data TUnreifyContext :: Type -> Exp K.Context
220−class
175+type instance Eval (TUnreifyContext a) = UnwrapReify (Eval (TContext a))
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
tests/Rel8/Generic/Rel8able/Test.hs → src/Rel8/Generic/Rel8able/Test.hsrenamed+16−47
@@ -1,11 +1,11 @@
1{-# language DataKinds #-}
1{-# language DataKinds #-}
2{-# language DeriveAnyClass #-}
2{-# language DeriveAnyClass #-}
3{-# language DeriveGeneric #-}
3{-# language DeriveGeneric #-}
4−{-# language DerivingStrategies #-}
4+{-# language DerivingVia #-}
5{-# language DuplicateRecordFields #-}
5{-# language DuplicateRecordFields #-}
6{-# language FlexibleInstances #-}
6{-# language FlexibleInstances #-}
7{-# language MultiParamTypeClasses #-}
7{-# language MultiParamTypeClasses #-}
8−{-# language StandaloneKindSignatures #-}
8+{-# language StandaloneDeriving #-}
9{-# language TypeFamilies #-}
9{-# language TypeFamilies #-}
10{-# language UndecidableInstances #-}
10{-# language UndecidableInstances #-}
11
11
@@ -98,20 +98,30 @@ data S3Object = S3Object
98 deriving stock Generic
98 deriving stock Generic
99
99
100
100
101+deriving via HKDT S3Object
102+ instance Table Result S3Object
103+
104+
105+deriving via HKDT S3Object
101−instance x ~ HKD S3Object Expr => ToExprs x S3Object
106+ instance x ~ HKD S3Object Expr => ToExprs x S3Object
102
107
103
108
104data HKDSum = HKDSumA Text | HKDSumB Bool Char | HKDSumC
109data HKDSum = HKDSumA Text | HKDSumB Bool Char | HKDSumC
105 deriving stock Generic
110 deriving stock Generic
106
111
107
112
113+deriving via HKDT HKDSum
114+ instance Table Result HKDSum
115+
116+
117+deriving via HKDT HKDSum
108−instance x ~ HKD HKDSum Expr => ToExprs x HKDSum
118+ instance x ~ HKD HKDSum Expr => ToExprs x HKDSum
109
119
110
120
111data HKDTest f = HKDTest
121data HKDTest f = HKDTest
112 { s3Object :: Lift f S3Object
122 { s3Object :: Lift f S3Object
113 , hkdSum :: Lift f HKDSum
123 , hkdSum :: Lift f HKDSum
114− }
124+ }
115 deriving stock Generic
125 deriving stock Generic
116 deriving anyclass Rel8able
126 deriving anyclass Rel8able
117
127
@@ -138,51 +148,10 @@ data TableSum f
138 deriving stock Generic
148 deriving stock Generic
139
149
140
150
141−data BarbieSum f
142− = BarbieSumA (f Bool) (f Text)
143− | BarbieSumB
144− | BarbieSumC (f Text)
145− deriving stock Generic
146−
147−
148data TableProduct f = TableProduct
151data TableProduct f = TableProduct
149− { sum :: HADT f BarbieSum
152+ { sum :: HADT f TableSum
150 , list :: TableList f
153 , list :: TableList f
151 , foos :: HList f (HADT f TableSum, Lift f HKDSum, HKDTest f)
154 , foos :: HList f (HADT f TableSum, Lift f HKDSum, HKDTest f)
152 }
155 }
153 deriving stock Generic
156 deriving stock Generic
154 deriving anyclass Rel8able
157 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 @@
12{-# language UndecidableInstances #-}
12{-# language UndecidableInstances #-}
13
13
14module Rel8.Generic.Table
14module Rel8.Generic.Table
15+ ( GGTable, GGColumns, GGContext, ggfromColumns, ggtoColumns, ggtable
15− ( GGSerialize, GGColumns, ggfromResult, ggtoResult
16+ , GGToExprs, ggfromResult, ggtoResult
16 , GAlgebra
17 , GAlgebra
17 )
18 )
18where
19where
@@ -25,19 +26,43 @@ import Prelude ()
25-- rel8
26-- rel8
26import Rel8.FCF ( Eval, Exp )
27import Rel8.FCF ( Eval, Exp )
27import Rel8.Generic.Table.ADT
28import Rel8.Generic.Table.ADT
29+ ( GTableADT, GColumnsADT, gfromColumnsADT, gtoColumnsADT, gtableADT
28− ( GSerializeADT, GColumnsADT, gtoResultADT, gfromResultADT
30+ , GToExprsADT, gtoResultADT, gfromResultADT
31+ )
32+import Rel8.Generic.Table.Record
33+ ( GTable, GColumns, GContext, gfromColumns, gtoColumns, gtable
34+ , GToExprs, gtoResult, gfromResult
29 )
35 )
30−import Rel8.Generic.Table.Record ( GSerialize, GColumns, gtoResult, gfromResult )
31import Rel8.Kind.Algebra
36import Rel8.Kind.Algebra
32 ( Algebra( Product, Sum )
37 ( Algebra( Product, Sum )
33 , SAlgebra( SProduct, SSum )
38 , SAlgebra( SProduct, SSum )
34 , KnownAlgebra, algebraSing
39 , KnownAlgebra, algebraSing
35 )
40 )
41+import Rel8.Schema.Context ( Col )
36import qualified Rel8.Schema.Kind as K
42import qualified Rel8.Schema.Kind as K
43+import Rel8.Schema.Null ( Nullify )
44+import Rel8.Schema.Spec ( Spec( Spec ), SSpec )
37import Rel8.Schema.Result ( Result )
45import Rel8.Schema.Result ( Result )
38
46
39
47
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
41 :: Algebra
66 :: Algebra
42 -> (Type -> Type -> Exp Constraint)
67 -> (Type -> Type -> Exp Constraint)
43 -> (Type -> Exp K.HTable)
68 -> (Type -> Exp K.HTable)
@@ -46,12 +71,12 @@ data GGSerialize
46 -> Exp Constraint
71 -> Exp Constraint
47
72
48
73
49−type instance Eval (GGSerialize 'Product _Serialize _Columns exprs rep) =
74+type instance Eval (GGToExprs 'Product _ToExprs _Columns exprs rep) =
50− GSerialize _Serialize _Columns exprs rep
75+ GToExprs _ToExprs _Columns exprs rep
51
76
52
77
53−type instance Eval (GGSerialize 'Sum _Serialize _Columns exprs rep) =
78+type instance Eval (GGToExprs 'Sum _ToExprs _Columns exprs rep) =
54− GSerializeADT _Serialize _Columns exprs rep
79+ GToExprsADT _ToExprs _Columns exprs rep
55
80
56
81
57data GGColumns
82data GGColumns
@@ -67,6 +92,19 @@ type instance Eval (GGColumns 'Product _Columns rep) = GColumns _Columns rep
67type instance Eval (GGColumns 'Sum _Columns rep) = GColumnsADT _Columns rep
92type instance Eval (GGColumns 'Sum _Columns rep) = GColumnsADT _Columns rep
68
93
69
94
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+
70type GAlgebra :: (Type -> Type) -> Algebra
108type GAlgebra :: (Type -> Type) -> Algebra
71type family GAlgebra rep where
109type family GAlgebra rep where
72 GAlgebra (M1 _ _ rep) = GAlgebra rep
110 GAlgebra (M1 _ _ rep) = GAlgebra rep
@@ -77,27 +115,74 @@ type family GAlgebra rep where
77 GAlgebra (K1 _ _) = 'Product
115 GAlgebra (K1 _ _) = 'Product
78
116
79
117
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+
80−ggfromResult :: forall algebra _Serialize _Columns exprs rep x.
161+ggfromResult :: forall algebra _ToExprs _Columns exprs rep x.
81 ( KnownAlgebra algebra
162 ( KnownAlgebra algebra
82− , Eval (GGSerialize algebra _Serialize _Columns exprs rep)
163+ , Eval (GGToExprs algebra _ToExprs _Columns exprs rep)
83 )
164 )
84− => (forall expr a proxy. Eval (_Serialize expr a)
165+ => (forall expr a proxy. Eval (_ToExprs expr a)
166+ => proxy expr
85− => proxy expr -> Eval (_Columns expr) Result -> a)
167+ -> Eval (_Columns expr) (Col Result)
168+ -> a)
86− -> Eval (GGColumns algebra _Columns exprs) Result
169+ -> Eval (GGColumns algebra _Columns exprs) (Col Result)
87 -> rep x
170 -> rep x
88−ggfromResult f x = case algebraSing @algebra of
171+ggfromResult = case algebraSing @algebra of
89− SProduct -> gfromResult @_Serialize @_Columns @exprs @rep f x
172+ SProduct -> gfromResult @_ToExprs @_Columns @exprs
90− SSum -> gfromResultADT @_Serialize @_Columns @exprs @rep f x
173+ SSum -> gfromResultADT @_ToExprs @_Columns @exprs
91
174
92
175
93−ggtoResult :: forall algebra _Serialize _Columns exprs rep x.
176+ggtoResult :: forall algebra _ToExprs _Columns exprs rep x.
94 ( KnownAlgebra algebra
177 ( KnownAlgebra algebra
95− , Eval (GGSerialize algebra _Serialize _Columns exprs rep)
178+ , Eval (GGToExprs algebra _ToExprs _Columns exprs rep)
96 )
179 )
97− => (forall expr a proxy. Eval (_Serialize expr a)
180+ => (forall expr a proxy. Eval (_ToExprs expr a)
181+ => proxy expr
182+ -> a
98− => proxy expr -> a -> Eval (_Columns expr) Result)
183+ -> Eval (_Columns expr) (Col Result))
99 -> rep x
184 -> rep x
100− -> Eval (GGColumns algebra _Columns exprs) Result
185+ -> Eval (GGColumns algebra _Columns exprs) (Col Result)
101−ggtoResult f x = case algebraSing @algebra of
186+ggtoResult = case algebraSing @algebra of
102− SProduct -> gtoResult @_Serialize @_Columns @exprs @rep f x
187+ SProduct -> gtoResult @_ToExprs @_Columns @exprs
103− SSum -> gtoResultADT @_Serialize @_Columns @exprs @rep f x
188+ SSum -> gtoResultADT @_ToExprs @_Columns @exprs
src/Rel8/Generic/Table/ADT.hs+203−61
@@ -4,6 +4,7 @@
4{-# language FlexibleInstances #-}
4{-# language FlexibleInstances #-}
5{-# language LambdaCase #-}
5{-# language LambdaCase #-}
6{-# language MultiParamTypeClasses #-}
6{-# language MultiParamTypeClasses #-}
7+{-# language PolyKinds #-}
7{-# language RankNTypes #-}
8{-# language RankNTypes #-}
8{-# language ScopedTypeVariables #-}
9{-# language ScopedTypeVariables #-}
9{-# language StandaloneKindSignatures #-}
10{-# language StandaloneKindSignatures #-}
@@ -13,13 +14,13 @@
13{-# language UndecidableInstances #-}
14{-# language UndecidableInstances #-}
14
15
15module Rel8.Generic.Table.ADT
16module Rel8.Generic.Table.ADT
17+ ( GTableADT, GColumnsADT, gfromColumnsADT, gtoColumnsADT, gtableADT
18+ , GTableADT', GColumnsADT'
16− ( GSerializeADT, GColumnsADT, gfromResultADT, gtoResultADT
19+ , GToExprsADT, gfromResultADT, gtoResultADT
17− , GSerializeADT', GColumnsADT'
18 )
20 )
19where
21where
20
22
21-- base
23-- base
22−import Data.Functor.Identity ( Identity( Identity ) )
23import Data.Kind ( Constraint, Type )
24import Data.Kind ( Constraint, Type )
24import Data.Proxy ( Proxy( Proxy ) )
25import Data.Proxy ( Proxy( Proxy ) )
25import GHC.Generics
26import GHC.Generics
@@ -32,14 +33,23 @@ import Prelude hiding ( null )
32
33
33-- rel8
34-- rel8
34import Rel8.FCF ( Eval, Exp )
35import Rel8.FCF ( Eval, Exp )
35−import Rel8.Generic.Table.Record ( GSerialize, GColumns, gfromResult, gtoResult )
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 )
36−import Rel8.Schema.HTable ( HTable )
41+import Rel8.Schema.HTable ( HTable, hmap )
37−import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) )
42+import Rel8.Schema.HTable.Identity ( HIdentity( HType ), HType )
38import Rel8.Schema.HTable.Label ( HLabel, hlabel, hunlabel )
43import Rel8.Schema.HTable.Label ( HLabel, hlabel, hunlabel )
39import Rel8.Schema.HTable.Nullify ( HNullify, hnulls, hnullify, hunnullify )
44import Rel8.Schema.HTable.Nullify ( HNullify, hnulls, hnullify, hunnullify )
40import Rel8.Schema.HTable.Product ( HProduct( HProduct ) )
45import Rel8.Schema.HTable.Product ( HProduct( HProduct ) )
41import qualified Rel8.Schema.Kind as K
46import qualified Rel8.Schema.Kind as K
47+import Rel8.Schema.Null ( Nullify )
48+import Rel8.Schema.Spec ( Spec( Spec ), SSpec )
49+import Rel8.Schema.Result
50+ ( Col( R ), Result
42−import Rel8.Schema.Result ( Result, null, nullifier, unnullifier )
51+ , null, nullifier, unnullifier
52+ )
43import Rel8.Type.Tag ( Tag( Tag ) )
53import Rel8.Type.Tag ( Tag( Tag ) )
44
54
45-- text
55-- text
@@ -51,7 +61,7 @@ type GColumnsADT
51 -> (Type -> Type) -> K.HTable
61 -> (Type -> Type) -> K.HTable
52type family GColumnsADT _Columns rep where
62type family GColumnsADT _Columns rep where
53 GColumnsADT _Columns (M1 D _ rep) =
63 GColumnsADT _Columns (M1 D _ rep) =
54− GColumnsADT' _Columns (HLabel "tag" (HIdentity Tag)) rep
64+ GColumnsADT' _Columns (HLabel "tag" (HType Tag)) rep
55
65
56
66
57type GColumnsADT'
67type GColumnsADT'
@@ -65,118 +75,246 @@ type family GColumnsADT' _Columns htable rep where
65 HProduct htable (HLabel label (HNullify (GColumns _Columns rep)))
75 HProduct htable (HLabel label (HNullify (GColumns _Columns rep)))
66
76
67
77
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
69 :: (Type -> Type -> Exp Constraint)
184 :: (Type -> Type -> Exp Constraint)
70 -> (Type -> Exp K.HTable)
185 -> (Type -> Exp K.HTable)
71 -> (Type -> Type) -> (Type -> Type) -> Constraint
186 -> (Type -> Type) -> (Type -> Type) -> Constraint
72−class GSerializeADT _Serialize _Columns exprs rep where
187+class GToExprsADT _ToExprs _Columns exprs rep where
73 gfromResultADT :: ()
188 gfromResultADT :: ()
74− => (forall expr a proxy. Eval (_Serialize expr a)
189+ => (forall expr a proxy.
190+ ( Eval (_ToExprs expr a)
191+ , HTable (Eval (_Columns expr))
192+ )
193+ => proxy expr
75− => proxy expr -> Eval (_Columns expr) Result -> a)
194+ -> Eval (_Columns expr) (Col Result)
195+ -> a)
76− -> GColumnsADT _Columns exprs Result
196+ -> GColumnsADT _Columns exprs (Col Result)
77 -> rep x
197 -> rep x
78
198
79 gtoResultADT :: ()
199 gtoResultADT :: ()
80− => (forall expr a proxy. Eval (_Serialize expr a)
200+ => (forall expr a proxy.
201+ ( Eval (_ToExprs expr a)
202+ , HTable (Eval (_Columns expr))
203+ )
204+ => proxy expr
205+ -> a
81− => proxy expr -> a -> Eval (_Columns expr) Result)
206+ -> Eval (_Columns expr) (Col Result))
82 -> rep x
207 -> rep x
83− -> GColumnsADT _Columns exprs Result
208+ -> GColumnsADT _Columns exprs (Col Result)
84
209
85
210
86instance
211instance
87− ( htable ~ HLabel "tag" (HIdentity Tag)
212+ ( htable ~ HLabel "tag" (HType Tag)
88− , GSerializeADT' _Serialize _Columns htable exprs rep
213+ , GToExprsADT' _ToExprs _Columns htable exprs rep
89 )
214 )
90− => GSerializeADT _Serialize _Columns (M1 D meta exprs) (M1 D meta rep)
215+ => GToExprsADT _ToExprs _Columns (M1 D meta exprs) (M1 D meta rep)
91 where
216 where
92 gfromResultADT fromResult columns =
217 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
94 Just rep -> M1 rep
219 Just rep -> M1 rep
95 _ -> error "ADT.fromColumns: mismatch between tag and data"
220 _ -> error "ADT.fromColumns: mismatch between tag and data"
96 where
221 where
97− tag = (\(HIdentity (Identity a)) -> a) . hunlabel @"tag"
222+ tag = (\(HType (R a)) -> a) . hunlabel @_ @"tag" unlabeler
98
223
99 gtoResultADT toResult (M1 rep) =
224 gtoResultADT toResult (M1 rep) =
100− gtoResultADT' @_Serialize @_Columns @htable @exprs @rep toResult tag (Just rep)
225+ gtoResultADT' @_ToExprs @_Columns @_ @exprs toResult tag (Just rep)
101 where
226 where
102− tag = hlabel @"tag" . HIdentity . Identity
227+ tag = hlabel @_ @"tag" labeler . HType . R
103
228
104
229
105−type GSerializeADT'
230+type GToExprsADT'
106 :: (Type -> Type -> Exp Constraint)
231 :: (Type -> Type -> Exp Constraint)
107 -> (Type -> Exp K.HTable)
232 -> (Type -> Exp K.HTable)
108 -> K.HTable -> (Type -> Type) -> (Type -> Type) -> Constraint
233 -> K.HTable -> (Type -> Type) -> (Type -> Type) -> Constraint
109−class GSerializeADT' _Serialize _Columns htable exprs rep where
234+class GToExprsADT' _ToExprs _Columns htable exprs rep where
110− gfromResultADT' :: context ~ Result
235+ gfromResultADT' :: ()
111− => (forall expr a proxy. Eval (_Serialize expr a)
236+ => (forall expr a proxy.
237+ ( Eval (_ToExprs expr a)
238+ , HTable (Eval (_Columns expr))
239+ )
240+ => proxy expr
112− => proxy expr -> Eval (_Columns expr) context -> a)
241+ -> Eval (_Columns expr) (Col Result)
242+ -> a)
113− -> (htable Result -> Tag)
243+ -> (htable (Col Result) -> Tag)
114− -> GColumnsADT' _Columns htable exprs context
244+ -> GColumnsADT' _Columns htable exprs (Col Result)
115 -> Maybe (rep x)
245 -> Maybe (rep x)
116
246
117− gtoResultADT' :: context ~ Result
247+ gtoResultADT' :: ()
118− => (forall expr a proxy. Eval (_Serialize expr a)
248+ => (forall expr a proxy.
249+ ( Eval (_ToExprs expr a)
250+ , HTable (Eval (_Columns expr))
251+ )
252+ => proxy expr
253+ -> a
119− => proxy expr -> a -> Eval (_Columns expr) context)
254+ -> Eval (_Columns expr) (Col Result))
120− -> (Tag -> htable Result)
255+ -> (Tag -> htable (Col Result))
121 -> Maybe (rep x)
256 -> Maybe (rep x)
122− -> GColumnsADT' _Columns htable exprs context
257+ -> GColumnsADT' _Columns htable exprs (Col Result)
123
258
124 extract :: GColumnsADT' _Columns htable exprs context -> htable context
259 extract :: GColumnsADT' _Columns htable exprs context -> htable context
125
260
126
261
127instance
262instance
128− ( htable' ~ GColumnsADT' _Columns htable exprs1
263+ ( htable' ~ GColumnsADT' _Columns htable a
129− , GSerializeADT' _Serialize _Columns htable exprs1 a
264+ , GToExprsADT' _ToExprs _Columns htable a rep1
130− , GSerializeADT' _Serialize _Columns htable' exprs2 b
265+ , GToExprsADT' _ToExprs _Columns htable' b rep2
131 )
266 )
132− => GSerializeADT' _Serialize _Columns htable (exprs1 :+: exprs2) (a :+: b)
267+ => GToExprsADT' _ToExprs _Columns htable (a :+: b) (rep1 :+: rep2)
133 where
268 where
134 gfromResultADT' fromResult f columns =
269 gfromResultADT' fromResult f columns =
135 case ma of
270 case ma of
136 Just a -> Just (L1 a)
271 Just a -> Just (L1 a)
137 Nothing -> R1 <$>
272 Nothing -> R1 <$>
138− gfromResultADT' @_Serialize @_Columns @_ @exprs2 @b
273+ gfromResultADT' @_ToExprs @_Columns @_ @b @rep2
139 fromResult
274 fromResult
140− (f . extract @_Serialize @_Columns @_ @exprs1 @a)
275+ (f . extract @_ToExprs @_Columns @_ @a @rep1)
141 columns
276 columns
142 where
277 where
143 ma =
278 ma =
144− gfromResultADT' @_Serialize @_Columns @_ @exprs1 @a
279+ gfromResultADT' @_ToExprs @_Columns @_ @a @rep1
145 fromResult
280 fromResult
146 f
281 f
147− (extract @_Serialize @_Columns @_ @exprs2 @b columns)
282+ (extract @_ToExprs @_Columns @_ @b @rep2 columns)
148
283
149 gtoResultADT' toResult tag = \case
284 gtoResultADT' toResult tag = \case
150 Just (L1 a) ->
285 Just (L1 a) ->
151− gtoResultADT' @_Serialize @_Columns @_ @exprs2 @b
286+ gtoResultADT' @_ToExprs @_Columns @_ @b @rep2
152 toResult
287 toResult
153− (\_ -> gtoResultADT' @_Serialize @_Columns @_ @exprs1 @a
288+ (\_ -> gtoResultADT' @_ToExprs @_Columns @_ @a @rep1
154 toResult
289 toResult
155 tag
290 tag
156 (Just a))
291 (Just a))
157 Nothing
292 Nothing
158 Just (R1 b) ->
293 Just (R1 b) ->
159− gtoResultADT' @_Serialize @_Columns @_ @exprs2 @b
294+ gtoResultADT' @_ToExprs @_Columns @_ @b @rep2
160 toResult
295 toResult
161 (\tag' ->
296 (\tag' ->
162− gtoResultADT' @_Serialize @_Columns @_ @exprs1 @a
297+ gtoResultADT' @_ToExprs @_Columns @_ @a @rep1
163 toResult
298 toResult
164 (\_ -> tag tag')
299 (\_ -> tag tag')
165 Nothing)
300 Nothing)
166 (Just b)
301 (Just b)
167 Nothing ->
302 Nothing ->
168− gtoResultADT' @_Serialize @_Columns @_ @exprs2 @b
303+ gtoResultADT' @_ToExprs @_Columns @_ @b @rep2
169 toResult
304 toResult
170− (\_ -> gtoResultADT' @_Serialize @_Columns @_ @exprs1 @a toResult tag Nothing)
305+ (\_ -> gtoResultADT' @_ToExprs @_Columns @_ @a @rep1 toResult tag Nothing)
171 Nothing
306 Nothing
172
307
173 extract =
308 extract =
174− extract @_Serialize @_Columns @_ @exprs1 @a .
309+ extract @_ToExprs @_Columns @_ @a @rep1 .
175− extract @_Serialize @_Columns @_ @exprs2 @b
310+ extract @_ToExprs @_Columns @_ @b @rep2
176
311
177
312
313+instance
178−instance (meta ~ 'MetaCons label _fixity _isRecord, KnownSymbol label) =>
314+ ( meta ~ 'MetaCons label _fixity _isRecord
315+ , KnownSymbol label
316+ )
179− GSerializeADT' _Serialize _Columns _htable (M1 C meta U1) (M1 C meta U1)
317+ => GToExprsADT' _ToExprs _Columns htable (M1 C meta U1) (M1 C meta U1)
180 where
318 where
181 gfromResultADT' _ tag columns
319 gfromResultADT' _ tag columns
182 | tag columns == tag' = Just (M1 U1)
320 | tag columns == tag' = Just (M1 U1)
@@ -193,29 +331,33 @@ instance (meta ~ 'MetaCons label _fixity _isRecord, KnownSymbol label) =>
193
331
194instance {-# OVERLAPPABLE #-}
332instance {-# OVERLAPPABLE #-}
195 ( HTable (GColumns _Columns exprs)
333 ( HTable (GColumns _Columns exprs)
196− , GSerialize _Serialize _Columns exprs rep
334+ , GToExprs _ToExprs _Columns exprs rep
197 , meta ~ 'MetaCons label _fixity _isRecord
335 , meta ~ 'MetaCons label _fixity _isRecord
198 , KnownSymbol label
336 , KnownSymbol label
199− , GColumnsADT' _Columns htable (M1 C ('MetaCons label _fixity _isRecord) exprs) ~
337+ , GColumnsADT' _Columns htable (M1 C meta exprs) ~
200 HProduct htable (HLabel label (HNullify (GColumns _Columns exprs)))
338 HProduct htable (HLabel label (HNullify (GColumns _Columns exprs)))
201 )
339 )
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)
203 where
341 where
204 gfromResultADT' fromResult tag (HProduct a b)
342 gfromResultADT' fromResult tag (HProduct a b)
205 | tag a == tag' =
343 | tag a == tag' =
206− M1 . gfromResult @_Serialize @_Columns @exprs @rep fromResult <$>
344+ M1 . gfromResult @_ToExprs @_Columns @exprs fromResult <$>
207− hunnullify unnullifier (hunlabel b)
345+ hunnullify unnullifier (hunlabel unlabeler b)
208 | otherwise = Nothing
346 | otherwise = Nothing
209 where
347 where
210 tag' = Tag $ pack $ symbolVal (Proxy @label)
348 tag' = Tag $ pack $ symbolVal (Proxy @label)
211
349
212 gtoResultADT' toResult tag = \case
350 gtoResultADT' toResult tag = \case
213− Nothing -> HProduct (tag tag') (hlabel (hnulls (const null)))
351+ Nothing -> HProduct (tag tag') (hlabel labeler (hnulls (const null)))
214 Just (M1 rep) -> HProduct (tag tag') $
352 Just (M1 rep) -> HProduct (tag tag') $
215− hlabel $
353+ hlabel labeler $
216 hnullify nullifier $
354 hnullify nullifier $
217− gtoResult @_Serialize @_Columns @exprs @rep toResult rep
355+ gtoResult @_ToExprs @_Columns @exprs toResult rep
218 where
356 where
219 tag' = Tag $ pack $ symbolVal (Proxy @label)
357 tag' = Tag $ pack $ symbolVal (Proxy @label)
220
358
221 extract (HProduct a _) = a
359 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 @@
14
14
15module Rel8.Generic.Table.Record
15module Rel8.Generic.Table.Record
16 ( GTable, GColumns, GContext, gfromColumns, gtoColumns, gtable
16 ( GTable, GColumns, GContext, gfromColumns, gtoColumns, gtable
17− , GSerialize, gfromResult, gtoResult
17+ , GToExprs, gfromResult, gtoResult
18 )
18 )
19where
19where
20
20
@@ -26,13 +26,18 @@ import GHC.Generics
26 , C, D, S
26 , C, D, S
27 , Meta( MetaSel )
27 , Meta( MetaSel )
28 )
28 )
29+import GHC.TypeLits ( KnownSymbol )
29import Prelude hiding ( null )
30import Prelude hiding ( null )
30
31
31-- rel8
32-- rel8
32import Rel8.FCF ( Eval, Exp )
33import 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 )
33import Rel8.Schema.HTable.Label ( HLabel, hlabel, hunlabel )
37import Rel8.Schema.HTable.Label ( HLabel, hlabel, hunlabel )
34import Rel8.Schema.HTable.Product ( HProduct(..) )
38import Rel8.Schema.HTable.Product ( HProduct(..) )
35import qualified Rel8.Schema.Kind as K
39import qualified Rel8.Schema.Kind as K
40+import Rel8.Schema.Result ( Result )
36
41
37
42
38type GColumns :: (Type -> Exp K.HTable) -> (Type -> Type) -> K.HTable
43type GColumns :: (Type -> Exp K.HTable) -> (Type -> Type) -> K.HTable
@@ -55,8 +60,8 @@ type family GContext _Context rep where
55type GTable
60type GTable
56 :: (Type -> Exp Constraint)
61 :: (Type -> Exp Constraint)
57 -> (Type -> Exp K.HTable)
62 -> (Type -> Exp K.HTable)
58− -> (Type -> Type) -> Constraint
63+ -> K.HContext -> (Type -> Type) -> Constraint
59−class GTable _Table _Columns rep
64+class GTable _Table _Columns context rep
60 where
65 where
61 gfromColumns :: ()
66 gfromColumns :: ()
62 => (forall a. Eval (_Table a) => Eval (_Columns a) context -> a)
67 => (forall a. Eval (_Table a) => Eval (_Columns a) context -> a)
@@ -73,109 +78,129 @@ class GTable _Table _Columns rep
73 -> GColumns _Columns rep context
78 -> GColumns _Columns rep context
74
79
75
80
76−type GSerialize
81+instance GTable _Table _Columns context rep =>
77− :: (Type -> Type -> Exp Constraint)
82+ GTable _Table _Columns context (M1 D c rep)
78− -> (Type -> Exp K.HTable)
79− -> (Type -> Type) -> (Type -> Type) -> Constraint
80−class GSerialize _Serialize _Columns exprs rep
81 where
83 where
82− gfromResult :: ()
84+ gfromColumns fromColumns =
83− => (forall expr a proxy. Eval (_Serialize expr a)
85+ M1 . gfromColumns @_Table @_Columns @context @rep fromColumns
84− => proxy expr -> Eval (_Columns expr) context -> a)
86+ gtoColumns toColumns (M1 a) =
85− -> GColumns _Columns exprs context
87+ gtoColumns @_Table @_Columns @context @rep toColumns a
86− -> rep x
87−
88− gtoResult :: ()
89− => (forall expr a proxy. Eval (_Serialize expr a)
90− => proxy expr -> a -> Eval (_Columns expr) context)
88+ gtable = gtable @_Table @_Columns @context @rep
91− -> rep x
92− -> GColumns _Columns exprs context
93
89
94
90
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)
96 where
93 where
97 gfromColumns fromColumns =
94 gfromColumns fromColumns =
98− M1 . gfromColumns @_Table @_Columns @rep fromColumns
95+ M1 . gfromColumns @_Table @_Columns @context @rep fromColumns
99 gtoColumns toColumns (M1 a) =
96 gtoColumns toColumns (M1 a) =
100− gtoColumns @_Table @_Columns @rep toColumns a
97+ gtoColumns @_Table @_Columns @context @rep toColumns a
101− gtable = gtable @_Table @_Columns @rep
98+ gtable = gtable @_Table @_Columns @context @rep
102
99
103
100
104−instance GSerialize _Serialize _Columns exprs rep =>
101+instance
105− GSerialize _Serialize _Columns (M1 D c exprs) (M1 D c rep)
102+ ( GTable _Table _Columns context rep1
103+ , GTable _Table _Columns context rep2
104+ )
105+ => GTable _Table _Columns context (rep1 :*: rep2)
106 where
106 where
107− gfromResult fromResult =
107+ gfromColumns fromColumns (HProduct a b) =
108− M1 . gfromResult @_Serialize @_Columns @exprs @rep fromResult
108+ gfromColumns @_Table @_Columns @context @rep1 fromColumns a :*:
109− gtoResult toResult (M1 a) =
109+ gfromColumns @_Table @_Columns @context @rep2 fromColumns b
110− gtoResult @_Serialize @_Columns @exprs @rep toResult a
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)
111
116
112
117
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+ )
113−instance GTable _Table _Columns rep => GTable _Table _Columns (M1 C c rep)
126+ => GTable _Table _Columns context (M1 S meta k1)
114 where
127 where
115− gfromColumns fromColumns =
128+ gfromColumns fromColumns = M1 . K1 . fromColumns . hunlabel hunlabeler
116− M1 . gfromColumns @_Table @_Columns @rep fromColumns
117− gtoColumns toColumns (M1 a) =
129+ gtoColumns toColumns (M1 (K1 a)) = hlabel hlabeler (toColumns a)
118− gtoColumns @_Table @_Columns @rep toColumns a
130+ gtable table = hlabel hlabeler (table (Proxy @a))
119− gtable = gtable @_Table @_Columns @rep
120
131
121
132
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+
122−instance GSerialize _Serialize _Columns exprs rep =>
160+instance GToExprs _ToExprs _Columns exprs rep =>
123− GSerialize _Serialize _Columns (M1 C c exprs) (M1 C c rep)
161+ GToExprs _ToExprs _Columns (M1 D c exprs) (M1 D c rep)
124 where
162 where
125 gfromResult fromResult =
163 gfromResult fromResult =
126− M1 . gfromResult @_Serialize @_Columns @exprs @rep fromResult
164+ M1 . gfromResult @_ToExprs @_Columns @exprs fromResult
127 gtoResult toResult (M1 a) =
165 gtoResult toResult (M1 a) =
128− gtoResult @_Serialize @_Columns @exprs @rep toResult a
166+ gtoResult @_ToExprs @_Columns @exprs toResult a
129
167
130
168
131−instance (GTable _Table _Columns rep1, GTable _Table _Columns rep2) =>
169+instance GToExprs _ToExprs _Columns exprs rep =>
132− GTable _Table _Columns (rep1 :*: rep2)
170+ GToExprs _ToExprs _Columns (M1 C c exprs) (M1 C c rep)
133 where
171 where
134− gfromColumns fromColumns (HProduct a b) =
172+ gfromResult fromResult =
135− gfromColumns @_Table @_Columns @rep1 fromColumns a :*:
173+ M1 . gfromResult @_ToExprs @_Columns @exprs fromResult
136− gfromColumns @_Table @_Columns @rep2 fromColumns b
174+ gtoResult toResult (M1 a) =
137− gtoColumns toColumns (a :*: b) = HProduct
175+ gtoResult @_ToExprs @_Columns @exprs toResult a
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)
143
176
144
177
145instance
178instance
146− ( GSerialize _Serialize _Columns expr1 rep1
179+ ( GToExprs _ToExprs _Columns exprs1 rep1
147− , GSerialize _Serialize _Columns expr2 rep2
180+ , GToExprs _ToExprs _Columns exprs2 rep2
148 )
181 )
149− => GSerialize _Serialize _Columns (expr1 :*: expr2) (rep1 :*: rep2)
182+ => GToExprs _ToExprs _Columns (exprs1 :*: exprs2) (rep1 :*: rep2)
150 where
183 where
151 gfromResult fromResult (HProduct a b) =
184 gfromResult fromResult (HProduct a b) =
152− gfromResult @_Serialize @_Columns @expr1 @rep1 fromResult a :*:
185+ gfromResult @_ToExprs @_Columns @exprs1 fromResult a :*:
153− gfromResult @_Serialize @_Columns @expr2 @rep2 fromResult b
186+ gfromResult @_ToExprs @_Columns @exprs2 fromResult b
154 gtoResult toResult (a :*: b) =
187 gtoResult toResult (a :*: b) =
155 HProduct
188 HProduct
156− (gtoResult @_Serialize @_Columns @expr1 @rep1 toResult a)
189+ (gtoResult @_ToExprs @_Columns @exprs1 toResult a)
157− (gtoResult @_Serialize @_Columns @expr2 @rep2 toResult b)
190+ (gtoResult @_ToExprs @_Columns @exprs2 toResult b)
158
191
159
192
160instance
193instance
161− ( Eval (_Table a)
194+ ( Eval (_ToExprs exprs a)
195+ , HTable (Eval (_Columns exprs))
196+ , KnownSymbol label
162 , meta ~ 'MetaSel ('Just label) _su _ss _ds
197 , meta ~ 'MetaSel ('Just label) _su _ss _ds
163− , k1 ~ K1 i a
198+ , k1 ~ K1 i exprs
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
176 , k1' ~ K1 i a
199 , k1' ~ K1 i a
177 )
200 )
178− => GSerialize _Serialize _Columns (M1 S meta k1) (M1 S meta k1')
201+ => GToExprs _ToExprs _Columns (M1 S meta k1) (M1 S meta k1')
179 where
202 where
180− gfromResult fromResult = M1 . K1 . fromResult (Proxy @expr) . hunlabel
203+ gfromResult fromResult =
204+ M1 . K1 . fromResult (Proxy @exprs) . hunlabel hunlabeler
181− gtoResult toResult (M1 (K1 a)) = hlabel (toResult (Proxy @expr) a)
205+ gtoResult toResult (M1 (K1 a)) =
206+ hlabel hlabeler (toResult (Proxy @exprs) a)
src/Rel8/Kind/Context.hs+33−8
@@ -1,11 +1,13 @@
1{-# language DataKinds #-}
1{-# language DataKinds #-}
2{-# language GADTs #-}
2{-# language GADTs #-}
3+{-# language LambdaCase #-}
3{-# language StandaloneKindSignatures #-}
4{-# language StandaloneKindSignatures #-}
4−{-# language TypeSynonymInstances #-}
5
5
6module Rel8.Kind.Context
6module Rel8.Kind.Context
7 ( Reifiable( contextSing )
7 ( Reifiable( contextSing )
8 , SContext(..)
8 , SContext(..)
9+ , sReifiable
10+ , sLabelable
9 )
11 )
10where
12where
11
13
@@ -16,9 +18,12 @@ import Prelude ()
16-- rel8
18-- rel8
17import Rel8.Aggregate ( Aggregate )
19import Rel8.Aggregate ( Aggregate )
18import Rel8.Expr ( Expr )
20import 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 )
20import Rel8.Schema.Kind ( Context )
24import Rel8.Schema.Kind ( Context )
21import Rel8.Schema.Name ( Name )
25import Rel8.Schema.Name ( Name )
26+import Rel8.Schema.Reify ( Reify )
22import Rel8.Schema.Result ( Result )
27import Rel8.Schema.Result ( Result )
23
28
24
29
@@ -26,13 +31,13 @@ type SContext :: Context -> Type
26data SContext context where
31data SContext context where
27 SAggregate :: SContext Aggregate
32 SAggregate :: SContext Aggregate
28 SExpr :: SContext Expr
33 SExpr :: SContext Expr
29− SField :: SContext (Field table)
30 SName :: SContext Name
34 SName :: SContext Name
31 SResult :: SContext Result
35 SResult :: SContext Result
36+ SReify :: SContext context -> SContext (Reify context)
32
37
33
38
34type Reifiable :: Context -> Constraint
39type Reifiable :: Context -> Constraint
35−class Reifiable context where
40+class Interpretation context => Reifiable context where
36 contextSing :: SContext context
41 contextSing :: SContext context
37
42
38
43
@@ -44,13 +49,33 @@ instance Reifiable Expr where
44 contextSing = SExpr
49 contextSing = SExpr
45
50
46
51
47−instance Reifiable (Field table) where
48− contextSing = SField
49−
50−
51instance Reifiable Result where
52instance Reifiable Result where
52 contextSing = SResult
53 contextSing = SResult
53
54
54
55
55instance Reifiable Name where
56instance Reifiable Name where
56 contextSing = SName
57 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 @@
4
4
5module Rel8.Order
5module Rel8.Order
6 ( Order(..)
6 ( Order(..)
7+ , toOrderExprs
7 )
8 )
8where
9where
9
10
@@ -16,7 +17,8 @@ import Prelude
16import Data.Functor.Contravariant.Divisible ( Decidable, Divisible )
17import Data.Functor.Contravariant.Divisible ( Decidable, Divisible )
17
18
18-- opaleye
19-- 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
20
22
21
23
22-- | An ordering expression for @a@. Primitive orderings are defined with
24-- | An ordering expression for @a@. Primitive orderings are defined with
@@ -24,7 +26,12 @@ import qualified Opaleye.Order as Opaleye
24-- instances.
26-- instances.
25--
27--
26-- A common pattern is to use '<>' to combine multiple orderings in sequence,
28-- 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.
28type Order :: Type -> Type
30type Order :: Type -> Type
29newtype Order a = Order (Opaleye.Order a)
31newtype Order a = Order (Opaleye.Order a)
30 deriving newtype (Contravariant, Divisible, Decidable, Semigroup, Monoid)
32 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 #-}
2{-# language StandaloneKindSignatures #-}
3{-# language StandaloneKindSignatures #-}
3−{-# language UndecidableInstances #-}
4
4
5module Rel8.Query
5module Rel8.Query
6 ( Query( Query )
6 ( Query( Query )
@@ -8,201 +8,37 @@ module Rel8.Query
8where
8where
9
9
10-- base
10-- base
11−import Control.Applicative ( liftA2 )
12−import Control.Monad ( liftM2 )
13import Data.Kind ( Type )
11import Data.Kind ( Type )
14−import Data.Monoid ( Any( Any ) )
15import Prelude
12import Prelude
16
13
17-- opaleye
14-- opaleye
18−import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
15+import qualified Opaleye.Select 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
23
16
24-- rel8
17-- rel8
25−import Rel8.Expr ( Expr )
26import Rel8.Query.Set ( unionAll )
18import Rel8.Query.Set ( unionAll )
27−import Rel8.Query.Opaleye ( fromOpaleye )
28import Rel8.Query.Values ( values )
19import Rel8.Query.Values ( values )
29−import Rel8.Table ( Table, fromColumns, toColumns )
30import Rel8.Table.Alternative
20import Rel8.Table.Alternative
31 ( AltTable, (<|>:)
21 ( AltTable, (<|>:)
32 , AlternativeTable, emptyTable
22 , AlternativeTable, emptyTable
33 )
23 )
34−import Rel8.Table.Projection ( Projectable, apply, project )
35
24
36-- semigroupoids
25-- semigroupoids
37−import Data.Functor.Apply ( Apply, (<.>) )
26+import Data.Functor.Apply ( Apply, WrappedApplicative(..) )
38import Data.Functor.Bind ( Bind, (>>-) )
27import Data.Functor.Bind ( Bind, (>>-) )
39
28
40
29
41-- | The @Query@ monad allows you to compose a @SELECT@ query. This monad has
30-- | The @Query@ monad allows you to compose a @SELECT@ query. This monad has
42-- semantics similar to the list (@[]@) monad.
31-- semantics similar to the list (@[]@) monad.
43type Query :: Type -> Type
32type Query :: Type -> Type
44−newtype Query a =
33+newtype Query a = Query (Opaleye.Select a)
45− Query (
34+ deriving newtype (Functor, Applicative, Monad)
46− -- This is based on Opaleye's Select monad, but with two addtions. We
35+ deriving Apply via (WrappedApplicative Opaleye.Select)
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
174
36
175
37
176instance Bind Query where
38instance Bind Query where
177 (>>-) = (>>=)
39 (>>-) = (>>=)
178
40
179
41
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−
206-- | '<|>:' = 'unionAll'.
42-- | '<|>:' = 'unionAll'.
207instance AltTable Query where
43instance AltTable Query where
208 (<|>:) = unionAll
44 (<|>:) = unionAll
@@ -211,13 +47,3 @@ instance AltTable Query where
211-- | 'emptyTable' = 'values' @[]@.
47-- | 'emptyTable' = 'values' @[]@.
212instance AlternativeTable Query where
48instance AlternativeTable Query where
213 emptyTable = values []
49 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
7
7
8-- base
8-- base
9import Data.Kind ( Type )
9import Data.Kind ( Type )
10−import Data.Monoid ( Any )
11import Prelude ()
10import Prelude ()
12
11
13-- opaleye
12-- opaleye
14−import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
15import qualified Opaleye.Select as Opaleye
13import qualified Opaleye.Select as Opaleye
16
14
17
15
18type Query :: Type -> Type
16type 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 @@
1{-# language FlexibleContexts #-}
1{-# language FlexibleContexts #-}
2{-# language MonoLocalBinds #-}
2{-# language MonoLocalBinds #-}
3−{-# language ScopedTypeVariables #-}
4−{-# language TypeApplications #-}
5
3
6module Rel8.Query.Aggregate
4module Rel8.Query.Aggregate
7 ( aggregate
5 ( aggregate
8 , countRows
6 , countRows
9− , mode
10 )
7 )
11where
8where
12
9
13-- base
10-- base
14−import Data.Functor.Contravariant ( (>$<) )
15import Data.Int ( Int64 )
11import Data.Int ( Int64 )
16import Prelude
12import Prelude
17
13
@@ -22,16 +18,9 @@ import qualified Opaleye.Aggregate as Opaleye
22import Rel8.Aggregate ( Aggregates )
18import Rel8.Aggregate ( Aggregates )
23import Rel8.Expr ( Expr )
19import Rel8.Expr ( Expr )
24import Rel8.Expr.Aggregate ( countStar )
20import Rel8.Expr.Aggregate ( countStar )
25−import Rel8.Expr.Order ( desc )
26import Rel8.Query ( Query )
21import Rel8.Query ( Query )
27−import Rel8.Query.Limit ( limit )
28import Rel8.Query.Maybe ( optional )
22import Rel8.Query.Maybe ( optional )
29import Rel8.Query.Opaleye ( mapOpaleye )
23import 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 )
35import Rel8.Table.Opaleye ( aggregator )
24import Rel8.Table.Opaleye ( aggregator )
36import Rel8.Table.Maybe ( maybeTable )
25import Rel8.Table.Maybe ( maybeTable )
37
26
@@ -46,11 +35,3 @@ aggregate = mapOpaleye (Opaleye.aggregate aggregator)
46-- will return @0@.
35-- will return @0@.
47countRows :: Query a -> Query (Expr Int64)
36countRows :: Query a -> Query (Expr Int64)
48countRows = fmap (maybeTable 0 id) . optional . aggregate . fmap (const countStar)
37countRows = 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+
1module Rel8.Query.Distinct
3module Rel8.Query.Distinct
2 ( distinct
4 ( distinct
3 , distinctOn
5 , distinctOn
@@ -6,11 +8,12 @@ module Rel8.Query.Distinct
6where
8where
7
9
8-- base
10-- base
9−import Prelude ()
11+import Prelude
10
12
11-- opaleye
13-- opaleye
12import qualified Opaleye.Distinct as Opaleye
14import 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
14
17
15-- rel8
18-- rel8
16import Rel8.Order ( Order( Order ) )
19import Rel8.Order ( Order( Order ) )
@@ -30,11 +33,13 @@ distinct = mapOpaleye (Opaleye.distinctExplicit distinctspec)
30-- to a projection. If multiple rows have the same projection, it is
33-- to a projection. If multiple rows have the same projection, it is
31-- unspecified which row will be returned. If this matters, use 'distinctOnBy'.
34-- unspecified which row will be returned. If this matters, use 'distinctOnBy'.
32distinctOn :: EqTable b => (a -> b) -> Query a -> Query a
35distinctOn :: EqTable b => (a -> b) -> Query a -> Query a
36+distinctOn proj =
33−distinctOn proj = mapOpaleye (Opaleye.distinctOnExplicit unpackspec proj)
37+ mapOpaleye (\q -> Opaleye.productQueryArr (Opaleye.distinctOn unpackspec proj . Opaleye.runSimpleQueryArr q))
34
38
35
39
36-- | Select all distinct rows from a query, where rows are equivalent according
40-- | Select all distinct rows from a query, where rows are equivalent according
37-- to a projection. If there are multiple rows with the same projection, the
41-- to a projection. If there are multiple rows with the same projection, the
38-- first row according to the specified 'Order' will be returned.
42-- first row according to the specified 'Order' will be returned.
39distinctOnBy :: EqTable b => (a -> b) -> Order a -> Query a -> Query a
43distinctOnBy :: 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 )
17import Rel8.Query.Opaleye ( fromOpaleye )
17import Rel8.Query.Opaleye ( fromOpaleye )
18import Rel8.Schema.Name ( Selects )
18import Rel8.Schema.Name ( Selects )
19import Rel8.Schema.Table ( TableSchema )
19import Rel8.Schema.Table ( TableSchema )
20−import Rel8.Table.Cols ( fromCols, toCols )
20+import Rel8.Table ( fromColumns, toColumns )
21import Rel8.Table.Opaleye ( table, unpackspec )
21import Rel8.Table.Opaleye ( table, unpackspec )
22
22
23
23
@@ -25,8 +25,8 @@ import Rel8.Table.Opaleye ( table, unpackspec )
25-- table@.
25-- table@.
26each :: Selects names exprs => TableSchema names -> Query exprs
26each :: Selects names exprs => TableSchema names -> Query exprs
27each =
27each =
28− fmap fromCols .
28+ fmap fromColumns .
29 fromOpaleye .
29 fromOpaleye .
30 Opaleye.selectTableExplicit unpackspec .
30 Opaleye.selectTableExplicit unpackspec .
31 table .
31 table .
32− fmap toCols
32+ fmap toColumns
src/Rel8/Query/Either.hs+6−10
@@ -10,11 +10,7 @@ where
10-- base
10-- base
11import Prelude
11import Prelude
12
12
13−-- comonad
14−import Control.Comonad ( extract )
15−
16-- rel8
13-- rel8
17−import Rel8.Expr ( Expr )
18import Rel8.Expr.Eq ( (==.) )
14import Rel8.Expr.Eq ( (==.) )
19import Rel8.Query ( Query )
15import Rel8.Query ( Query )
20import Rel8.Query.Filter ( where_ )
16import Rel8.Query.Filter ( where_ )
@@ -27,17 +23,17 @@ import Rel8.Table.Maybe ( MaybeTable( MaybeTable ), isJustTable )
27
23
28
24
29-- | Filter 'EitherTable's, keeping only 'leftTable's.
25-- | Filter 'EitherTable's, keeping only 'leftTable's.
30−keepLeftTable :: EitherTable Expr a b -> Query a
26+keepLeftTable :: EitherTable a b -> Query a
31keepLeftTable e@(EitherTable _ a _) = do
27keepLeftTable e@(EitherTable _ a _) = do
32 where_ $ isLeftTable e
28 where_ $ isLeftTable e
33− pure (extract a)
29+ pure a
34
30
35
31
36-- | Filter 'EitherTable's, keeping only 'rightTable's.
32-- | Filter 'EitherTable's, keeping only 'rightTable's.
37−keepRightTable :: EitherTable Expr a b -> Query b
33+keepRightTable :: EitherTable a b -> Query b
38keepRightTable e@(EitherTable _ _ b) = do
34keepRightTable e@(EitherTable _ _ b) = do
39 where_ $ isRightTable e
35 where_ $ isRightTable e
40− pure (extract b)
36+ pure b
41
37
42
38
43-- | @bitraverseEitherTable f g x@ will pass all @leftTable@s through @f@ and
39-- | @bitraverseEitherTable f g x@ will pass all @leftTable@s through @f@ and
@@ -59,8 +55,8 @@ keepRightTable e@(EitherTable _ _ b) = do
59bitraverseEitherTable :: ()
55bitraverseEitherTable :: ()
60 => (a -> Query c)
56 => (a -> Query c)
61 -> (b -> Query d)
57 -> (b -> Query d)
62− -> EitherTable Expr a b
58+ -> EitherTable a b
63− -> Query (EitherTable Expr c d)
59+ -> Query (EitherTable c d)
64bitraverseEitherTable f g e@(EitherTable tag _ _) = do
60bitraverseEitherTable f g e@(EitherTable tag _ _) = do
65 mc@(MaybeTable _ c) <- optional (f =<< keepLeftTable e)
61 mc@(MaybeTable _ c) <- optional (f =<< keepLeftTable e)
66 md@(MaybeTable _ d) <- optional (g =<< keepRightTable e)
62 md@(MaybeTable _ d) <- optional (g =<< keepRightTable e)
src/Rel8/Query/Evaluate.hs+61−28
@@ -1,49 +1,82 @@
1+{-# language DerivingStrategies #-}
1{-# language FlexibleContexts #-}
2{-# language FlexibleContexts #-}
2−{-# language TupleSections #-}
3+{-# language GeneralizedNewtypeDeriving #-}
4+{-# language NamedFieldPuns #-}
5+{-# language StandaloneKindSignatures #-}
3
6
4module Rel8.Query.Evaluate
7module Rel8.Query.Evaluate
8+ ( Evaluate
9+ , eval
5− ( evaluate
10+ , evaluate
6 )
11 )
7where
12where
8
13
9-- base
14-- base
10−import Control.Monad ( (>=>) )
15+import Data.Kind ( Type )
11−import Data.Foldable ( foldl' )
16+import Data.Monoid ( Endo ( Endo ), appEndo )
12−import Data.List.NonEmpty ( NonEmpty( (:|) ), nonEmpty )
13−import Data.Monoid ( Any( Any ) )
14−import Prelude hiding ( undefined )
17+import Prelude
15
18
16-- opaleye
19-- opaleye
17import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
20import 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
18
26
19-- rel8
27-- rel8
20import Rel8.Expr ( Expr )
28import Rel8.Expr ( Expr )
21−import Rel8.Expr.Bool ( (&&.) )
22−import Rel8.Expr.Opaleye ( fromPrimExpr )
23import Rel8.Query ( Query( Query ) )
29import Rel8.Query ( Query( Query ) )
24−import Rel8.Query.Rebind ( rebind )
25import Rel8.Table ( Table )
30import Rel8.Table ( Table )
26−import Rel8.Table.Bool ( case_ )
31+import Rel8.Table.Opaleye ( unpackspec )
27−import Rel8.Table.Undefined ( undefined )
28
32
33+-- semigroupoids
34+import Data.Functor.Apply ( Apply )
35+import Data.Functor.Bind ( Bind, (>>-) )
29
36
30−-- | 'evaluate' takes expressions that could potentially have side effects and
37+-- transformers
31−-- \"runs\" them in the 'Query' monad. The returned expressions have no side
38+import Control.Monad.Trans.State.Strict ( State, get, put, runState )
32−-- effects and can safely be reused.
33−evaluate :: Table Expr a => a -> Query a
34−evaluate = laterally >=> rebind "eval"
35
39
36
40
37−laterally :: Table Expr a => a -> Query a
41+type Evaluations :: Type
38−laterally a = Query $ \bindings -> pure $ (Any True,) $
42+data Evaluations = Evaluations
39− case nonEmpty bindings of
43+ { tag :: !Opaleye.Tag
40− Nothing -> a
44+ , bindings :: !(Endo (Opaleye.Bindings Opaleye.PrimExpr))
41− Just bindings' -> case_ [(condition, a)] undefined
45+ }
42− where
43− condition = foldl1' (&&.) (fmap go bindings')
44− where
45− go = fromPrimExpr . Opaleye.UnExpr Opaleye.OpIsNotNull
46
46
47
47
48−foldl1' :: (a -> a -> a) -> NonEmpty a -> a
48+-- | Some PostgreSQL functions, such as 'Rel8.nextval', have side effects,
49−foldl1' f (a :| as) = foldl' f a as
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 @@
2
2
3module Rel8.Query.Exists
3module Rel8.Query.Exists
4 ( exists, inQuery
4 ( exists, inQuery
5− , present, with, withBy
5+ , whereExists, with, withBy
6− , absent, without, withoutBy
6+ , whereNotExists, without, withoutBy
7 )
7 )
8where
8where
9
9
@@ -11,44 +11,46 @@ where
11import Prelude hiding ( filter )
11import Prelude hiding ( filter )
12
12
13-- opaleye
13-- opaleye
14−import qualified Opaleye.Exists as Opaleye
15import qualified Opaleye.Operators as Opaleye
14import qualified Opaleye.Operators as Opaleye
16
15
17-- rel8
16-- rel8
18import Rel8.Expr ( Expr )
17import Rel8.Expr ( Expr )
19−import Rel8.Expr.Opaleye ( fromColumn, fromPrimExpr )
20import Rel8.Query ( Query )
18import Rel8.Query ( Query )
21import Rel8.Query.Filter ( filter )
19import Rel8.Query.Filter ( filter )
20+import Rel8.Query.Maybe ( optional )
22import Rel8.Query.Opaleye ( mapOpaleye )
21import Rel8.Query.Opaleye ( mapOpaleye )
23import Rel8.Table.Eq ( EqTable, (==:) )
22import Rel8.Table.Eq ( EqTable, (==:) )
23+import Rel8.Table.Maybe ( isJustTable )
24
24
25
25
26-- | Checks if a query returns at least one row.
26-- | Checks if a query returns at least one row.
27exists :: Query a -> Query (Expr Bool)
27exists :: 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
29
31
30
32
31inQuery :: EqTable a => a -> Query a -> Query (Expr Bool)
33inQuery :: EqTable a => a -> Query a -> Query (Expr Bool)
32inQuery a = exists . (>>= filter (a ==:))
34inQuery a = exists . (>>= filter (a ==:))
33
35
34
36
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@
36-- is equivalent to @WHERE EXISTS@ in SQL.
38-- is equivalent to @WHERE EXISTS@ in SQL.
37−present :: Query a -> Query ()
39+whereExists :: Query a -> Query ()
38−present = mapOpaleye Opaleye.restrictExists
40+whereExists = mapOpaleye Opaleye.restrictExists
39
41
40
42
41−-- | Produce the empty query if the given query returns rows. @absent@
43+-- | Produce the empty query if the given query returns rows. @whereNotExists@
42-- is equivalent to @WHERE NOT EXISTS@ in SQL.
44-- is equivalent to @WHERE NOT EXISTS@ in SQL.
43−absent :: Query a -> Query ()
45+whereNotExists :: Query a -> Query ()
44−absent = mapOpaleye Opaleye.restrictNotExists
46+whereNotExists = mapOpaleye Opaleye.restrictNotExists
45
47
46
48
47-- | @with@ is similar to 'filter', but allows the predicate to be a full query.
49-- | @with@ is similar to 'filter', but allows the predicate to be a full query.
48--
50--
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'.
50with :: (a -> Query b) -> a -> Query a
52with :: (a -> Query b) -> a -> Query a
51−with f a = a <$ present (f a)
53+with f a = a <$ whereExists (f a)
52
54
53
55
54-- | Like @with@, but with a custom membership test.
56-- | Like @with@, but with a custom membership test.
@@ -58,7 +60,7 @@ withBy predicate bs = with $ \a -> bs >>= filter (predicate a)
58
60
59-- | Filter rows where @a -> Query b@ yields no rows.
61-- | Filter rows where @a -> Query b@ yields no rows.
60without :: (a -> Query b) -> a -> Query a
62without :: (a -> Query b) -> a -> Query a
61−without f a = a <$ absent (f a)
63+without f a = a <$ whereNotExists (f a)
62
64
63
65
64-- | Like @without@, but with a custom membership test.
66-- | 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
19import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
19import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
20
20
21-- rel8
21-- rel8
22−import Rel8.Expr ( Expr )
22+import Rel8.Expr ( Col( E, unE ), Expr )
23import Rel8.Expr.Aggregate ( listAggExpr, nonEmptyAggExpr )
23import Rel8.Expr.Aggregate ( listAggExpr, nonEmptyAggExpr )
24import Rel8.Expr.Opaleye ( mapPrimExpr )
24import Rel8.Expr.Opaleye ( mapPrimExpr )
25import Rel8.Query ( Query )
25import Rel8.Query ( Query )
26import Rel8.Query.Aggregate ( aggregate )
26import Rel8.Query.Aggregate ( aggregate )
27import Rel8.Query.Maybe ( optional )
27import Rel8.Query.Maybe ( optional )
28−import Rel8.Query.Rebind ( rebind )
29import Rel8.Schema.HTable.Vectorize ( hunvectorize )
28import Rel8.Schema.HTable.Vectorize ( hunvectorize )
30import Rel8.Schema.Null ( Sql, Unnullify )
29import Rel8.Schema.Null ( Sql, Unnullify )
31−import Rel8.Schema.Spec ( Spec( Spec, info ) )
30+import Rel8.Schema.Spec ( SSpec( SSpec, info ) )
32−import Rel8.Table ( Table, fromColumns )
31+import Rel8.Table ( Table, fromColumns, toColumns )
33−import Rel8.Table.Cols ( toCols )
34import Rel8.Table.Aggregate ( listAgg, nonEmptyAgg )
32import Rel8.Table.Aggregate ( listAgg, nonEmptyAgg )
35import Rel8.Table.List ( ListTable( ListTable ) )
33import Rel8.Table.List ( ListTable( ListTable ) )
36import Rel8.Table.Maybe ( maybeTable )
34import Rel8.Table.Maybe ( maybeTable )
@@ -47,12 +45,12 @@ import Rel8.Type.Information ( TypeInformation )
47--
45--
48-- @many@ is analogous to 'Control.Applicative.many' from
46-- @many@ is analogous to 'Control.Applicative.many' from
49-- @Control.Applicative@.
47-- @Control.Applicative@.
50−many :: Table Expr a => Query a -> Query (ListTable Expr a)
48+many :: Table Expr a => Query a -> Query (ListTable a)
51many =
49many =
52 fmap (maybeTable mempty (\(ListTable a) -> ListTable a)) .
50 fmap (maybeTable mempty (\(ListTable a) -> ListTable a)) .
53 optional .
51 optional .
54 aggregate .
52 aggregate .
55− fmap (listAgg . toCols)
53+ fmap (listAgg . toColumns)
56
54
57
55
58-- | Aggregate a 'Query' into a 'NonEmptyTable'. If the supplied query returns
56-- | Aggregate a 'Query' into a 'NonEmptyTable'. If the supplied query returns
@@ -63,11 +61,11 @@ many =
63--
61--
64-- @some@ is analogous to 'Control.Applicative.some' from
62-- @some@ is analogous to 'Control.Applicative.some' from
65-- @Control.Applicative@.
63-- @Control.Applicative@.
66−some :: Table Expr a => Query a -> Query (NonEmptyTable Expr a)
64+some :: Table Expr a => Query a -> Query (NonEmptyTable a)
67some =
65some =
68 fmap (\(NonEmptyTable a) -> NonEmptyTable a) .
66 fmap (\(NonEmptyTable a) -> NonEmptyTable a) .
69 aggregate .
67 aggregate .
70− fmap (nonEmptyAgg . toCols)
68+ fmap (nonEmptyAgg . toColumns)
71
69
72
70
73-- | A version of 'many' specialised to single expressions.
71-- | A version of 'many' specialised to single expressions.
@@ -84,20 +82,18 @@ someExpr = aggregate . fmap nonEmptyAggExpr
84-- element of the given @ListTable@.
82-- element of the given @ListTable@.
85--
83--
86-- @catListTable@ is an inverse to 'many'.
84-- @catListTable@ is an inverse to 'many'.
87−catListTable :: Table Expr a => ListTable Expr a -> Query a
85+catListTable :: Table Expr a => ListTable a -> Query a
88−catListTable (ListTable as) =
86+catListTable (ListTable as) = pure $ fromColumns $ runIdentity $
89− rebind "unnest" $ fromColumns $ runIdentity $
90− hunvectorize (\Spec {info} -> pure . sunnest info) as
87+ hunvectorize (\SSpec {info} -> pure . E . sunnest info . unE) as
91
88
92
89
93-- | Expand a 'NonEmptyTable' into a 'Query', where each row in the query is an
90-- | Expand a 'NonEmptyTable' into a 'Query', where each row in the query is an
94-- element of the given @NonEmptyTable@.
91-- element of the given @NonEmptyTable@.
95--
92--
96-- @catNonEmptyTable@ is an inverse to 'some'.
93-- @catNonEmptyTable@ is an inverse to 'some'.
97−catNonEmptyTable :: Table Expr a => NonEmptyTable Expr a -> Query a
94+catNonEmptyTable :: Table Expr a => NonEmptyTable a -> Query a
98−catNonEmptyTable (NonEmptyTable as) =
95+catNonEmptyTable (NonEmptyTable as) = pure $ fromColumns $ runIdentity $
99− rebind "unnest" $ fromColumns $ runIdentity $
100− hunvectorize (\Spec {info} -> pure . sunnest info) as
96+ hunvectorize (\SSpec {info} -> pure . E . sunnest info . unE) as
101
97
102
98
103-- | Expand an expression that contains a list into a 'Query', where each row
99-- | Expand an expression that contains a list into a 'Query', where each row
@@ -105,7 +101,7 @@ catNonEmptyTable (NonEmptyTable as) =
105--
101--
106-- @catList@ is an inverse to 'manyExpr'.
102-- @catList@ is an inverse to 'manyExpr'.
107catList :: Sql DBType a => Expr [a] -> Query (Expr a)
103catList :: Sql DBType a => Expr [a] -> Query (Expr a)
108−catList = rebind "unnest" . sunnest typeInformation
104+catList = pure . sunnest typeInformation
109
105
110
106
111-- | Expand an expression that contains a non-empty list into a 'Query', where
107-- | Expand an expression that contains a non-empty list into a 'Query', where
@@ -113,7 +109,7 @@ catList = rebind "unnest" . sunnest typeInformation
113--
109--
114-- @catNonEmpty@ is an inverse to 'someExpr'.
110-- @catNonEmpty@ is an inverse to 'someExpr'.
115catNonEmpty :: Sql DBType a => Expr (NonEmpty a) -> Query (Expr a)
111catNonEmpty :: Sql DBType a => Expr (NonEmpty a) -> Query (Expr a)
116−catNonEmpty = rebind "unnest" . sunnest typeInformation
112+catNonEmpty = pure . sunnest typeInformation
117
113
118
114
119sunnest :: TypeInformation (Unnullify a) -> Expr (list a) -> Expr a
115sunnest :: 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−
5module Rel8.Query.Maybe
1module Rel8.Query.Maybe
6 ( optional
2 ( optional
7 , catMaybeTable
3 , catMaybeTable
@@ -12,20 +8,23 @@ where
12-- base
8-- base
13import Prelude
9import Prelude
14
10
15−-- comonad
16−import Control.Comonad ( extract )
17−
18-- opaleye
11-- 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
20
17
21-- rel8
18-- rel8
22−import Rel8.Expr ( Expr )
19+import Rel8.Expr.Bool ( true )
23import Rel8.Expr.Eq ( (==.) )
20import Rel8.Expr.Eq ( (==.) )
24−import Rel8.Expr.Opaleye ( fromColumn, fromPrimExpr )
21+import Rel8.Expr.Opaleye ( toPrimExpr )
25import Rel8.Query ( Query )
22import Rel8.Query ( Query )
26import Rel8.Query.Filter ( where_ )
23import Rel8.Query.Filter ( where_ )
27import Rel8.Query.Opaleye ( mapOpaleye )
24import 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 )
29
28
30
29
31-- | Convert a query that might return zero rows to a query that always returns
30-- | Convert a query that might return zero rows to a query that always returns
@@ -33,11 +32,18 @@ import Rel8.Table.Maybe ( MaybeTable(..), isJustTable )
33--
32--
34-- To speak in more concrete terms, 'optional' is most useful to write @LEFT
33-- To speak in more concrete terms, 'optional' is most useful to write @LEFT
35-- JOIN@s.
34-- JOIN@s.
36−optional :: Query a -> Query (MaybeTable Expr a)
35+optional :: Query a -> Query (MaybeTable a)
37−optional = mapOpaleye $ Opaleye.optionalInternal $ \tag a -> MaybeTable
36+optional = mapOpaleye $ Opaleye.QueryArr . go
38− { tag = fromPrimExpr $ fromColumn tag
37+ where
39− , just = pure a
38+ go query (i, left, tag) =
40− }
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
41
47
42
48
43-- | Filter out 'MaybeTable's, returning only the tables that are not-null.
49-- | Filter out 'MaybeTable's, returning only the tables that are not-null.
@@ -45,24 +51,23 @@ optional = mapOpaleye $ Opaleye.optionalInternal $ \tag a -> MaybeTable
45-- This operation can be used to "undo" the effect of 'optional', which
51-- This operation can be used to "undo" the effect of 'optional', which
46-- operationally is like turning a @LEFT JOIN@ back into a full @JOIN@. You
52-- operationally is like turning a @LEFT JOIN@ back into a full @JOIN@. You
47-- can think of this as analogous to 'Data.Maybe.catMaybes'.
53-- can think of this as analogous to 'Data.Maybe.catMaybes'.
48−catMaybeTable :: MaybeTable Expr a -> Query a
54+catMaybeTable :: MaybeTable a -> Query a
49catMaybeTable ma@(MaybeTable _ a) = do
55catMaybeTable ma@(MaybeTable _ a) = do
50 where_ $ isJustTable ma
56 where_ $ isJustTable ma
51− pure (extract a)
57+ pure a
52
58
53
59
54-- | Extend an optional query with another query. This is useful if you want
60-- | Extend an optional query with another query. This is useful if you want
55-- to step through multiple @LEFT JOINs@.
61-- to step through multiple @LEFT JOINs@.
56--
62--
57-- Note that @traverseMaybeTable@ takes a @a -> Query b@ function, which means
63-- 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
59-- @a -> Query b@ function returns no rows, then the resulting query will also
65-- @a -> Query b@ function returns no rows, then the resulting query will also
60-- have no rows. However, regardless of the given @a -> Query b@ function, if
66-- have no rows. However, regardless of the given @a -> Query b@ function, if
61-- the input is @nothingTable@, you will always get exactly one @nothingTable@
67-- the input is @nothingTable@, you will always get exactly one @nothingTable@
62-- back.
68-- back.
63−traverseMaybeTable :: (a -> Query b) -> MaybeTable Expr a -> Query (MaybeTable Expr b)
69+traverseMaybeTable :: (a -> Query b) -> MaybeTable a -> Query (MaybeTable b)
64traverseMaybeTable query ma@(MaybeTable input _) = do
70traverseMaybeTable query ma@(MaybeTable input _) = do
65− optional (query =<< catMaybeTable ma) >>= \case
71+ MaybeTable output b <- optional (query =<< catMaybeTable ma)
66− MaybeTable output b -> do
67− where_ $ output ==. input
72+ where_ $ expr output ==. expr input
68− pure $ MaybeTable input b
73+ pure $ MaybeTable input b
src/Rel8/Query/Null.hs−15
@@ -1,8 +1,5 @@
1−{-# language FlexibleContexts #-}
2−
3module Rel8.Query.Null
1module Rel8.Query.Null
4 ( catNull
2 ( catNull
5− , catNullTable
6 )
3 )
7where
4where
8
5
@@ -12,8 +9,6 @@ import Prelude
12-- rel8
9-- rel8
13import Rel8.Expr ( Expr )
10import Rel8.Expr ( Expr )
14import Rel8.Expr.Null ( isNonNull, unsafeUnnullify )
11import Rel8.Expr.Null ( isNonNull, unsafeUnnullify )
15−import Rel8.Table ( Table )
16−import Rel8.Table.Null ( NullTable, isNonNullTable, unsafeUnnullifyTable )
17import Rel8.Query ( Query )
12import Rel8.Query ( Query )
18import Rel8.Query.Filter ( where_ )
13import Rel8.Query.Filter ( where_ )
19
14
@@ -26,13 +21,3 @@ catNull :: Expr (Maybe a) -> Query (Expr a)
26catNull a = do
21catNull a = do
27 where_ $ isNonNull a
22 where_ $ isNonNull a
28 pure $ unsafeUnnullify a
23 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−
3module Rel8.Query.Opaleye
1module Rel8.Query.Opaleye
4 ( fromOpaleye
2 ( fromOpaleye
5 , toOpaleye
3 , toOpaleye
6 , mapOpaleye
4 , mapOpaleye
7 , zipOpaleyeWith
5 , zipOpaleyeWith
8− , unsafePeekQuery
9 )
6 )
10where
7where
11
8
12-- base
9-- base
13−import Control.Applicative ( liftA2 )
14import Prelude
10import Prelude
15
11
16-- opaleye
12-- opaleye
17−import qualified Opaleye.Internal.QueryArr as Opaleye
13+import qualified Opaleye.Select as Opaleye
18−import qualified Opaleye.Internal.Tag as Opaleye
19
14
20-- rel8
15-- rel8
21import {-# SOURCE #-} Rel8.Query ( Query( Query ) )
16import {-# SOURCE #-} Rel8.Query ( Query( Query ) )
22
17
23
18
24fromOpaleye :: Opaleye.Select a -> Query a
19fromOpaleye :: Opaleye.Select a -> Query a
25−fromOpaleye = Query . pure . fmap pure
20+fromOpaleye = Query
26
21
27
22
28toOpaleye :: Query a -> Opaleye.Select a
23toOpaleye :: Query a -> Opaleye.Select a
29−toOpaleye (Query a) = snd <$> a mempty
24+toOpaleye (Query a) = a
30
25
31
26
32mapOpaleye :: (Opaleye.Select a -> Opaleye.Select b) -> Query a -> Query b
27mapOpaleye :: (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
34
29
35
30
36zipOpaleyeWith :: ()
31zipOpaleyeWith :: ()
37 => (Opaleye.Select a -> Opaleye.Select b -> Opaleye.Select c)
32 => (Opaleye.Select a -> Opaleye.Select b -> Opaleye.Select c)
38 -> Query a -> Query b -> Query c
33 -> Query a -> Query b -> Query c
39−zipOpaleyeWith f (Query a) (Query b) = Query $ liftA2 (zipping f) a b
34+zipOpaleyeWith f a b = fromOpaleye $ f (toOpaleye a) (toOpaleye 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
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 @@
1{-# language FlexibleContexts #-}
1{-# language FlexibleContexts #-}
2−{-# language MonoLocalBinds #-}
2+{-# language TypeFamilies #-}
3+{-# language ViewPatterns #-}
3
4
4module Rel8.Query.SQL
5module Rel8.Query.SQL
5 ( showQuery
6 ( showQuery
7+ , sqlForQuery, sqlForQueryWithNames
6 )
8 )
7where
9where
8
10
9-- base
11-- base
12+import Data.Foldable ( fold )
13+import Data.Functor.Const ( Const( Const ), getConst )
14+import Data.Void ( Void )
10import Prelude
15import Prelude
11
16
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+
12-- rel8
25-- rel8
13−import Rel8.Expr ( Expr )
26+import Rel8.Expr ( Expr, Col( E ) )
27+import Rel8.Expr.Opaleye ( toPrimExpr )
14import Rel8.Query ( Query )
28import Rel8.Query ( Query )
15−import Rel8.Statement.Select ( ppSelect )
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 )
16−import Rel8.Table ( Table )
33+import Rel8.Table.Name ( namesFromLabels )
34+import Rel8.Table.Opaleye ( castTable )
17
35
18
36
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.
20showQuery :: Table Expr a => Query a -> String
39showQuery :: 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 )
23
23
24
24
25-- | Combine the results of two queries of the same type, collapsing
25-- | 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@.
27union :: EqTable a => Query a -> Query a -> Query a
27union :: EqTable a => Query a -> Query a -> Query a
28union = zipOpaleyeWith (Opaleye.unionExplicit binaryspec)
28union = zipOpaleyeWith (Opaleye.unionExplicit binaryspec)
29
29
30
30
31-- | Combine the results of two queries of the same type, retaining duplicates.
31-- | 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@.
33unionAll :: Table Expr a => Query a -> Query a -> Query a
33unionAll :: Table Expr a => Query a -> Query a -> Query a
34unionAll = zipOpaleyeWith (Opaleye.unionAllExplicit binaryspec)
34unionAll = zipOpaleyeWith (Opaleye.unionAllExplicit binaryspec)
35
35
36
36
37-- | Find the intersection of two queries, collapsing duplicates. @intersect a
37-- | 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@.
39intersect :: EqTable a => Query a -> Query a -> Query a
39intersect :: EqTable a => Query a -> Query a -> Query a
40intersect = zipOpaleyeWith (Opaleye.intersectExplicit binaryspec)
40intersect = zipOpaleyeWith (Opaleye.intersectExplicit binaryspec)
41
41
42
42
43-- | Find the intersection of two queries, retaining duplicates. @intersectAll
43-- | 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@.
45intersectAll :: EqTable a => Query a -> Query a -> Query a
45intersectAll :: EqTable a => Query a -> Query a -> Query a
46intersectAll = zipOpaleyeWith (Opaleye.intersectAllExplicit binaryspec)
46intersectAll = zipOpaleyeWith (Opaleye.intersectAllExplicit binaryspec)
47
47
48
48
49-- | Find the difference of two queries, collapsing duplicates @except a b@ is
49-- | 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@.
51except :: EqTable a => Query a -> Query a -> Query a
51except :: EqTable a => Query a -> Query a -> Query a
52except = zipOpaleyeWith (Opaleye.exceptExplicit binaryspec)
52except = zipOpaleyeWith (Opaleye.exceptExplicit binaryspec)
53
53
54
54
55-- | Find the difference of two queries, retaining duplicates. @exceptAll a b@
55-- | 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@.
57exceptAll :: EqTable a => Query a -> Query a -> Query a
57exceptAll :: EqTable a => Query a -> Query a -> Query a
58exceptAll = zipOpaleyeWith (Opaleye.exceptAllExplicit binaryspec)
58exceptAll = zipOpaleyeWith (Opaleye.exceptAllExplicit binaryspec)
src/Rel8/Query/These.hs+39−52
@@ -1,5 +1,4 @@
1{-# language FlexibleContexts #-}
1{-# language FlexibleContexts #-}
2−{-# language GADTs #-}
3
2
4module Rel8.Query.These
3module Rel8.Query.These
5 ( alignBy
4 ( alignBy
@@ -15,29 +14,27 @@ where
15-- base
14-- base
16import Prelude
15import Prelude
17
16
18−-- comonad
19−import Control.Comonad ( extract )
20−
21-- opaleye
17-- opaleye
22−import qualified Opaleye.Internal.PackMap as Opaleye
18+import qualified Opaleye.Internal.Join as Opaleye
23import qualified Opaleye.Internal.PrimQuery as Opaleye
19import qualified Opaleye.Internal.PrimQuery as Opaleye
24−import qualified Opaleye.Internal.QueryArr as Opaleye
25−import qualified Opaleye.Internal.Tag as Opaleye
26
20
27-- rel8
21-- rel8
28import Rel8.Expr ( Expr )
22import Rel8.Expr ( Expr )
29import Rel8.Expr.Bool ( boolExpr, not_ )
23import Rel8.Expr.Bool ( boolExpr, not_ )
30import Rel8.Expr.Eq ( (==.) )
24import Rel8.Expr.Eq ( (==.) )
31−import Rel8.Expr.Opaleye ( toPrimExpr, traversePrimExpr )
25+import Rel8.Expr.Opaleye ( toColumn, toPrimExpr )
32import Rel8.Expr.Serialize ( litExpr )
26import Rel8.Expr.Serialize ( litExpr )
33import Rel8.Query ( Query )
27import Rel8.Query ( Query )
34import Rel8.Query.Filter ( where_ )
28import Rel8.Query.Filter ( where_ )
35import Rel8.Query.Maybe ( optional )
29import Rel8.Query.Maybe ( optional )
36import Rel8.Query.Opaleye ( zipOpaleyeWith )
30import Rel8.Query.Opaleye ( zipOpaleyeWith )
31+import Rel8.Table ( Table )
37import Rel8.Table.Either ( EitherTable( EitherTable ) )
32import Rel8.Table.Either ( EitherTable( EitherTable ) )
38import Rel8.Table.Maybe ( MaybeTable( MaybeTable ), isJustTable )
33import Rel8.Table.Maybe ( MaybeTable( MaybeTable ), isJustTable )
34+import Rel8.Table.Opaleye ( unpackspec )
35+import Rel8.Table.Tag ( Tag(..) )
39import Rel8.Table.These
36import Rel8.Table.These
40− ( TheseTable( TheseTable, here, there )
37+ ( TheseTable( TheseTable )
41 , hasHereTable, hasThereTable
38 , hasHereTable, hasThereTable
42 , isThisTable, isThatTable, isThoseTable
39 , isThisTable, isThatTable, isThoseTable
43 )
40 )
@@ -45,91 +42,81 @@ import Rel8.Type.Tag ( EitherTag( IsLeft, IsRight ) )
45
42
46
43
47-- | Corresponds to a @FULL OUTER JOIN@ between two queries.
44-- | Corresponds to a @FULL OUTER JOIN@ between two queries.
48−alignBy :: ()
45+alignBy :: (Table Expr a, Table Expr b)
49 => (a -> b -> Expr Bool)
46 => (a -> b -> Expr Bool)
50− -> Query a -> Query b -> Query (TheseTable Expr a b)
47+ -> Query a -> Query b -> Query (TheseTable a b)
51−alignBy condition = zipOpaleyeWith $ \left right -> Opaleye.stateQueryArr $ \_ t -> case t of
48+alignBy condition as bs =
52− tag -> (tab, join', tag''')
49+ uncurry TheseTable <$> zipOpaleyeWith fullOuterJoin as bs
53− where
50+ where
54− (ma, left', tag') = Opaleye.runStateQueryArr (pure <$> left) () tag
51+ fullOuterJoin a b =
55− (mb, right', tag'') = Opaleye.runStateQueryArr (pure <$> right) () tag'
52+ Opaleye.joinExplicit unpackspec unpackspec pure pure full a b on
56− MaybeTable hasHere a = ma
53+ where
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''
54+ full = Opaleye.FullJoin
64− where
55+ on = toColumn . toPrimExpr . uncurry condition
65− on = toPrimExpr $ condition (extract a) (extract b)
56+
66− left'' = (Opaleye.NonLateral, Opaleye.toPrimQuery (left' <> Opaleye.aRebind lbindings))
57+
67− right'' = (Opaleye.NonLateral, Opaleye.toPrimQuery (right' <> Opaleye.aRebind rbindings))
58+-- | Filter 'TheseTable's, keeping only 'thisTable's and 'thoseTable's.
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)
59+keepHereTable :: TheseTable a b -> Query (a, MaybeTable b)
75keepHereTable = loseThatTable
60keepHereTable = loseThatTable
76
61
77
62
63+-- | Filter 'TheseTable's, keeping on
78−loseHereTable :: TheseTable Expr a b -> Query b
64+loseHereTable :: TheseTable a b -> Query b
79loseHereTable = keepThatTable
65loseHereTable = keepThatTable
80
66
81
67
82−keepThereTable :: TheseTable Expr a b -> Query (MaybeTable Expr a, b)
68+keepThereTable :: TheseTable a b -> Query (MaybeTable a, b)
83keepThereTable = loseThisTable
69keepThereTable = loseThisTable
84
70
85
71
86−loseThereTable :: TheseTable Expr a b -> Query a
72+loseThereTable :: TheseTable a b -> Query a
87loseThereTable = keepThisTable
73loseThereTable = keepThisTable
88
74
89
75
90−keepThisTable :: TheseTable Expr a b -> Query a
76+keepThisTable :: TheseTable a b -> Query a
91keepThisTable t@(TheseTable (MaybeTable _ a) _) = do
77keepThisTable t@(TheseTable (MaybeTable _ a) _) = do
92 where_ $ isThisTable t
78 where_ $ isThisTable t
93− pure (extract a)
79+ pure a
94
80
95
81
96−loseThisTable :: TheseTable Expr a b -> Query (MaybeTable Expr a, b)
82+loseThisTable :: TheseTable a b -> Query (MaybeTable a, b)
97loseThisTable t@(TheseTable ma (MaybeTable _ b)) = do
83loseThisTable t@(TheseTable ma (MaybeTable _ b)) = do
98 where_ $ not_ $ isThisTable t
84 where_ $ not_ $ isThisTable t
99− pure (ma, extract b)
85+ pure (ma, b)
100
86
101
87
102−keepThatTable :: TheseTable Expr a b -> Query b
88+keepThatTable :: TheseTable a b -> Query b
103keepThatTable t@(TheseTable _ (MaybeTable _ b)) = do
89keepThatTable t@(TheseTable _ (MaybeTable _ b)) = do
104 where_ $ isThatTable t
90 where_ $ isThatTable t
105− pure (extract b)
91+ pure b
106
92
107
93
108−loseThatTable :: TheseTable Expr a b -> Query (a, MaybeTable Expr b)
94+loseThatTable :: TheseTable a b -> Query (a, MaybeTable b)
109loseThatTable t@(TheseTable (MaybeTable _ a) mb) = do
95loseThatTable t@(TheseTable (MaybeTable _ a) mb) = do
110 where_ $ not_ $ isThatTable t
96 where_ $ not_ $ isThatTable t
111− pure (extract a, mb)
97+ pure (a, mb)
112
98
113
99
114−keepThoseTable :: TheseTable Expr a b -> Query (a, b)
100+keepThoseTable :: TheseTable a b -> Query (a, b)
115keepThoseTable t@(TheseTable (MaybeTable _ a) (MaybeTable _ b)) = do
101keepThoseTable t@(TheseTable (MaybeTable _ a) (MaybeTable _ b)) = do
116 where_ $ isThoseTable t
102 where_ $ isThoseTable t
117− pure (extract a, extract b)
103+ pure (a, b)
118
104
119
105
120−loseThoseTable :: TheseTable Expr a b -> Query (EitherTable Expr a b)
106+loseThoseTable :: TheseTable a b -> Query (EitherTable a b)
121loseThoseTable t@(TheseTable (MaybeTable _ a) (MaybeTable _ b)) = do
107loseThoseTable t@(TheseTable (MaybeTable _ a) (MaybeTable _ b)) = do
122 where_ $ not_ $ isThoseTable t
108 where_ $ not_ $ isThoseTable t
123− pure $ EitherTable tag a b
109+ pure $ EitherTable result a b
124 where
110 where
125 tag = boolExpr (litExpr IsLeft) (litExpr IsRight) (isThatTable t)
111 tag = boolExpr (litExpr IsLeft) (litExpr IsRight) (isThatTable t)
112+ result = (mempty `asTypeOf` result) {expr = tag}
126
113
127
114
128bitraverseTheseTable :: ()
115bitraverseTheseTable :: ()
129 => (a -> Query c)
116 => (a -> Query c)
130 -> (b -> Query d)
117 -> (b -> Query d)
131− -> TheseTable Expr a b
118+ -> TheseTable a b
132− -> Query (TheseTable Expr c d)
119+ -> Query (TheseTable c d)
133bitraverseTheseTable f g t = do
120bitraverseTheseTable f g t = do
134 mc <- optional (f . fst =<< keepHereTable t)
121 mc <- optional (f . fst =<< keepHereTable t)
135 md <- optional (g . snd =<< keepThereTable t)
122 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 @@
1{-# language DataKinds #-}
1{-# language DataKinds #-}
2−{-# language EmptyCase #-}
3{-# language FlexibleContexts #-}
2{-# language FlexibleContexts #-}
4−{-# language GADTs #-}
3+{-# language FlexibleInstances #-}
5−{-# language LambdaCase #-}
4+{-# language InstanceSigs #-}
5+{-# language MultiParamTypeClasses #-}
6{-# language NamedFieldPuns #-}
6{-# language NamedFieldPuns #-}
7+{-# language ScopedTypeVariables #-}
7{-# language StandaloneKindSignatures #-}
8{-# language StandaloneKindSignatures #-}
9+{-# language TypeFamilies #-}
8
10
9module Rel8.Schema.Context.Nullify
11module Rel8.Schema.Context.Nullify
10− ( Nullifiability(..), NonNullifiability(..), nullifiableOrNot, absurd
11− , Nullifiable, nullifiability
12− , guarder, nullifier, unnullifier
12+ ( Nullifiable( ConstrainTag, encodeTag, decodeTag, nullifier, unnullifier )
13− , sguard, snullify
13+ , HNullifiable( HConstrainTag, hencodeTag, hdecodeTag, hnullifier, hunnullifier )
14+ , runTag, unnull
14 )
15 )
15where
16where
16
17
17-- base
18-- base
18−import Data.Bool ( bool )
19−import Data.Functor.Identity ( Identity( Identity ) )
20import Data.Kind ( Constraint, Type )
19import Data.Kind ( Constraint, Type )
20+import GHC.TypeLits ( KnownSymbol )
21import Prelude hiding ( null )
21import Prelude hiding ( null )
22
22
23-- opaleye
23-- opaleye
24import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
24import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
25
25
26-- rel8
26-- rel8
27−import Rel8.Aggregate ( Aggregate(..), zipOutputs )
27+import Rel8.Aggregate
28+ ( Aggregate( Aggregate ), Col( A )
29+ , mapInputs
30+ , unsafeMakeAggregate
31+ )
28−import Rel8.Expr ( Expr )
32+import Rel8.Expr ( Expr, Col( E ) )
29import Rel8.Expr.Bool ( boolExpr )
33import Rel8.Expr.Bool ( boolExpr )
30import Rel8.Expr.Null ( nullify, unsafeUnnullify )
34import Rel8.Expr.Null ( nullify, unsafeUnnullify )
31−import Rel8.Expr.Opaleye ( fromPrimExpr )
35+import Rel8.Expr.Opaleye ( fromPrimExpr, toPrimExpr )
32−import Rel8.Kind.Context ( SContext(..) )
36+import Rel8.Schema.Context ( Interpretation )
33−import Rel8.Schema.Field ( Field )
34import qualified Rel8.Schema.Kind as K
37import qualified Rel8.Schema.Kind as K
35−import Rel8.Schema.Name ( Name( Name ) )
38+import Rel8.Schema.Name ( Name( Name ), Col( N ) )
36−import Rel8.Schema.Null ( Nullify, Nullity( Null, NotNull ) )
39+import Rel8.Schema.Null ( Nullify, Nullity( Null, NotNull ), Sql )
37−import Rel8.Schema.Result ( Result )
40+import Rel8.Schema.Dict ( Dict( Dict ) )
38−import Rel8.Schema.Spec ( Spec(..) )
41+import Rel8.Schema.Spec ( Spec( Spec ), SSpec(..) )
39−
42+import Rel8.Schema.Spec.ConstrainDBType ( ConstrainDBType )
40−
43+import qualified Rel8.Schema.Spec.ConstrainDBType as ConstrainDBType
41−type Nullifiability :: K.Context -> Type
44+import Rel8.Table.Tag ( Tag(..), Taggable, fromAggregate, fromExpr, fromName )
42−data Nullifiability context where
43− NAggregate :: Nullifiability Aggregate
44− NExpr :: Nullifiability Expr
45− NName :: Nullifiability Name
46
45
47
46
48type Nullifiable :: K.Context -> Constraint
47type Nullifiable :: K.Context -> Constraint
49−class Nullifiable context where
48+class Interpretation context => Nullifiable context where
50− nullifiability :: Nullifiability context
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)
51
79
52
80
53instance Nullifiable Aggregate where
81instance 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 #-}
55
102
56
103
57instance Nullifiable Expr where
104instance 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 #-}
59
115
60
116
61instance Nullifiable Name where
117instance Nullifiable Name where
62− nullifiability = NName
118+ encodeTag Tag {name} = N name
63−
119+ decodeTag (N name) = fromName name
64−
120+ nullifier _ _ _ (N (Name name)) = N (Name name)
65−type NonNullifiability :: K.Context -> Type
121+ unnullifier _ (N (Name name)) = N (Name name)
66−data NonNullifiability context where
122+
67− NField :: NonNullifiability (Field table)
123+ {-# INLINABLE encodeTag #-}
68− NResult :: NonNullifiability Result
124+ {-# INLINABLE decodeTag #-}
69−
125+ {-# INLINABLE nullifier #-}
70−
126+ {-# INLINABLE unnullifier #-}
71−nullifiableOrNot :: ()
127+
72− => SContext context
128+
73− -> Either (NonNullifiability context) (Nullifiability context)
129+runTag :: Nullity a -> Expr Bool -> Expr a -> Expr (Nullify a)
74−nullifiableOrNot = \case
130+runTag nullity tag a = case nullity of
75− SAggregate -> Right NAggregate
131+ Null -> boolExpr null a tag
76− SExpr -> Right NExpr
132+ NotNull -> boolExpr null (nullify a) tag
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
131 where
133 where
132 null = fromPrimExpr $ Opaleye.ConstExpr Opaleye.NullLit
134 null = fromPrimExpr $ Opaleye.ConstExpr Opaleye.NullLit
133
135
134
136
135−snullify :: Nullity a -> Expr a -> Expr (Nullify a)
137+unnull :: Nullity a -> Expr (Nullify a) -> Expr a
136−snullify nullity a = case nullity of
138+unnull nullity a = case nullity of
137 Null -> a
139 Null -> a
138− NotNull -> nullify a
140+ NotNull -> unsafeUnnullify a
139
141
140
142
141−sunnullify :: Nullity a -> Expr (Nullify a) -> Expr a
143+type HNullifiable :: K.HContext -> Constraint
142−sunnullify nullity a = case nullity of
144+class HNullifiable context where
143− Null -> a
145+ type HConstrainTag context :: Type -> Constraint
144− NotNull -> unsafeUnnullify a
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 @@
16module Rel8.Schema.HTable
16module Rel8.Schema.HTable
17 ( HTable (HField, HConstrainTable)
17 ( HTable (HField, HConstrainTable)
18 , hfield, htabulate, htraverse, hdicts, hspecs
18 , hfield, htabulate, htraverse, hdicts, hspecs
19− , hfoldMap, hmap, htabulateA, htraverseP, htraversePWithField
19+ , hmap, htabulateA
20 )
20 )
21where
21where
22
22
23-- base
23-- base
24−import Data.Functor.Const ( Const( Const ), getConst )
25import Data.Kind ( Constraint, Type )
24import Data.Kind ( Constraint, Type )
26import Data.Functor.Compose ( Compose( Compose ), getCompose )
25import Data.Functor.Compose ( Compose( Compose ), getCompose )
27import Data.Proxy ( Proxy )
26import Data.Proxy ( Proxy )
@@ -33,21 +32,16 @@ import GHC.Generics
33 )
32 )
34import Prelude
33import Prelude
35
34
36−-- profunctors
37−import Data.Profunctor ( rmap, Profunctor (lmap) )
38−
39−-- product-profunctors
40−import Data.Profunctor.Product ( ProductProfunctor ((****)) )
41−
42-- rel8
35-- rel8
43import Rel8.Schema.Dict ( Dict )
36import Rel8.Schema.Dict ( Dict )
44−import Rel8.Schema.Spec ( Spec )
37+import Rel8.Schema.Spec ( Spec, SSpec )
45import Rel8.Schema.HTable.Product ( HProduct( HProduct ) )
38import Rel8.Schema.HTable.Product ( HProduct( HProduct ) )
46import qualified Rel8.Schema.Kind as K
39import qualified Rel8.Schema.Kind as K
47
40
48-- semigroupoids
41-- semigroupoids
49import Data.Functor.Apply ( Apply, (<.>) )
42import Data.Functor.Apply ( Apply, (<.>) )
50
43
44+
51-- | A @HTable@ is a functor-indexed/higher-kinded data type that is
45-- | A @HTable@ is a functor-indexed/higher-kinded data type that is
52-- representable ('htabulate'/'hfield'), constrainable ('hdicts'), and
46-- representable ('htabulate'/'hfield'), constrainable ('hdicts'), and
53-- specified ('hspecs').
47-- specified ('hspecs').
@@ -56,14 +50,14 @@ import Data.Functor.Apply ( Apply, (<.>) )
56-- instances yourself or specify this constraint.
50-- instances yourself or specify this constraint.
57type HTable :: K.HTable -> Constraint
51type HTable :: K.HTable -> Constraint
58class HTable t where
52class HTable t where
59− type HField t = (field :: Type -> Type) | field -> t
53+ type HField t = (field :: Spec -> Type) | field -> t
60− type HConstrainTable t (c :: Type -> Constraint) :: Constraint
54+ type HConstrainTable t (c :: Spec -> Constraint) :: Constraint
61
55
62− hfield :: t context -> HField t a -> context a
56+ hfield :: t context -> HField t spec -> context spec
63− htabulate :: (forall a. HField t a -> context a) -> t context
57+ htabulate :: (forall spec. HField t spec -> context spec) -> t context
64− htraverse :: Apply m => (forall a. f a -> m (g a)) -> t f -> m (t g)
58+ htraverse :: Apply m => (forall spec. f spec -> m (g spec)) -> t f -> m (t g)
65 hdicts :: HConstrainTable t c => t (Dict c)
59 hdicts :: HConstrainTable t c => t (Dict c)
66− hspecs :: t Spec
60+ hspecs :: t SSpec
67
61
68 type HField t = GHField t
62 type HField t = GHField t
69 type HConstrainTable t c = HConstrainTable (GHColumns (Rep (t Proxy))) c
63 type HConstrainTable t c = HConstrainTable (GHColumns (Rep (t Proxy))) c
@@ -74,7 +68,7 @@ class HTable t where
74 , HField (GHColumns (Rep (t Proxy))) ~ HField (GHColumns (Rep (t context)))
68 , HField (GHColumns (Rep (t Proxy))) ~ HField (GHColumns (Rep (t context)))
75 , GHTable context (Rep (t context))
69 , GHTable context (Rep (t context))
76 )
70 )
77− => t context -> HField t a -> context a
71+ => t context -> HField t spec -> context spec
78 hfield table (GHField field) = hfield (toGHColumns (from table)) field
72 hfield table (GHField field) = hfield (toGHColumns (from table)) field
79
73
80 default htabulate ::
74 default htabulate ::
@@ -83,7 +77,7 @@ class HTable t where
83 , HField (GHColumns (Rep (t Proxy))) ~ HField (GHColumns (Rep (t context)))
77 , HField (GHColumns (Rep (t Proxy))) ~ HField (GHColumns (Rep (t context)))
84 , GHTable context (Rep (t context))
78 , GHTable context (Rep (t context))
85 )
79 )
86− => (forall a. HField t a -> context a) -> t context
80+ => (forall spec. HField t spec -> context spec) -> t context
87 htabulate f = to $ fromGHColumns $ htabulate (f . GHField)
81 htabulate f = to $ fromGHColumns $ htabulate (f . GHField)
88
82
89 default htraverse
83 default htraverse
@@ -93,7 +87,7 @@ class HTable t where
93 , Generic (t g), GHTable g (Rep (t g))
87 , Generic (t g), GHTable g (Rep (t g))
94 , GHColumns (Rep (t f)) ~ GHColumns (Rep (t g))
88 , GHColumns (Rep (t f)) ~ GHColumns (Rep (t g))
95 )
89 )
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)
97 htraverse f = fmap (to . fromGHColumns) . htraverse f . toGHColumns . from
91 htraverse f = fmap (to . fromGHColumns) . htraverse f . toGHColumns . from
98
92
99 default hdicts
93 default hdicts
@@ -107,10 +101,10 @@ class HTable t where
107 hdicts = to $ fromGHColumns (hdicts @(GHColumns (Rep (t Proxy))) @c)
101 hdicts = to $ fromGHColumns (hdicts @(GHColumns (Rep (t Proxy))) @c)
108
102
109 default hspecs ::
103 default hspecs ::
110− ( Generic (t Spec)
104+ ( Generic (t SSpec)
111− , GHTable Spec (Rep (t Spec))
105+ , GHTable SSpec (Rep (t SSpec))
112 )
106 )
113− => t Spec
107+ => t SSpec
114 hspecs = to $ fromGHColumns hspecs
108 hspecs = to $ fromGHColumns hspecs
115
109
116 {-# INLINABLE hfield #-}
110 {-# INLINABLE hfield #-}
@@ -120,43 +114,22 @@ class HTable t where
120 {-# INLINABLE hspecs #-}
114 {-# INLINABLE hspecs #-}
121
115
122
116
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−
128hmap :: HTable t
117hmap :: HTable t
129− => (forall a. context a -> context' a) -> t context -> t context'
118+ => (forall spec. context spec -> context' spec) -> t context -> t context'
130hmap f a = htabulate $ \field -> f (hfield a field)
119hmap f a = htabulate $ \field -> f (hfield a field)
131
120
132
121
133htabulateA :: (HTable t, Apply m)
122htabulateA :: (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)
135htabulateA f = htraverse getCompose $ htabulate $ Compose . f
124htabulateA f = htraverse getCompose $ htabulate $ Compose . f
136{-# INLINABLE htabulateA #-}
125{-# INLINABLE htabulateA #-}
137
126
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)
154
127
155−type GHField :: K.HTable -> Type -> Type
128+type GHField :: K.HTable -> Spec -> Type
156−newtype GHField t a = GHField (HField (GHColumns (Rep (t Proxy))) a)
129+newtype GHField t spec = GHField (HField (GHColumns (Rep (t Proxy))) spec)
157
130
158
131
159−type GHTable :: K.Context -> (Type -> Type) -> Constraint
132+type GHTable :: K.HContext -> (Type -> Type) -> Constraint
160class HTable (GHColumns rep) => GHTable context rep | rep -> context where
133class HTable (GHColumns rep) => GHTable context rep | rep -> context where
161 type GHColumns rep :: K.HTable
134 type GHColumns rep :: K.HTable
162 toGHColumns :: rep x -> GHColumns rep context
135 toGHColumns :: rep x -> GHColumns rep context
@@ -182,10 +155,10 @@ instance (GHTable context a, GHTable context b) => GHTable context (a :*: b) whe
182
155
183
156
184-- | A HField type for indexing into HProduct.
157-- | A HField type for indexing into HProduct.
185−type HProductField :: K.HTable -> K.HTable -> Type -> Type
158+type HProductField :: K.HTable -> K.HTable -> Spec -> Type
186−data HProductField x y a
159+data HProductField x y spec
187− = HFst (HField x a)
160+ = HFst (HField x spec)
188− | HSnd (HField y a)
161+ | HSnd (HField y spec)
189
162
190
163
191instance (HTable x, HTable y) => HTable (HProduct x y) where
164instance (HTable x, HTable y) => HTable (HProduct x y) where
src/Rel8/Schema/HTable/Either.hs+3−2
@@ -15,16 +15,17 @@ import Prelude ()
15
15
16-- rel8
16-- rel8
17import Rel8.Schema.HTable ( HTable )
17import Rel8.Schema.HTable ( HTable )
18−import Rel8.Schema.HTable.Identity ( HIdentity )
18+import Rel8.Schema.HTable.Identity ( HIdentity(..) )
19import Rel8.Schema.HTable.Label ( HLabel )
19import Rel8.Schema.HTable.Label ( HLabel )
20import Rel8.Schema.HTable.Nullify ( HNullify )
20import Rel8.Schema.HTable.Nullify ( HNullify )
21import qualified Rel8.Schema.Kind as K
21import qualified Rel8.Schema.Kind as K
22+import Rel8.Schema.Spec ( Spec( Spec ) )
22import Rel8.Type.Tag ( EitherTag )
23import Rel8.Type.Tag ( EitherTag )
23
24
24
25
25type HEitherTable :: K.HTable -> K.HTable -> K.HTable
26type HEitherTable :: K.HTable -> K.HTable -> K.HTable
26data HEitherTable left right context = HEitherTable
27data HEitherTable left right context = HEitherTable
27− { htag :: HLabel "isRight" (HIdentity EitherTag) context
28+ { htag :: HIdentity ('Spec '["isRight"] EitherTag) context
28 , hleft :: HLabel "Left" (HNullify left) context
29 , hleft :: HLabel "Left" (HNullify left) context
29 , hright :: HLabel "Right" (HNullify right) context
30 , hright :: HLabel "Right" (HNullify right) context
30 }
31 }
src/Rel8/Schema/HTable/Identity.hs+28−16
@@ -1,17 +1,17 @@
1{-# language DataKinds #-}
1{-# language DataKinds #-}
2−{-# language FlexibleContexts #-}
2+{-# language GADTs #-}
3+{-# language PatternSynonyms #-}
3{-# language StandaloneKindSignatures #-}
4{-# language StandaloneKindSignatures #-}
4{-# language TypeFamilies #-}
5{-# language TypeFamilies #-}
5−{-# language UndecidableInstances #-}
6
6
7module Rel8.Schema.HTable.Identity
7module Rel8.Schema.HTable.Identity
8− ( HIdentity( HIdentity, unHIdentity )
8+ ( HIdentity( HIdentity, HType, unHIdentity )
9+ , HType
9 )
10 )
10where
11where
11
12
12-- base
13-- base
13import Data.Kind ( Type )
14import Data.Kind ( Type )
14−import Data.Type.Equality ( (:~:)( Refl ) )
15import Prelude
15import Prelude
16
16
17-- rel8
17-- rel8
@@ -21,23 +21,35 @@ import Rel8.Schema.HTable
21 , hfield, htabulate, htraverse, hdicts, hspecs
21 , hfield, htabulate, htraverse, hdicts, hspecs
22 )
22 )
23import qualified Rel8.Schema.Kind as K
23import qualified Rel8.Schema.Kind as K
24−import Rel8.Schema.Null ( Sql )
24+import Rel8.Schema.Spec ( Spec( Spec ), KnownSpec, specSing )
25−import Rel8.Schema.Spec ( specification )
26−import Rel8.Type ( DBType )
27
25
28
26
29−type HIdentity :: Type -> K.HTable
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
30−newtype HIdentity a context = HIdentity
37+newtype HIdentity spec context = HIdentity
31− { unHIdentity :: context a
38+ { unHIdentity :: context spec
32 }
39 }
33
40
34
41
42+type HIdentityField :: Spec -> Spec -> Type
43+data HIdentityField _spec spec where
44+ HIdentityField :: HIdentityField spec spec
45+
46+
35−instance Sql DBType a => HTable (HIdentity a) where
47+instance KnownSpec spec => HTable (HIdentity spec) where
36− type HConstrainTable (HIdentity a) constraint = constraint a
48+ type HConstrainTable (HIdentity spec) c = c spec
37− type HField (HIdentity a) = (:~:) a
49+ type HField (HIdentity spec) = HIdentityField spec
38
50
39− hfield (HIdentity a) Refl = a
51+ hfield (HIdentity a) HIdentityField = a
40− htabulate f = HIdentity $ f Refl
52+ htabulate f = HIdentity $ f HIdentityField
41 htraverse f (HIdentity a) = HIdentity <$> f a
53 htraverse f (HIdentity a) = HIdentity <$> f a
42 hdicts = HIdentity Dict
54 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 #-}
1{-# language DataKinds #-}
6{-# language DataKinds #-}
7+{-# language FlexibleInstances #-}
8+{-# language GADTs #-}
9+{-# language MultiParamTypeClasses #-}
10+{-# language QuantifiedConstraints #-}
2{-# language RankNTypes #-}
11{-# language RankNTypes #-}
3{-# language RecordWildCards #-}
12{-# language RecordWildCards #-}
4{-# language ScopedTypeVariables #-}
13{-# language ScopedTypeVariables #-}
5{-# language StandaloneKindSignatures #-}
14{-# language StandaloneKindSignatures #-}
6−{-# language TypeApplications #-}
7{-# language TypeFamilies #-}
15{-# language TypeFamilies #-}
16+{-# language TypeOperators #-}
17+{-# language UndecidableInstances #-}
8
18
9module Rel8.Schema.HTable.Label
19module Rel8.Schema.HTable.Label
20+ ( HLabel, Label
10− ( HLabel, hlabel, hrelabel, hunlabel
21+ , hlabel, hunlabel
11− , hproject
12 )
22 )
13where
23where
14
24
15-- base
25-- base
16−import Data.Kind ( Type )
17import Data.Proxy ( Proxy( Proxy ) )
26import Data.Proxy ( Proxy( Proxy ) )
18−import GHC.TypeLits ( KnownSymbol, Symbol, symbolVal )
27+import GHC.TypeLits ( KnownSymbol, Symbol )
19import Prelude
28import Prelude
20
29
21-- rel8
30-- rel8
31+import Rel8.Kind.Labels ( SLabels( SCons ) )
22import Rel8.Schema.HTable
32import Rel8.Schema.HTable
23− ( HTable, HConstrainTable, HField
33+ ( HTable
24− , htabulate, hfield, htraverse, hdicts, hspecs
34+ , hfield, htabulate, hspecs
25 )
35 )
26import qualified Rel8.Schema.Kind as K
36import 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)
28
41
29
42
30type HLabel :: Symbol -> K.HTable -> K.HTable
43type 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
32
47
33
48
34−type HLabelField :: Symbol -> K.HTable -> Type -> Type
49+data Label :: Symbol -> Spec -> Exp Spec
35−newtype HLabelField label table a = HLabelField (HField table a)
36
50
37
51
38−instance (HTable table, KnownSymbol label) => HTable (HLabel label table) where
52+type instance Eval (Label label ('Spec labels a)) = 'Spec (label : labels) a
39− type HField (HLabel label table) = HLabelField label table
40− type HConstrainTable (HLabel label table) constraint =
41− HConstrainTable table constraint
42
53
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 #-}
50
54
51−
55+instance KnownSymbol l => MapSpec (Label l) where
52−hlabel :: forall label t context. t context -> HLabel label t context
56+ mapInfo = \case
53−hlabel = HLabel
57+ SSpec {..} -> SSpec {labels = SCons Proxy labels, ..}
54−{-# INLINABLE hlabel #-}
55
58
56
59
60+hlabel :: (HTable t, KnownSymbol label)
61+ => (forall labels a. ()
62+ => context ('Spec labels a)
63+ -> context ('Spec (label ': labels) a))
64+ -> t context
57−hrelabel :: forall label' label t context. HLabel label t context -> HLabel label' t context
65+ -> HLabel label t context
58−hrelabel = hlabel . hunlabel
66+hlabel labeler a = HLabel $ htabulate $ \(HMapTableField field) ->
67+ case hfield hspecs field of
68+ SSpec {} -> labeler (hfield a field)
59−{-# INLINABLE hrelabel #-}
69+{-# INLINABLE hlabel #-}
60
70
61
71
72+hunlabel :: (HTable t, KnownSymbol label)
73+ => (forall labels a. ()
74+ => context ('Spec (label ': labels) a)
75+ -> context ('Spec labels a))
62−hunlabel :: forall label t context. HLabel label t context -> t context
76+ -> HLabel label t context
77+ -> t context
63−hunlabel (HLabel a) = a
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
64{-# INLINABLE hunlabel #-}
83{-# 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 @@
6{-# language GADTs #-}
6{-# language GADTs #-}
7{-# language InstanceSigs #-}
7{-# language InstanceSigs #-}
8{-# language MultiParamTypeClasses #-}
8{-# language MultiParamTypeClasses #-}
9−{-# language RankNTypes #-}
9+{-# language PolyKinds #-}
10{-# language ScopedTypeVariables #-}
10{-# language ScopedTypeVariables #-}
11{-# language StandaloneKindSignatures #-}
11{-# language StandaloneKindSignatures #-}
12{-# language TypeApplications #-}
12{-# language TypeApplications #-}
@@ -19,38 +19,34 @@ module Rel8.Schema.HTable.MapTable
19 , MapSpec(..)
19 , MapSpec(..)
20 , Precompose(..)
20 , Precompose(..)
21 , HMapTableField(..)
21 , HMapTableField(..)
22− , hproject
23 )
22 )
24where
23where
25
24
26-- base
25-- base
27import Data.Kind ( Constraint, Type )
26import Data.Kind ( Constraint, Type )
28−import Prelude
27+import Prelude ( ($), (.), (<$>), fmap )
29
28
30-- rel8
29-- rel8
31−import Rel8.FCF ( Exp, Eval )
30+import Rel8.FCF
32import Rel8.Schema.HTable
31import 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 )
37import qualified Rel8.Schema.Kind as K
33import qualified Rel8.Schema.Kind as K
38import Rel8.Schema.Dict ( Dict( Dict ) )
34import Rel8.Schema.Dict ( Dict( Dict ) )
39
35
40
36
41−type HMapTable :: (Type -> Exp Type) -> K.HTable -> K.HTable
37+type HMapTable :: (a -> Exp b) -> ((a -> Type) -> Type) -> (b -> Type) -> Type
42−newtype HMapTable f t context = HMapTable
38+newtype HMapTable f t g = HMapTable
43− { unHMapTable :: t (Precompose f context)
39+ { unHMapTable :: t (Precompose f g)
44 }
40 }
45
41
46
42
47−type Precompose :: (Type -> Exp Type) -> K.Context -> K.Context
43+type Precompose :: (a -> Exp b) -> (b -> Type) -> a -> Type
48newtype Precompose f g x = Precompose
44newtype Precompose f g x = Precompose
49 { precomposed :: g (Eval (f x))
45 { precomposed :: g (Eval (f x))
50 }
46 }
51
47
52
48
53−type HMapTableField :: (Type -> Exp Type) -> K.HTable -> K.Context
49+type HMapTableField :: (Spec -> Exp a) -> K.HTable -> a -> Type
54data HMapTableField f t x where
50data HMapTableField f t x where
55 HMapTableField :: HField t a -> HMapTableField f t (Eval (f a))
51 HMapTableField :: HField t a -> HMapTableField f t (Eval (f a))
56
52
@@ -83,17 +79,11 @@ instance (HTable t, MapSpec f) => HTable (HMapTable f t) where
83 {-# INLINABLE hspecs #-}
79 {-# INLINABLE hspecs #-}
84
80
85
81
86−type MapSpec :: (Type -> Exp Type) -> Constraint
82+type MapSpec :: (Spec -> Exp Spec) -> Constraint
87class MapSpec f where
83class MapSpec f where
88− mapInfo :: Spec x -> Spec (Eval (f x))
84+ mapInfo :: SSpec x -> SSpec (Eval (f x))
89
85
90
86
91−type ComposeConstraint :: (Type -> Exp Type) -> (Type -> Constraint) -> Type -> Constraint
87+type ComposeConstraint :: (a -> Exp b) -> (b -> Constraint) -> a -> Constraint
92class c (Eval (f a)) => ComposeConstraint f c a
88class c (Eval (f a)) => ComposeConstraint f c a
93instance c (Eval (f a)) => ComposeConstraint f c a
89instance 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
15
15
16-- rel8
16-- rel8
17import Rel8.Schema.HTable ( HTable )
17import Rel8.Schema.HTable ( HTable )
18−import Rel8.Schema.HTable.Identity ( HIdentity )
18+import Rel8.Schema.HTable.Identity ( HIdentity(..) )
19import Rel8.Schema.HTable.Label ( HLabel )
19import Rel8.Schema.HTable.Label ( HLabel )
20import Rel8.Schema.HTable.Nullify ( HNullify )
20import Rel8.Schema.HTable.Nullify ( HNullify )
21import qualified Rel8.Schema.Kind as K
21import qualified Rel8.Schema.Kind as K
22+import Rel8.Schema.Spec ( Spec( Spec ) )
22import Rel8.Type.Tag ( MaybeTag )
23import Rel8.Type.Tag ( MaybeTag )
23
24
24
25
25type HMaybeTable :: K.HTable -> K.HTable
26type HMaybeTable :: K.HTable -> K.HTable
26data HMaybeTable table context = HMaybeTable
27data HMaybeTable table context = HMaybeTable
27− { htag :: HLabel "isJust" (HIdentity (Maybe MaybeTag)) context
28+ { htag :: HIdentity ('Spec '["isJust"] (Maybe MaybeTag)) context
28 , hjust :: HLabel "Just" (HNullify table) context
29 , hjust :: HLabel "Just" (HNullify table) context
29 }
30 }
30 deriving stock Generic
31 deriving stock Generic
src/Rel8/Schema/HTable/Nullify.hs+32−47
@@ -10,7 +10,6 @@
10{-# language NamedFieldPuns #-}
10{-# language NamedFieldPuns #-}
11{-# language QuantifiedConstraints #-}
11{-# language QuantifiedConstraints #-}
12{-# language RankNTypes #-}
12{-# language RankNTypes #-}
13−{-# language RecordWildCards #-}
14{-# language ScopedTypeVariables #-}
13{-# language ScopedTypeVariables #-}
15{-# language StandaloneKindSignatures #-}
14{-# language StandaloneKindSignatures #-}
16{-# language TypeFamilies #-}
15{-# language TypeFamilies #-}
@@ -19,34 +18,25 @@
19module Rel8.Schema.HTable.Nullify
18module Rel8.Schema.HTable.Nullify
20 ( HNullify( HNullify )
19 ( HNullify( HNullify )
21 , Nullify
20 , Nullify
22− , hguard
23− , hnulls
24− , hnullify
25− , hunnullify
21+ , hnulls, hnullify, hunnullify
26− , hproject
27 )
22 )
28where
23where
29
24
30-- base
25-- base
31−import Data.Kind ( Type )
32−import GHC.Generics ( Generic )
33import Prelude hiding ( null )
26import Prelude hiding ( null )
34
27
35-- rel8
28-- rel8
36−import Rel8.FCF ( Eval, Exp )
37import Rel8.Schema.HTable ( HTable, hfield, htabulate, htabulateA, hspecs )
29import 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 )
43import qualified Rel8.Schema.Kind as K
30import qualified Rel8.Schema.Kind as K
44import Rel8.Schema.Null ( Nullity( Null, NotNull ) )
31import Rel8.Schema.Null ( Nullity( Null, NotNull ) )
45import qualified Rel8.Schema.Null as Type ( Nullify )
32import qualified Rel8.Schema.Null as Type ( Nullify )
46−import Rel8.Schema.Spec ( Spec(..) )
33+import Rel8.Schema.Spec ( Spec( Spec ), SSpec(..) )
47
34
48-- semigroupoids
35-- semigroupoids
49import Data.Functor.Apply ( Apply )
36import Data.Functor.Apply ( Apply )
37+import Rel8.Schema.HTable.MapTable
38+import Rel8.FCF
39+import GHC.Generics (Generic)
50
40
51
41
52type HNullify :: K.HTable -> K.HTable
42type HNullify :: K.HTable -> K.HTable
@@ -55,63 +45,58 @@ newtype HNullify table context = HNullify (HMapTable Nullify table context)
55 deriving anyclass HTable
45 deriving anyclass HTable
56
46
57
47
48+
58−-- | Transform a 'Type' by allowing it to be @null@.
49+-- | Transform a 'Spec' by allowing it to be @null@.
59−data Nullify :: Type -> Exp Type
50+data Nullify :: Spec -> Exp Spec
51+
52+
60−type instance Eval (Nullify a) = Type.Nullify a
53+type instance Eval (Nullify ('Spec labels a)) =
54+ 'Spec labels (Type.Nullify a)
61
55
62
56
63instance MapSpec Nullify where
57instance MapSpec Nullify where
64 mapInfo = \case
58 mapInfo = \case
65− Spec {nullity, ..} -> Spec
59+ SSpec{labels, info, nullity} -> SSpec
60+ { labels
61+ , info
66− { nullity = case nullity of
62+ , nullity = case nullity of
67 Null -> Null
63 Null -> Null
68 NotNull -> Null
64 NotNull -> Null
69− , ..
65+ }
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
82
66
83
67
84hnulls :: HTable t
68hnulls :: HTable t
69+ => (forall labels a. ()
70+ => SSpec ('Spec labels a)
85− => (forall a. Spec a -> context (Type.Nullify a))
71+ -> context ('Spec labels (Type.Nullify a)))
86 -> HNullify t context
72 -> HNullify t context
87−hnulls null = HNullify $ htabulate $ \(HMapTableField field) ->
73+hnulls null = HNullify $ htabulate $ \(HMapTableField field) -> case hfield hspecs field of
88− case hfield hspecs field of
89− spec@Spec {} -> null spec
74+ spec@SSpec {} -> null spec
90{-# INLINABLE hnulls #-}
75{-# INLINABLE hnulls #-}
91
76
92
77
93hnullify :: HTable t
78hnullify :: HTable t
79+ => (forall labels a. ()
80+ => SSpec ('Spec labels a)
81+ -> context ('Spec labels a)
94− => (forall a. Spec a -> context a -> context (Type.Nullify a))
82+ -> context ('Spec labels (Type.Nullify a)))
95 -> t context
83 -> t context
96 -> HNullify t context
84 -> HNullify t context
97hnullify nullifier a = HNullify $ htabulate $ \(HMapTableField field) ->
85hnullify nullifier a = HNullify $ htabulate $ \(HMapTableField field) ->
98 case hfield hspecs field of
86 case hfield hspecs field of
99− spec@Spec {} -> nullifier spec (hfield a field)
87+ spec@SSpec {} -> nullifier spec (hfield a field)
100{-# INLINABLE hnullify #-}
88{-# INLINABLE hnullify #-}
101
89
102
90
103hunnullify :: (HTable t, Apply m)
91hunnullify :: (HTable t, Apply m)
92+ => (forall labels a. ()
93+ => SSpec ('Spec labels a)
104− => (forall a. Spec a -> context (Type.Nullify a) -> m (context a))
94+ -> context ('Spec labels (Type.Nullify a))
95+ -> m (context ('Spec labels a)))
105 -> HNullify t context
96 -> HNullify t context
106 -> m (t context)
97 -> m (t context)
107hunnullify unnullifier (HNullify as) =
98hunnullify unnullifier (HNullify as) =
108 htabulateA $ \field -> case hfield hspecs field of
99 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
110 a -> unnullifier spec a
101 a -> unnullifier spec a
111{-# INLINABLE hunnullify #-}
102{-# 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 )
19import Rel8.Schema.HTable.Label ( HLabel )
19import Rel8.Schema.HTable.Label ( HLabel )
20import Rel8.Schema.HTable.Nullify ( HNullify )
20import Rel8.Schema.HTable.Nullify ( HNullify )
21import qualified Rel8.Schema.Kind as K
21import qualified Rel8.Schema.Kind as K
22+import Rel8.Schema.Spec ( Spec( Spec ) )
22import Rel8.Type.Tag ( MaybeTag )
23import Rel8.Type.Tag ( MaybeTag )
23
24
24
25
25type HTheseTable :: K.HTable -> K.HTable -> K.HTable
26type HTheseTable :: K.HTable -> K.HTable -> K.HTable
26data HTheseTable here there context = HTheseTable
27data HTheseTable here there context = HTheseTable
27− { hhereTag :: HLabel "hereTag" (HIdentity (Maybe MaybeTag)) context
28+ { hhereTag :: HIdentity ('Spec '["hasHere"] (Maybe MaybeTag)) context
28 , hhere :: HLabel "Here" (HNullify here) context
29 , hhere :: HLabel "Here" (HNullify here) context
29− , hthereTag :: HLabel "thereTag" (HIdentity (Maybe MaybeTag)) context
30+ , hthereTag :: HIdentity ('Spec '["hasThere"] (Maybe MaybeTag)) context
30 , hthere :: HLabel "There" (HNullify there) context
31 , hthere :: HLabel "There" (HNullify there) context
31 }
32 }
32 deriving stock Generic
33 deriving stock Generic
src/Rel8/Schema/HTable/Vectorize.hs+45−37
@@ -9,6 +9,8 @@
9{-# language GADTs #-}
9{-# language GADTs #-}
10{-# language LambdaCase #-}
10{-# language LambdaCase #-}
11{-# language MultiParamTypeClasses #-}
11{-# language MultiParamTypeClasses #-}
12+{-# language NamedFieldPuns #-}
13+{-# language QuantifiedConstraints #-}
12{-# language RankNTypes #-}
14{-# language RankNTypes #-}
13{-# language RecordWildCards #-}
15{-# language RecordWildCards #-}
14{-# language ScopedTypeVariables #-}
16{-# language ScopedTypeVariables #-}
@@ -21,39 +23,34 @@ module Rel8.Schema.HTable.Vectorize
21 ( HVectorize
23 ( HVectorize
22 , hvectorize, hunvectorize
24 , hvectorize, hunvectorize
23 , happend, hempty
25 , happend, hempty
24− , hproject
25− , hcolumn
26 )
26 )
27where
27where
28
28
29-- base
29-- base
30−import Data.Kind ( Constraint, Type )
30+import Data.Kind ( Type )
31import Data.List.NonEmpty ( NonEmpty )
31import Data.List.NonEmpty ( NonEmpty )
32−import GHC.Generics (Generic)
33import Prelude
32import Prelude
34
33
35-- rel8
34-- rel8
36−import Rel8.FCF ( Eval, Exp )
35+import Rel8.Schema.Context.Label ( HLabelable, hlabeler, hunlabeler )
37import Rel8.Schema.Dict ( Dict( Dict ) )
36import Rel8.Schema.Dict ( Dict( Dict ) )
38import qualified Rel8.Schema.Kind as K
37import qualified Rel8.Schema.Kind as K
38+import Rel8.Schema.HTable
39+ ( HTable
39−import Rel8.Schema.HTable ( HTable, hfield, htabulate, htabulateA, hspecs )
40+ , 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 )
45 )
41 )
46−import qualified Rel8.Schema.HTable.MapTable as HMapTable ( hproject )
47import Rel8.Schema.Null ( Unnullify, NotNull, Nullity( NotNull ) )
42import Rel8.Schema.Null ( Unnullify, NotNull, Nullity( NotNull ) )
48−import Rel8.Schema.Spec ( Spec(..) )
43+import Rel8.Schema.Spec ( Spec( Spec ), SSpec(..) )
49import Rel8.Type.Array ( listTypeInformation, nonEmptyTypeInformation )
44import Rel8.Type.Array ( listTypeInformation, nonEmptyTypeInformation )
50import Rel8.Type.Information ( TypeInformation )
45import Rel8.Type.Information ( TypeInformation )
51
46
52-- semialign
47-- semialign
53import Data.Zip ( Unzip, Zip, Zippy(..) )
48import Data.Zip ( Unzip, Zip, Zippy(..) )
49+import Rel8.FCF
50+import Rel8.Schema.HTable.MapTable
51+import GHC.Generics (Generic)
54
52
55
53
56−type Vector :: (Type -> Type) -> Constraint
57class Vector list where
54class Vector list where
58 listNotNull :: proxy a -> Dict NotNull (list a)
55 listNotNull :: proxy a -> Dict NotNull (list a)
59 vectorTypeInformation :: ()
56 vectorTypeInformation :: ()
@@ -78,16 +75,16 @@ newtype HVectorize list table context = HVectorize (HMapTable (Vectorize list) t
78 deriving anyclass HTable
75 deriving anyclass HTable
79
76
80
77
81−data Vectorize :: (Type -> Type) -> Type -> Exp Type
78+data Vectorize :: (Type -> Type) -> Spec -> Exp Spec
82
79
83
80
84−type instance Eval (Vectorize list a) = list a
81+type instance Eval (Vectorize list ('Spec labels a)) = 'Spec labels (list a)
85
82
86
83
87instance Vector list => MapSpec (Vectorize list) where
84instance Vector list => MapSpec (Vectorize list) where
88 mapInfo = \case
85 mapInfo = \case
89− Spec {..} -> case listNotNull @list nullity of
86+ SSpec {..} -> case listNotNull @list nullity of
90− Dict -> Spec
87+ Dict -> SSpec
91 { nullity = NotNull
88 { nullity = NotNull
92 , info = vectorTypeInformation nullity info
89 , info = vectorTypeInformation nullity info
93 , ..
90 , ..
@@ -95,49 +92,60 @@ instance Vector list => MapSpec (Vectorize list) where
95
92
96
93
97hvectorize :: (HTable t, Unzip f, Vector list)
94hvectorize :: (HTable t, Unzip f, Vector list)
95+ => (forall labels a. ()
96+ => SSpec ('Spec labels a)
97+ -> f (context ('Spec labels a))
98− => (forall a. Spec a -> f (context a) -> context' (list a))
98+ -> context' ('Spec labels (list a)))
99 -> f (t context)
99 -> f (t context)
100 -> HVectorize list t context'
100 -> HVectorize list t context'
101hvectorize vectorizer as = HVectorize $ htabulate $ \(HMapTableField field) ->
101hvectorize vectorizer as = HVectorize $ htabulate $ \(HMapTableField field) ->
102 case hfield hspecs field of
102 case hfield hspecs field of
103− spec -> vectorizer spec (fmap (`hfield` field) as)
103+ spec@SSpec {} -> vectorizer spec (fmap (`hfield` field) as)
104{-# INLINABLE hvectorize #-}
104{-# INLINABLE hvectorize #-}
105
105
106
106
107hunvectorize :: (HTable t, Zip f, Vector list)
107hunvectorize :: (HTable t, Zip f, Vector list)
108+ => (forall labels a. ()
109+ => SSpec ('Spec labels a)
108− => (forall a. Spec a -> context (list a) -> f (context' a))
110+ -> context ('Spec labels (list a))
111+ -> f (context' ('Spec labels a)))
109 -> HVectorize list t context
112 -> HVectorize list t context
110 -> f (t context')
113 -> f (t context')
111hunvectorize unvectorizer (HVectorize table) =
114hunvectorize unvectorizer (HVectorize table) =
112 getZippy $ htabulateA $ \field -> case hfield hspecs field of
115 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
114 a -> Zippy (unvectorizer spec a)
117 a -> Zippy (unvectorizer spec a)
115{-# INLINABLE hunvectorize #-}
118{-# INLINABLE hunvectorize #-}
116
119
117
120
118−happend :: (HTable t, Vector list)
121+happend :: (HTable t, Vector list) =>
119− => (forall a. Spec a -> context (list a) -> context (list a) -> context (list a))
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+ )
120 -> HVectorize list t context
129 -> HVectorize list t context
121 -> HVectorize list t context
130 -> HVectorize list t context
122 -> HVectorize list t context
131 -> HVectorize list t context
123happend append (HVectorize as) (HVectorize bs) = HVectorize $
132happend append (HVectorize as) (HVectorize bs) = HVectorize $
124 htabulate $ \field@(HMapTableField j) -> case (hfield as field, hfield bs field) of
133 htabulate $ \field@(HMapTableField j) -> case (hfield as field, hfield bs field) of
125 (a, b) -> case hfield hspecs j of
134 (a, b) -> case hfield hspecs j of
126− spec -> append spec a b
135+ SSpec {nullity, info} -> append nullity info a b
127
136
128
137
129−hempty :: HTable t
138+hempty :: HTable t =>
130− => (forall a. Spec a -> context [a])
139+ ( forall labels a. ()
140+ => Nullity a
141+ -> TypeInformation (Unnullify a)
142+ -> context ('Spec labels [a])
143+ )
131 -> HVectorize [] t context
144 -> HVectorize [] t context
132−hempty empty = HVectorize $ htabulate $ \(HMapTableField field) ->
145+hempty empty = HVectorize $ htabulate $ \(HMapTableField field) -> case hfield hspecs field of
133− empty (hfield hspecs field)
146+ SSpec {nullity, info} -> empty nullity info
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)
140
147
141
148
142−hcolumn :: HVectorize list (HIdentity a) context -> context (list a)
149+instance HLabelable g => HLabelable (Precompose (Vectorize list) g) where
143−hcolumn (HVectorize (HMapTable (HIdentity (Precompose a)))) = a
150+ hlabeler = Precompose . hlabeler . precomposed
151+ hunlabeler = Precompose . hunlabeler . precomposed
src/Rel8/Schema/Kind.hs+15−6
@@ -1,9 +1,8 @@
1{-# language StandaloneKindSignatures #-}
1{-# language StandaloneKindSignatures #-}
2
2
3module Rel8.Schema.Kind
3module Rel8.Schema.Kind
4− ( Rel8able
4+ ( Context, Rel8able
5− , Context
6− , HTable
5+ , HContext, HTable
7 )
6 )
8where
7where
9
8
@@ -11,13 +10,23 @@ where
11import Data.Kind ( Type )
10import Data.Kind ( Type )
12import Prelude ()
11import Prelude ()
13
12
13+-- rel8
14+import Rel8.Schema.Spec ( Spec )
14
15
16+
15−type Context :: Type
17+type HContext :: Type
16−type Context = Type -> Type
18+type HContext = Spec -> Type
17
19
18
20
19type HTable :: Type
21type HTable :: Type
22+type HTable = HContext -> Type
23+
24+
25+data X
26+
27+
28+type Context :: Type
20−type HTable = Context -> Type
29+type Context = X -> Type
21
30
22
31
23type Rel8able :: Type
32type Rel8able :: Type
src/Rel8/Schema/Name.hs+56−34
@@ -3,44 +3,41 @@
3{-# language FlexibleContexts #-}
3{-# language FlexibleContexts #-}
4{-# language FlexibleInstances #-}
4{-# language FlexibleInstances #-}
5{-# language GADTs #-}
5{-# language GADTs #-}
6−{-# language GeneralizedNewtypeDeriving #-}
7{-# language MultiParamTypeClasses #-}
6{-# language MultiParamTypeClasses #-}
7+{-# language PolyKinds #-}
8{-# language RankNTypes #-}
8{-# language RankNTypes #-}
9+{-# language StandaloneDeriving #-}
9{-# language StandaloneKindSignatures #-}
10{-# language StandaloneKindSignatures #-}
10{-# language TypeFamilies #-}
11{-# language TypeFamilies #-}
11{-# language UndecidableInstances #-}
12{-# language UndecidableInstances #-}
13+{-# language UndecidableSuperClasses #-}
12
14
13module Rel8.Schema.Name
15module Rel8.Schema.Name
14 ( Name(..)
16 ( Name(..)
17+ , Col( N, unN )
15 , Selects
18 , Selects
16− , ppColumn
17 )
19 )
18where
20where
19
21
20-- base
22-- base
21−import Data.Functor.Identity ( Identity( Identity ) )
23+import Data.Functor.Identity ( Identity )
22import Data.Kind ( Constraint, Type )
24import Data.Kind ( Constraint, Type )
23−import Data.String ( IsString )
25+import Data.String ( IsString, fromString )
24import Prelude
26import Prelude
25
27
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−
33-- rel8
28-- rel8
34import Rel8.Expr ( Expr )
29import Rel8.Expr ( Expr )
35−import qualified Rel8.Schema.Kind as K
30+import Rel8.Schema.Context ( Interpretation, Col )
31+import Rel8.Schema.Context.Label ( Labelable, labeler, unlabeler )
36−import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) )
32+import Rel8.Schema.HTable.Identity ( HIdentity( HType ), HType )
37import Rel8.Schema.Null ( Sql )
33import Rel8.Schema.Null ( Sql )
34+import Rel8.Schema.Reify ( notReify )
35+import Rel8.Schema.Result ( Result )
36+import Rel8.Schema.Spec ( Spec( Spec ) )
38import Rel8.Table
37import Rel8.Table
39− ( Table, Columns, Context, fromColumns, toColumns
38+ ( Table, Columns, Context, fromColumns, toColumns, reify, unreify
40− , FromExprs, fromResult, toResult
41− , Transpose
42 )
39 )
43−import Rel8.Table.Transpose ( Transposes )
40+import Rel8.Table.Recontextualize ( Recontextualize )
44import Rel8.Type ( DBType )
41import Rel8.Type ( DBType )
45
42
46
43
@@ -48,30 +45,55 @@ import Rel8.Type ( DBType )
48-- schema definition. You can construct names by using the @OverloadedStrings@
45-- schema definition. You can construct names by using the @OverloadedStrings@
49-- extension and writing string literals. This is typically done when providing
46-- extension and writing string literals. This is typically done when providing
50-- a 'TableSchema' value.
47-- a 'TableSchema' value.
51−type Name :: K.Context
48+type Name :: k -> Type
52−newtype Name a = Name String
49+data Name a where
50+ Name :: k ~ Type => !String -> Name (a :: k)
51+
52+
53− deriving stock Show
53+deriving stock instance Show (Name a)
54− deriving newtype IsString
54+
55+
56+instance k ~ Type => IsString (Name (a :: k)) where
57+ fromString = Name
55
58
56
59
57instance Sql DBType a => Table Name (Name a) where
60instance Sql DBType a => Table Name (Name a) where
58− type Columns (Name a) = HIdentity a
61+ type Columns (Name a) = HType a
59 type Context (Name a) = Name
62 type Context (Name a) = Name
60− type FromExprs (Name a) = a
61− type Transpose to (Name a) = to a
62
63
63− toColumns a = HIdentity a
64+ toColumns a = HType (N a)
64− fromColumns (HIdentity a) = a
65+ fromColumns (HType (N a)) = a
65− toResult a = HIdentity (Identity a)
66+ reify = notReify
66− fromResult (HIdentity (Identity a)) = a
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
67
93
68
94
69-- | @Selects a b@ means that @a@ is a schema (i.e., a 'Table' of 'Name's) for
95-- | @Selects a b@ means that @a@ is a schema (i.e., a 'Table' of 'Name's) for
70-- the 'Expr' columns in @b@.
96-- the 'Expr' columns in @b@.
71type Selects :: Type -> Type -> Constraint
97type Selects :: Type -> Type -> Constraint
72−class Transposes Name Expr names exprs => Selects names exprs
98+class Recontextualize Name Expr names exprs => Selects names exprs
73−instance Transposes Name Expr names exprs => Selects names exprs
99+instance Recontextualize Name Expr names exprs => Selects names exprs
74−
75−
76−ppColumn :: String -> Doc
77−ppColumn = Opaleye.ppSqlExpr . Opaleye.ColumnSqlExpr . Opaleye.SqlColumn
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 @@
5{-# language TypeFamilies #-}
5{-# language TypeFamilies #-}
6
6
7module Rel8.Schema.Result
7module Rel8.Schema.Result
8− ( Result
8+ ( Col( R, unR ), Result
9+ , relabel
9 , null, nullifier, unnullifier
10 , null, nullifier, unnullifier
10 , vectorizer, unvectorizer
11 , vectorizer, unvectorizer
11 )
12 )
12where
13where
13
14
14-- base
15-- base
15−import Data.Functor.Identity ( Identity( Identity), runIdentity )
16import Prelude hiding ( null )
16import Prelude hiding ( null )
17
17
18-- rel8
18-- rel8
19+import Rel8.Schema.Context ( Interpretation( Col ) )
20+import Rel8.Schema.HTable.Identity ( HIdentity(..) )
19import Rel8.Schema.Kind ( Context )
21import Rel8.Schema.Kind ( Context )
20import Rel8.Schema.Null ( Nullify, Nullity( Null, NotNull ) )
22import Rel8.Schema.Null ( Nullify, Nullity( Null, NotNull ) )
21−import Rel8.Schema.Spec ( Spec(..) )
23+import Rel8.Schema.Spec ( Spec( Spec ), SSpec(..) )
22
24
23
25
24-- | The @Result@ context is the context used for decoded query results.
26-- | The @Result@ context is the context used for decoded query results.
@@ -26,29 +28,52 @@ import Rel8.Schema.Spec ( Spec(..) )
26-- When a query is executed against a PostgreSQL database, Rel8 parses the
28-- When a query is executed against a PostgreSQL database, Rel8 parses the
27-- returned rows, decoding each row into the @Result@ context.
29-- returned rows, decoding each row into the @Result@ context.
28type Result :: Context
30type Result :: Context
29−type Result = Identity
31+data Result a
30
32
31
33
32−null :: Result (Maybe a)
34+instance Interpretation Result where
33−null = Identity Nothing
35+ data Col Result _spec where
36+ R :: {unR :: !a} -> Col Result ('Spec labels a)
34
37
35
38
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)
36−nullifier :: Spec a -> Result a -> Result (Nullify a)
52+ -> Col Result ('Spec labels (Nullify a))
37−nullifier Spec {nullity} (Identity a) = Identity $ case nullity of
53+nullifier SSpec {nullity} (R a) = R $ case nullity of
38 Null -> a
54 Null -> a
39 NotNull -> Just a
55 NotNull -> Just a
40
56
41
57
42−unnullifier :: Spec a -> Result (Nullify a) -> Maybe (Result a)
58+unnullifier :: ()
59+ => SSpec ('Spec labels a)
60+ -> Col Result ('Spec labels (Nullify a))
61+ -> Maybe (Col Result ('Spec labels a))
43−unnullifier Spec {nullity} (Identity a) =
62+unnullifier SSpec {nullity} (R a) =
44 case nullity of
63 case nullity of
45− Null -> pure $ Identity a
64+ Null -> pure $ R a
46− NotNull -> Identity <$> a
65+ NotNull -> R <$> a
47
66
48
67
49−vectorizer :: Functor f => Spec a -> f (Result a) -> Result (f a)
68+vectorizer :: Functor f
69+ => SSpec ('Spec labels a)
70+ -> f (Col Result ('Spec labels a))
71+ -> Col Result ('Spec labels (f a))
50−vectorizer _ = Identity . fmap runIdentity
72+vectorizer _ = R . fmap unR
51
73
52
74
53−unvectorizer :: Functor f => Spec a -> Result (f a) -> f (Result a)
75+unvectorizer :: Functor f
76+ => SSpec ('Spec labels a)
77+ -> Col Result ('Spec labels (f a))
78+ -> f (Col Result ('Spec labels a))
54−unvectorizer _ (Identity results) = Identity <$> results
79+unvectorizer _ (R results) = R <$> results
src/Rel8/Schema/Spec.hs+37−17
@@ -1,34 +1,54 @@
1+{-# language DataKinds #-}
1{-# language FlexibleContexts #-}
2{-# language FlexibleContexts #-}
2−{-# language MonoLocalBinds #-}
3+{-# language GADTs #-}
3{-# language StandaloneKindSignatures #-}
4{-# language StandaloneKindSignatures #-}
5+{-# language UndecidableInstances #-}
4
6
5module Rel8.Schema.Spec
7module Rel8.Schema.Spec
8+ ( Spec( Spec )
6− ( Spec( Spec, labels, info, nullity )
9+ , SSpec( SSpec, labels, info, nullity )
7− , specification
10+ , KnownSpec( specSing )
8 )
11 )
9where
12where
10
13
11-- base
14-- base
12−import Data.Kind ( Type )
15+import Data.Kind ( Constraint, Type )
13−import Prelude
16+import Prelude ()
14
17
15-- rel8
18-- rel8
19+import Rel8.Kind.Labels ( Labels, SLabels, KnownLabels, labelsSing )
16import Rel8.Schema.Null ( Nullity, Sql, Unnullify, nullable )
20import Rel8.Schema.Null ( Nullity, Sql, Unnullify, nullable )
17import Rel8.Type ( DBType, typeInformation )
21import Rel8.Type ( DBType, typeInformation )
18import Rel8.Type.Information ( TypeInformation )
22import Rel8.Type.Information ( TypeInformation )
19
23
20
24
21−type Spec :: Type -> Type
25+type Spec :: Type
22−data Spec a = Spec
26+data Spec = Spec Labels Type
23− { labels :: [String]
24− , info :: TypeInformation (Unnullify a)
25− , nullity :: Nullity a
26− }
27
27
28
28
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
29−specification :: Sql DBType a => Spec a
46+ , Sql DBType a
30−specification = Spec
47+ )
48+ => KnownSpec ('Spec labels a)
49+ where
50+ specSing = SSpec
31− { labels = []
51+ { labels = labelsSing
32− , info = typeInformation
52+ , info = typeInformation
33− , nullity = nullable
53+ , nullity = nullable
34− }
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 @@
1{-# language DeriveFunctor #-}
1{-# language DeriveFunctor #-}
2{-# language DerivingStrategies #-}
2{-# language DerivingStrategies #-}
3−{-# language DisambiguateRecordFields #-}
4−{-# language NamedFieldPuns #-}
5−{-# language StandaloneKindSignatures #-}
6
3
7module Rel8.Schema.Table
4module Rel8.Schema.Table
8 ( TableSchema(..)
5 ( TableSchema(..)
9− , ppTable
10 )
6 )
11where
7where
12
8
13-- base
9-- base
14−import Data.Kind ( Type )
15import Prelude
10import Prelude
16
11
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−
24
12
25-- | The schema for a table. This is used to specify the name and schema that a
13-- | The schema for a table. This is used to specify the name and schema that a
26-- table belongs to (the @FROM@ part of a SQL query), along with the schema of
14-- table belongs to (the @FROM@ part of a SQL query), along with the schema of
@@ -28,7 +16,6 @@ import Text.PrettyPrint ( Doc )
28--
16--
29-- For each selectable table in your database, you should provide a
17-- For each selectable table in your database, you should provide a
30-- @TableSchema@ in order to interact with the table via Rel8.
18-- @TableSchema@ in order to interact with the table via Rel8.
31−type TableSchema :: Type -> Type
32data TableSchema names = TableSchema
19data TableSchema names = TableSchema
33 { name :: String
20 { name :: String
34 -- ^ The name of the table.
21 -- ^ The name of the table.
@@ -40,10 +27,3 @@ data TableSchema names = TableSchema
40 -- data type here, parameterized by the 'Rel8.ColumnSchema.ColumnSchema' functor.
27 -- data type here, parameterized by the 'Rel8.ColumnSchema.ColumnSchema' functor.
41 }
28 }
42 deriving stock Functor
29 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 @@
1{-# language DuplicateRecordFields #-}
1{-# language DuplicateRecordFields #-}
2{-# language GADTs #-}
2{-# language GADTs #-}
3{-# language NamedFieldPuns #-}
3{-# language NamedFieldPuns #-}
4−{-# language RankNTypes #-}
4+{-# language ScopedTypeVariables #-}
5−{-# language RecordWildCards #-}
6{-# language StandaloneKindSignatures #-}
5{-# language StandaloneKindSignatures #-}
7−{-# language StrictData #-}
6+{-# language TypeApplications #-}
8
7
9module Rel8.Statement.Delete
8module Rel8.Statement.Delete
10 ( Delete(..)
9 ( Delete(..)
11 , delete
10 , delete
12− , ppDelete
13 )
11 )
14where
12where
15
13
16-- base
14-- base
15+import Control.Exception ( throwIO )
17import Data.Kind ( Type )
16import Data.Kind ( Type )
18import Prelude
17import Prelude
19
18
20-- hasql
19-- hasql
20+import Hasql.Connection ( Connection )
21+import qualified Hasql.Decoders as Hasql
21import qualified Hasql.Encoders as Hasql
22import qualified Hasql.Encoders as Hasql
23+import qualified Hasql.Session as Hasql
22import qualified Hasql.Statement as Hasql
24import qualified Hasql.Statement as Hasql
23
25
24−-- pretty
26+-- opaleye
25−import Text.PrettyPrint ( Doc, (<+>), ($$), text )
27+import qualified Opaleye.Internal.Manipulation as Opaleye
26
28
27-- rel8
29-- rel8
28import Rel8.Expr ( Expr )
30import Rel8.Expr ( Expr )
29−import Rel8.Query ( Query )
31+import Rel8.Expr.Opaleye ( toColumn, toPrimExpr )
30import Rel8.Schema.Name ( Selects )
32import Rel8.Schema.Name ( Selects )
31−import Rel8.Schema.Table ( TableSchema, ppTable )
33+import Rel8.Schema.Table ( TableSchema )
32−import Rel8.Statement.Returning ( Returning, decodeReturning, ppReturning )
34+import Rel8.Statement.Returning ( Returning( NumberOfRowsAffected, Projection ) )
33−import Rel8.Statement.Using ( ppUsing )
35+import Rel8.Table ( fromColumns, toColumns )
34−import Rel8.Statement.Where ( ppWhere )
36+import Rel8.Table.Opaleye ( castTable, table, unpackspec )
37+import Rel8.Table.Serialize ( Serializable, parse )
35
38
36-- text
39-- text
37import qualified Data.Text as Text
40import qualified Data.Text as Text
@@ -44,10 +47,7 @@ data Delete a where
44 Delete :: Selects names exprs =>
47 Delete :: Selects names exprs =>
45 { from :: TableSchema names
48 { from :: TableSchema names
46 -- ^ Which table to delete from.
49 -- ^ 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
51 -- ^ Which rows should be selected for deletion.
51 -- ^ Which rows should be selected for deletion.
52 , returning :: Returning names a
52 , returning :: Returning names a
53 -- ^ What to return from the @DELETE@ statement.
53 -- ^ What to return from the @DELETE@ statement.
@@ -55,25 +55,38 @@ data Delete a where
55 -> Delete a
55 -> Delete a
56
56
57
57
58−ppDelete :: Delete a -> Doc
58+-- | Run a @DELETE@ statement.
59−ppDelete Delete {..} = case ppUsing using of
59+delete :: Connection -> Delete a -> IO a
60− Nothing ->
60+delete c Delete {from, deleteWhere, returning} =
61− text "DELETE FROM" <+> ppTable from $$
61+ case returning of
62− text "WHERE false"
62+ NumberOfRowsAffected -> Hasql.run session c >>= either throwIO pure
63− Just (usingDoc, i) ->
63+ where
64− text "DELETE FROM" <+> ppTable from $$
64+ session = Hasql.statement () statement
65− usingDoc $$
65+ statement = Hasql.Statement bytes params decode prepare
66− ppWhere from (deleteWhere i) $$
66+ bytes = encodeUtf8 $ Text.pack sql
67− ppReturning from returning
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
68
74
69−
75+ Projection project -> Hasql.run session c >>= either throwIO pure
70−-- | Run a 'Delete' statement.
76+ where
71−delete :: Delete a -> Hasql.Statement () a
77+ session = Hasql.statement () statement
72−delete d@Delete {returning} = Hasql.Statement bytes params decode prepare
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
73 where
89 where
74− bytes = encodeUtf8 $ Text.pack sql
90+ decoder :: forall exprs projection a. Serializable projection a
75− params = Hasql.noParams
91+ => (exprs -> projection) -> Hasql.Result [a]
76− decode = decodeReturning returning
92+ decoder _ = Hasql.rowList (parse @projection @a)
77− prepare = False
78− sql = show doc
79− doc = ppDelete d
src/Rel8/Statement/Insert.hs+72−43
@@ -1,59 +1,64 @@
1{-# language DuplicateRecordFields #-}
1{-# language DuplicateRecordFields #-}
2−{-# language FlexibleContexts #-}
3{-# language GADTs #-}
2{-# language GADTs #-}
4{-# language NamedFieldPuns #-}
3{-# language NamedFieldPuns #-}
5−{-# language RecordWildCards #-}
4+{-# language ScopedTypeVariables #-}
6{-# language StandaloneKindSignatures #-}
5{-# language StandaloneKindSignatures #-}
7−{-# language StrictData #-}
6+{-# language TypeApplications #-}
8
7
9module Rel8.Statement.Insert
8module Rel8.Statement.Insert
10 ( Insert(..)
9 ( Insert(..)
10+ , OnConflict(..)
11 , insert
11 , insert
12− , ppInsert
13− , ppInto
14 )
12 )
15where
13where
16
14
17-- base
15-- base
18−import Data.Foldable ( toList )
16+import Control.Exception ( throwIO )
17+import Data.List.NonEmpty ( NonEmpty( (:|) ) )
19import Data.Kind ( Type )
18import Data.Kind ( Type )
20import Prelude
19import Prelude
21
20
22-- hasql
21-- hasql
22+import Hasql.Connection ( Connection )
23+import qualified Hasql.Decoders as Hasql
23import qualified Hasql.Encoders as Hasql
24import qualified Hasql.Encoders as Hasql
25+import qualified Hasql.Session as Hasql
24import qualified Hasql.Statement as Hasql
26import qualified Hasql.Statement as Hasql
25
27
26-- opaleye
28-- opaleye
27−import qualified Opaleye.Internal.HaskellDB.Sql.Print as Opaleye
29+import qualified Opaleye.Internal.Manipulation as Opaleye
28−
30+import qualified Opaleye.Manipulation as Opaleye
29−-- pretty
30−import Text.PrettyPrint ( Doc, (<+>), ($$), parens, text )
31
31
32-- rel8
32-- rel8
33−import Rel8.Query ( Query )
34−import Rel8.Schema.Name ( Name, Selects, ppColumn )
33+import Rel8.Schema.Name ( Selects )
35−import Rel8.Schema.Table ( TableSchema(..), ppTable )
34+import Rel8.Schema.Table ( TableSchema )
36−import Rel8.Statement.OnConflict ( OnConflict, ppOnConflict )
35+import Rel8.Statement.Returning ( Returning( Projection, NumberOfRowsAffected ) )
37−import Rel8.Statement.Returning ( Returning, decodeReturning, ppReturning )
36+import Rel8.Table ( fromColumns, toColumns )
38−import Rel8.Statement.Select ( ppRows )
37+import Rel8.Table.Opaleye ( castTable, table, unpackspec )
39−import Rel8.Table ( Table )
40−import Rel8.Table.Name ( showNames )
38+import Rel8.Table.Serialize ( Serializable, parse )
41
39
42-- text
40-- text
43import qualified Data.Text as Text ( pack )
41import qualified Data.Text as Text ( pack )
44import Data.Text.Encoding ( encodeUtf8 )
42import Data.Text.Encoding ( encodeUtf8 )
45
43
46
44
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+
47-- | The constituent parts of a SQL @INSERT@ statement.
53-- | The constituent parts of a SQL @INSERT@ statement.
48type Insert :: Type -> Type
54type Insert :: Type -> Type
49data Insert a where
55data Insert a where
50 Insert :: Selects names exprs =>
56 Insert :: Selects names exprs =>
51 { into :: TableSchema names
57 { into :: TableSchema names
52 -- ^ Which table to insert into.
58 -- ^ Which table to insert into.
53− , rows :: Query exprs
59+ , rows :: [exprs]
54− -- ^ The rows to insert. This can be an arbitrary query — use
60+ -- ^ The rows to insert.
55− -- 'Rel8.values' insert a static list of rows.
56− , onConflict :: OnConflict names
61+ , onConflict :: OnConflict
57 -- ^ What to do if the inserted rows conflict with data already in the
62 -- ^ What to do if the inserted rows conflict with data already in the
58 -- table.
63 -- table.
59 , returning :: Returning names a
64 , returning :: Returning names a
@@ -62,28 +67,52 @@ data Insert a where
62 -> Insert a
67 -> Insert a
63
68
64
69
65−ppInsert :: Insert a -> Doc
70+-- | Run an @INSERT@ statement
66−ppInsert Insert {..} =
71+insert :: Connection -> Insert a -> IO a
67− text "INSERT INTO" <+>
72+insert c Insert {into, rows, onConflict, returning} =
68− ppInto into $$
73+ case (rows, returning) of
69− ppRows rows $$
74+ ([], NumberOfRowsAffected) -> pure 0
70− ppOnConflict into onConflict $$
75+ ([], Projection _) -> pure []
71− ppReturning into returning
72
76
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
73
89
74−ppInto :: Table Name a => TableSchema a -> Doc
90+ (x:xs, Projection project) -> Hasql.run session c >>= either throwIO pure
75−ppInto table@TableSchema {columns} =
91+ where
76− ppTable table <+>
92+ session = Hasql.statement () statement
77− parens (Opaleye.commaV ppColumn (toList (showNames columns)))
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
78
109
79−
80−-- | Run an 'Insert' statement.
81−insert :: Insert a -> Hasql.Statement () a
82−insert i@Insert {returning} = Hasql.Statement bytes params decode prepare
83 where
110 where
84− bytes = encodeUtf8 $ Text.pack sql
111+ onConflict' =
85− params = Hasql.noParams
112+ case onConflict of
86− decode = decodeReturning returning
113+ DoNothing -> Just Opaleye.DoNothing
87− prepare = False
114+ Abort -> Nothing
88− sql = show doc
115+
89− doc = ppInsert i
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 @@
1{-# language GADTs #-}
1{-# language GADTs #-}
2−{-# language LambdaCase #-}
3−{-# language NamedFieldPuns #-}
4−{-# language RankNTypes #-}
5−{-# language ScopedTypeVariables #-}
6{-# language StandaloneKindSignatures #-}
2{-# language StandaloneKindSignatures #-}
7−{-# language StrictData #-}
8−{-# language TypeApplications #-}
9
3
10module Rel8.Statement.Returning
4module Rel8.Statement.Returning
11− ( Returning( NumberOfRowsAffected, Projection )
5+ ( Returning(..)
12− , decodeReturning
13− , ppReturning
14 )
6 )
15where
7where
16
8
17-- base
9-- base
18−import Control.Applicative ( liftA2 )
19−import Data.Foldable ( toList )
20import Data.Int ( Int64 )
10import Data.Int ( Int64 )
21import Data.Kind ( Type )
11import Data.Kind ( Type )
22−import Data.List.NonEmpty ( NonEmpty )
23−import Prelude
12+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 )
35
13
36-- rel8
14-- rel8
37import Rel8.Schema.Name ( Selects )
15import 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 )
41
17
42−-- semigropuoids
43−import Data.Functor.Apply ( Apply, (<.>) )
44
18
45−
46−-- | 'Rel8.Insert', 'Rel8.Update' and 'Rel8.Delete' all support returning either
19+-- | @INSERT@, @UPDATE@ and @DELETE@ all support returning either the number of
47−-- the number of rows affected, or the actual rows modified.
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).
48type Returning :: Type -> Type -> Type
24type Returning :: Type -> Type -> Type
49data Returning names a where
25data 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.
54 NumberOfRowsAffected :: Returning names Int64
26 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)
27+ Projection :: (Selects names exprs, Serializable projection a)
61− => (exprs -> returning)
28+ => (exprs -> projection)
62 -> Returning names [a]
29 -> 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 #-}
4{-# language MonoLocalBinds #-}
1{-# language MonoLocalBinds #-}
5{-# language ScopedTypeVariables #-}
2{-# language ScopedTypeVariables #-}
6−{-# language StandaloneKindSignatures #-}
7{-# language TypeApplications #-}
3{-# language TypeApplications #-}
8
4
9module Rel8.Statement.Select
5module Rel8.Statement.Select
10 ( select
6 ( select
11− , ppSelect
7+ , selectWithNames
12−
13− , Optimized(..)
14− , ppPrimSelect
15− , ppRows
16 )
8 )
17where
9where
18
10
19-- base
11-- base
20−import Data.Foldable ( toList )
12+import Control.Exception ( throwIO )
21−import Data.Kind ( Type )
22−import Data.Void ( Void )
23−import Prelude hiding ( undefined )
13+import Prelude
24
14
25-- hasql
15-- hasql
16+import Hasql.Connection ( Connection )
26import qualified Hasql.Decoders as Hasql
17import qualified Hasql.Decoders as Hasql
27import qualified Hasql.Encoders as Hasql
18import qualified Hasql.Encoders as Hasql
19+import qualified Hasql.Session as Hasql
28import qualified Hasql.Statement as Hasql
20import qualified Hasql.Statement as Hasql
29
21
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−
44-- rel8
22-- rel8
45−import Rel8.Expr ( Expr )
46−import Rel8.Expr.Bool ( false )
47−import Rel8.Expr.Opaleye ( toPrimExpr )
48import Rel8.Query ( Query )
23import Rel8.Query ( Query )
49−import Rel8.Query.Opaleye ( toOpaleye )
24+import Rel8.Query.SQL ( sqlForQuery, sqlForQueryWithNames )
50import Rel8.Schema.Name ( Selects )
25import 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
56import Rel8.Table.Serialize ( Serializable, parse )
26import Rel8.Table.Serialize ( Serializable, parse )
57−import Rel8.Table.Undefined ( undefined )
58
27
59-- text
28-- text
60import qualified Data.Text as Text
29import qualified Data.Text as Text
61import Data.Text.Encoding ( encodeUtf8 )
30import Data.Text.Encoding ( encodeUtf8 )
62
31
63
32
64−-- | Run a @SELECT@ statement, returning all rows.
33+-- | Run a @SELECT@ query, returning all rows.
65select :: forall exprs a. Serializable exprs a
34select :: forall exprs a. Serializable exprs a
66− => Query exprs -> Hasql.Statement () [a]
35+ => Connection -> Query exprs -> IO [a]
67−select query = Hasql.Statement bytes params decode prepare
36+select c query = case sqlForQuery query of
68− where
37+ Nothing -> pure []
69− bytes = encodeUtf8 (Text.pack sql)
38+ Just sql -> Hasql.run session c >>= either throwIO pure
70− params = Hasql.noParams
39+ where
71− decode = Hasql.rowList (parse @exprs @a)
40+ session = Hasql.statement () statement
72− prepare = False
41+ statement = Hasql.Statement bytes params decode prepare
73− sql = show doc
42+ bytes = encodeUtf8 (Text.pack sql)
74− doc = ppSelect query
43+ params = Hasql.noParams
75−
44+ decode = Hasql.rowList (parse @exprs @a)
76−
45+ prepare = False
77−ppSelect :: Table Expr a => Query a -> Doc
46+
78−ppSelect query =
47+
79− Opaleye.ppSql $ primSelectWith names (toCols exprs') primQuery'
48+selectWithNames :: forall exprs a names.
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
49+ ( Selects names exprs
137− => names -> exprs -> Opaleye.PrimQuery' Void -> Opaleye.Select
50+ , Serializable exprs a
138−primSelectWith names exprs query =
51+ )
139− Opaleye.SelectFrom $ Opaleye.newSelect
52+ => Connection -> names -> Query exprs -> IO [a]
140− { Opaleye.attrs = Opaleye.SelectAttrs attrs
53+selectWithNames c names query = case sqlForQueryWithNames names query of
141− , Opaleye.tables = Opaleye.oneTable (primSelect query)
54+ Nothing -> pure []
142− }
55+ Just sql -> Hasql.run session c >>= either throwIO pure
143− where
56+ where
144− attrs = makeAttr <$> exprsWithNames names (castTable exprs)
57+ session = Hasql.statement () statement
145− where
58+ statement = Hasql.Statement bytes params decode prepare
146− makeAttr (label, expr) =
59+ bytes = encodeUtf8 (Text.pack sql)
147− (Opaleye.sqlExpr expr, Just (Opaleye.SqlColumn label))
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 #-}
2{-# language GADTs #-}
1{-# language GADTs #-}
3{-# language NamedFieldPuns #-}
2{-# language NamedFieldPuns #-}
4−{-# language RecordWildCards #-}
3+{-# language ScopedTypeVariables #-}
5{-# language StandaloneKindSignatures #-}
4{-# language StandaloneKindSignatures #-}
6−{-# language StrictData #-}
5+{-# language TypeApplications #-}
7
6
8module Rel8.Statement.Update
7module Rel8.Statement.Update
9 ( Update(..)
8 ( Update(..)
10 , update
9 , update
11− , ppUpdate
12 )
10 )
13where
11where
14
12
15-- base
13-- base
14+import Control.Exception ( throwIO )
16import Data.Kind ( Type )
15import Data.Kind ( Type )
17import Prelude
16import Prelude
18
17
19-- hasql
18-- hasql
19+import Hasql.Connection ( Connection )
20+import qualified Hasql.Decoders as Hasql
20import qualified Hasql.Encoders as Hasql
21import qualified Hasql.Encoders as Hasql
22+import qualified Hasql.Session as Hasql
21import qualified Hasql.Statement as Hasql
23import qualified Hasql.Statement as Hasql
22
24
23−-- pretty
25+-- opaleye
24−import Text.PrettyPrint ( Doc, (<+>), ($$), text )
26+import qualified Opaleye.Internal.Manipulation as Opaleye
25
27
26-- rel8
28-- rel8
27import Rel8.Expr ( Expr )
29import Rel8.Expr ( Expr )
28−import Rel8.Query ( Query )
30+import Rel8.Expr.Opaleye ( toColumn, toPrimExpr )
29import Rel8.Schema.Name ( Selects )
31import Rel8.Schema.Name ( Selects )
30−import Rel8.Schema.Table ( TableSchema(..), ppTable )
32+import Rel8.Schema.Table ( TableSchema )
31−import Rel8.Statement.Returning ( Returning, decodeReturning, ppReturning )
33+import Rel8.Statement.Returning ( Returning( Projection, NumberOfRowsAffected ) )
32−import Rel8.Statement.Set ( ppSet )
34+import Rel8.Table ( fromColumns, toColumns )
33−import Rel8.Statement.Using ( ppFrom )
35+import Rel8.Table.Opaleye ( castTable, table, unpackspec )
34−import Rel8.Statement.Where ( ppWhere )
36+import Rel8.Table.Serialize ( Serializable, parse )
35
37
36-- text
38-- text
37import qualified Data.Text as Text
39import qualified Data.Text as Text
@@ -44,12 +46,9 @@ data Update a where
44 Update :: Selects names exprs =>
46 Update :: Selects names exprs =>
45 { target :: TableSchema names
47 { target :: TableSchema names
46 -- ^ Which table to update.
48 -- ^ 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
51 -- ^ How to update each selected row.
50 -- ^ How to update each selected row.
52− , updateWhere :: from -> exprs -> Expr Bool
51+ , updateWhere :: exprs -> Expr Bool
53 -- ^ Which rows to select for update.
52 -- ^ Which rows to select for update.
54 , returning :: Returning names a
53 , returning :: Returning names a
55 -- ^ What to return from the @UPDATE@ statement.
54 -- ^ What to return from the @UPDATE@ statement.
@@ -57,27 +56,46 @@ data Update a where
57 -> Update a
56 -> Update a
58
57
59
58
60−ppUpdate :: Update a -> Doc
59+-- | Run an @UPDATE@ statement.
61−ppUpdate Update {..} = case ppFrom from of
60+update :: Connection -> Update a -> IO a
62− Nothing ->
61+update c Update {target, set, updateWhere, returning} =
63− text "UPDATE" <+> ppTable target $$
62+ case returning of
64− ppSet target id $$
63+ NumberOfRowsAffected -> Hasql.run session c >>= either throwIO pure
65− text "WHERE false"
64+ where
66− Just (fromDoc, i) ->
65+ session = Hasql.statement () statement
67− text "UPDATE" <+> ppTable target $$
66+ statement = Hasql.Statement bytes params decode prepare
68− ppSet target (set i) $$
67+ bytes = encodeUtf8 $ Text.pack sql
69− fromDoc $$
68+ params = Hasql.noParams
70− ppWhere target (updateWhere i) $$
69+ decode = Hasql.rowsAffected
71− ppReturning target returning
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
72
76
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
73
97
74−-- | Run an @UPDATE@ statement.
75−update :: Update a -> Hasql.Statement () a
76−update u@Update {returning} = Hasql.Statement bytes params decode prepare
77 where
98 where
78− bytes = encodeUtf8 $ Text.pack sql
99+ decoder :: forall exprs projection a. Serializable projection a
79− params = Hasql.noParams
100+ => (exprs -> projection) -> Hasql.Result [a]
80− decode = decodeReturning returning
101+ decoder _ = Hasql.rowList (parse @projection @a)
81− prepare = False
82− sql = show doc
83− doc = ppUpdate u
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 @@
3
3
4module Rel8.Statement.View
4module Rel8.Statement.View
5 ( createView
5 ( createView
6− , createOrReplaceView
7 )
6 )
8where
7where
9
8
10-- base
9-- base
10+import Control.Exception ( throwIO )
11+import Control.Monad ( (>=>) )
12+import Data.Foldable ( fold )
13+import Data.Maybe ( fromMaybe )
11import Prelude
14import Prelude
12
15
13-- hasql
16-- hasql
17+import Hasql.Connection ( Connection )
14import qualified Hasql.Decoders as Hasql
18import qualified Hasql.Decoders as Hasql
15import qualified Hasql.Encoders as Hasql
19import qualified Hasql.Encoders as Hasql
20+import qualified Hasql.Session as Hasql
16import qualified Hasql.Statement as Hasql
21import qualified Hasql.Statement as Hasql
17
22
18-- rel8
23-- rel8
19import Rel8.Query ( Query )
24import Rel8.Query ( Query )
25+import Rel8.Query.SQL ( sqlForQueryWithNames )
20import Rel8.Schema.Name ( Selects )
26import Rel8.Schema.Name ( Selects )
21−import Rel8.Schema.Table ( TableSchema )
27+import Rel8.Schema.Table ( TableSchema( TableSchema ) )
22−import Rel8.Statement.Insert ( ppInto )
28+import Rel8.Table.Alternative ( emptyTable )
23−import Rel8.Statement.Select ( ppSelect )
24−
25−-- pretty
26−import Text.PrettyPrint ( Doc, (<+>), ($$), text )
27
29
28-- text
30-- text
29import qualified Data.Text as Text
31import qualified Data.Text as Text
30import Data.Text.Encoding ( encodeUtf8 )
32import Data.Text.Encoding ( encodeUtf8 )
31
33
32
34
33−data CreateView = Create | CreateOrReplace
34−
35−
36-- | Given a 'TableSchema' and 'Query', @createView@ runs a @CREATE VIEW@
35-- | Given a 'TableSchema' and 'Query', @createView@ runs a @CREATE VIEW@
37-- statement that will save the given query as a view. This can be useful if
36-- statement that will save the given query as a view. This can be useful if
38-- you want to share Rel8 queries with other applications.
37-- you want to share Rel8 queries with other applications.
39createView :: Selects names exprs
38createView :: Selects names exprs
40− => TableSchema names -> Query exprs -> Hasql.Statement () ()
39+ => TableSchema names -> Query exprs -> Connection -> IO ()
41−createView =
40+createView (TableSchema name mschema names) query =
42− createViewGeneric Create
41+ Hasql.run session >=> either throwIO pure
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
60 where
42 where
43+ session = Hasql.statement () statement
44+ statement = Hasql.Statement bytes params decode prepare
61 bytes = encodeUtf8 (Text.pack sql)
45 bytes = encodeUtf8 (Text.pack sql)
62 params = Hasql.noParams
46 params = Hasql.noParams
63 decode = Hasql.noResult
47 decode = Hasql.noResult
64 prepare = False
48 prepare = False
65− sql = show doc
49+ sql = "CREATE VIEW " <> title <> " AS " <> select
66− doc = ppCreateView schema query replace
50+ where
67−
51+ title = case mschema of
68−
52+ Nothing -> quote name
69−ppCreateView :: Selects names exprs
53+ Just schema -> quote schema <> "." <> quote name
70− => TableSchema names -> Query exprs -> CreateView -> Doc
54+ select = fromMaybe fallback $ sqlForQueryWithNames names query
71−ppCreateView schema query replace =
55+ where
72− createOrReplace replace <+>
56+ fallback = fold $ sqlForQueryWithNames names emptyTable
73− ppInto schema $$
57+
74− text "AS" <+>
58+
75− ppSelect query
59+quote :: String -> String
60+quote string = "\"" <> concatMap go string <> "\""
76 where
61 where
77− createOrReplace Create = text "CREATE VIEW"
62+ go '"' = "\"\""
78− createOrReplace CreateOrReplace = text "CREATE OR REPLACE VIEW"
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 @@
1{-# language AllowAmbiguousTypes #-}
1{-# language AllowAmbiguousTypes #-}
2{-# language DataKinds #-}
2{-# language DataKinds #-}
3{-# language DefaultSignatures #-}
3{-# language DefaultSignatures #-}
4+{-# language DisambiguateRecordFields #-}
4{-# language FlexibleContexts #-}
5{-# language FlexibleContexts #-}
5{-# language FlexibleInstances #-}
6{-# language FlexibleInstances #-}
6{-# language FunctionalDependencies #-}
7{-# language FunctionalDependencies #-}
8+{-# language LambdaCase #-}
9+{-# language NamedFieldPuns #-}
7{-# language ScopedTypeVariables #-}
10{-# language ScopedTypeVariables #-}
8{-# language StandaloneKindSignatures #-}
11{-# language StandaloneKindSignatures #-}
9{-# language TypeApplications #-}
12{-# language TypeApplications #-}
10{-# language TypeFamilies #-}
13{-# language TypeFamilies #-}
14+{-# language TypeOperators #-}
11{-# language UndecidableInstances #-}
15{-# language UndecidableInstances #-}
12
16
13module Rel8.Table
17module Rel8.Table
14− ( Table
15− ( Columns, Context, fromColumns, toColumns
18+ ( Table (Columns, Context, Unreify, toColumns, fromColumns, reify, unreify)
16− , FromExprs, fromResult, toResult
17− , Transpose
18− )
19 , Congruent
19 , Congruent
20− , TTable, TColumns, TContext, TFromExprs, TTranspose
20+ , TTable, TColumns, TContext, TUnreify
21− , TSerialize
22 )
21 )
23where
22where
24
23
25-- base
24-- base
25+import Data.Functor ( ($>) )
26import Data.Functor.Identity ( Identity( Identity ) )
26import Data.Functor.Identity ( Identity( Identity ) )
27import Data.Kind ( Constraint, Type )
27import Data.Kind ( Constraint, Type )
28+import Data.List.NonEmpty ( NonEmpty )
29+import Data.Proxy ( Proxy( Proxy ) )
30+import Data.Type.Equality ( (:~:)( Refl ) )
28import GHC.Generics ( Generic, Rep, from, to )
31import GHC.Generics ( Generic, Rep, from, to )
29import Prelude hiding ( null )
32import Prelude hiding ( null )
30
33
31-- rel8
34-- rel8
32import Rel8.FCF ( Eval, Exp )
35import Rel8.FCF ( Eval, Exp )
33−import Rel8.Generic.Map ( Map )
36+import Rel8.Kind.Algebra ( KnownAlgebra )
37+import Rel8.Generic.Map ( GMap, GMappable, gmap, gunmap )
34−import Rel8.Generic.Table.Record
38+import Rel8.Generic.Table
35− ( GTable, GColumns, GContext, gfromColumns, gtoColumns
39+ ( GGTable, GGColumns, GGContext, ggfromColumns, ggtoColumns
36− , GSerialize, gfromResult, gtoResult
40+ , GAlgebra
37 )
41 )
38import Rel8.Generic.Record ( Record(..) )
42import Rel8.Generic.Record ( Record(..) )
43+import Rel8.Generic.Reify ( ARep )
44+import Rel8.Schema.Context.Label ( Labelable, labeler, unlabeler )
39import Rel8.Schema.HTable ( HTable )
45import 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 )
41import qualified Rel8.Schema.Kind as K
55import qualified Rel8.Schema.Kind as K
42import Rel8.Schema.Null ( Sql )
56import 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 )
44import Rel8.Type ( DBType )
69import 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 )
45
75
46
76
47-- | @Table@s are one of the foundational elements of Rel8, and describe data
77-- | @Table@s are one of the foundational elements of Rel8, and describe data
@@ -56,102 +86,90 @@ import Rel8.Type ( DBType )
56-- yourself, as anything that's an instance of 'Rel8.Rel8able' is always a
86-- yourself, as anything that's an instance of 'Rel8.Rel8able' is always a
57-- 'Table'.
87-- 'Table'.
58type Table :: K.Context -> Type -> Constraint
88type 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
89+class (HTable (Columns a), context ~ Context a) => Table context a | a -> context where
65− where
66 -- | The 'HTable' functor that describes the schema of this table.
90 -- | The 'HTable' functor that describes the schema of this table.
67 type Columns a :: K.HTable
91 type Columns a :: K.HTable
68
92
69 -- | The common context that all columns use as an interpretation.
93 -- | The common context that all columns use as an interpretation.
70 type Context a :: K.Context
94 type Context a :: K.Context
71
95
72− -- | The @FromExprs@ type family maps a type in the @Expr@ context to the
96+ type Unreify a :: Type
73− -- corresponding Haskell type.
74− type FromExprs a :: Type
75−
76− type Transpose (context' :: K.Context) a :: Type
77
97
98+ toColumns :: a -> Columns a (Col context)
99+ fromColumns :: Columns a (Col context) -> a
78
100
79− toColumns :: a -> Columns a context
101+ reify :: context :~: Reify ctx -> Unreify a -> a
80− fromColumns :: Columns a context -> a
102+ unreify :: context :~: Reify ctx -> a -> Unreify a
81
103
82− fromResult :: Columns a Result -> FromExprs a
104+ type Columns a = Eval (GGColumns (GAlgebra (Rep (Record a))) TColumns (Rep (Record 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))
105+ type Context a = Eval (GGContext (GAlgebra (Rep (Record a))) TContext (Rep (Record a)))
87− type FromExprs a = Map TFromExprs a
106+ type Unreify a = DefaultUnreify a
88− type Transpose context a = Map (TTranspose context) a
89
107
90 default toColumns ::
108 default toColumns ::
91 ( Generic (Record a)
109 ( Generic (Record a)
110+ , KnownAlgebra (GAlgebra (Rep (Record a)))
92− , GTable (TTable context) TColumns (Rep (Record a))
111+ , Eval (GGTable (GAlgebra (Rep (Record a))) (TTable context) TColumns (Col context) (Rep (Record a)))
93− , Columns a ~ GColumns TColumns (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)))
94 )
114 )
95− => a -> Columns a context
115+ => a -> Columns a (Col context)
96 toColumns =
116 toColumns =
117+ ggtoColumns
118+ @(GAlgebra (Rep (Record a)))
97− gtoColumns @(TTable context) @TColumns toColumns .
119+ @(TTable context)
120+ @TColumns
121+ id
122+ id
123+ toColumns .
98 from .
124 from .
99 Record
125 Record
100
126
101 default fromColumns ::
127 default fromColumns ::
102 ( Generic (Record a)
128 ( Generic (Record a)
129+ , KnownAlgebra (GAlgebra (Rep (Record a)))
103− , GTable (TTable context) TColumns (Rep (Record a))
130+ , Eval (GGTable (GAlgebra (Rep (Record a))) (TTable context) TColumns (Col context) (Rep (Record a)))
104− , Columns a ~ GColumns TColumns (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)))
105 )
133 )
106− => Columns a context -> a
134+ => Columns a (Col context) -> a
107 fromColumns =
135 fromColumns =
108 unrecord .
136 unrecord .
109 to .
137 to .
138+ ggfromColumns
139+ @(GAlgebra (Rep (Record a)))
110− gfromColumns @(TTable context) @TColumns fromColumns
140+ @(TTable context)
141+ @TColumns
142+ id
143+ id
144+ fromColumns
111
145
112− default toResult ::
146+ default reify ::
113− ( Generic (Record (FromExprs a))
147+ ( Generic (Record a)
114− , GSerialize TSerialize TColumns (Rep (Record a)) (Rep (Record (FromExprs a)))
148+ , Generic (Record (Unreify a))
115− , Columns a ~ GColumns TColumns (Rep (Record a))
149+ , GMappable (TTable context) (Rep (Record a))
150+ , Rep (Record (Unreify a)) ~ GMap TUnreify (Rep (Record a))
116 )
151 )
117− => FromExprs a -> Columns a Result
152+ => context :~: Reify ctx -> Unreify a -> a
118− toResult =
153+ reify Refl =
119− gtoResult
154+ unrecord .
120− @TSerialize
155+ to .
121− @TColumns
156+ gunmap @(TTable context) (Proxy @TUnreify) (reify Refl) .
122− @(Rep (Record a))
123− @(Rep (Record (FromExprs a)))
124− (\(_ :: proxy x) -> toResult @(Context x) @x) .
125 from .
157 from .
126 Record
158 Record
127
159
128− default fromResult ::
160+ default unreify ::
129− ( Generic (Record (FromExprs a))
161+ ( Generic (Record a)
130− , GSerialize TSerialize TColumns (Rep (Record a)) (Rep (Record (FromExprs a)))
162+ , Generic (Record (Unreify a))
131− , Columns a ~ GColumns TColumns (Rep (Record a))
163+ , GMappable (TTable context) (Rep (Record a))
164+ , Rep (Record (Unreify a)) ~ GMap TUnreify (Rep (Record a))
132 )
165 )
133− => Columns a Result -> FromExprs a
166+ => context :~: Reify ctx -> a -> Unreify a
134− fromResult =
167+ unreify Refl =
135 unrecord .
168 unrecord .
136 to .
169 to .
137− gfromResult
170+ gmap @(TTable context) (Proxy @TUnreify) (unreify Refl) .
138− @TSerialize
171+ from .
139− @TColumns
172+ Record
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
155
173
156
174
157data TTable :: K.Context -> Type -> Exp Constraint
175data TTable :: K.Context -> Type -> Exp Constraint
@@ -166,32 +184,175 @@ data TContext :: Type -> Exp K.Context
166type instance Eval (TContext a) = Context a
184type instance Eval (TContext a) = Context a
167
185
168
186
169−data TFromExprs :: Type -> Exp Type
187+data TUnreify :: Type -> Exp Type
170−type instance Eval (TFromExprs a) = FromExprs a
188+type instance Eval (TUnreify a) = Unreify a
171
189
172
190
173−data TTranspose :: K.Context -> Type -> Exp Type
191+type DefaultUnreify :: Type -> Type
174−type instance Eval (TTranspose context a) = Transpose context a
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))
175
205
176
206
177−data TSerialize :: Type -> Type -> Exp Constraint
207+-- | Any 'HTable' is also a 'Table'.
178−type instance Eval (TSerialize expr a) =
208+instance HTable t => Table context (t (Col context)) where
179− ( Table (Context expr) expr
209+ type Columns (t (Col context)) = t
180− , a ~ FromExprs expr
210+ type Context (t (Col context)) = context
181− )
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
182
218
183
219
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+
184−instance (Table context a, Table context b) => Table context (a, b)
342+instance (Table context a, Table context b, Labelable context)
343+ => Table context (a, b)
185
344
186
345
187instance
346instance
188 ( Table context a, Table context b, Table context c
347 ( Table context a, Table context b, Table context c
348+ , Labelable context
189 )
349 )
190 => Table context (a, b, c)
350 => Table context (a, b, c)
191
351
192
352
193instance
353instance
194 ( Table context a, Table context b, Table context c, Table context d
354 ( Table context a, Table context b, Table context c, Table context d
355+ , Labelable context
195 )
356 )
196 => Table context (a, b, c, d)
357 => Table context (a, b, c, d)
197
358
@@ -199,6 +360,7 @@ instance
199instance
360instance
200 ( Table context a, Table context b, Table context c, Table context d
361 ( Table context a, Table context b, Table context c, Table context d
201 , Table context e
362 , Table context e
363+ , Labelable context
202 )
364 )
203 => Table context (a, b, c, d, e)
365 => Table context (a, b, c, d, e)
204
366
@@ -206,6 +368,7 @@ instance
206instance
368instance
207 ( Table context a, Table context b, Table context c, Table context d
369 ( Table context a, Table context b, Table context c, Table context d
208 , Table context e, Table context f
370 , Table context e, Table context f
371+ , Labelable context
209 )
372 )
210 => Table context (a, b, c, d, e, f)
373 => Table context (a, b, c, d, e, f)
211
374
@@ -213,6 +376,7 @@ instance
213instance
376instance
214 ( Table context a, Table context b, Table context c, Table context d
377 ( Table context a, Table context b, Table context c, Table context d
215 , Table context e, Table context f, Table context g
378 , Table context e, Table context f, Table context g
379+ , Labelable context
216 )
380 )
217 => Table context (a, b, c, d, e, f, g)
381 => Table context (a, b, c, d, e, f, g)
218
382
src/Rel8/Table/ADT.hs+65−33
@@ -7,13 +7,13 @@
7{-# language ScopedTypeVariables #-}
7{-# language ScopedTypeVariables #-}
8{-# language StandaloneKindSignatures #-}
8{-# language StandaloneKindSignatures #-}
9{-# language TypeApplications #-}
9{-# language TypeApplications #-}
10−{-# language TypeFamilyDependencies #-}
10+{-# language TypeFamilies #-}
11{-# language UndecidableInstances #-}
11{-# language UndecidableInstances #-}
12{-# language UndecidableSuperClasses #-}
12{-# language UndecidableSuperClasses #-}
13
13
14module Rel8.Table.ADT
14module Rel8.Table.ADT
15 ( ADT( ADT )
15 ( ADT( ADT )
16− , ADTable
16+ , ADTable, fromADT, toADT
17 , BuildableADT
17 , BuildableADT
18 , BuildADT, buildADT
18 , BuildADT, buildADT
19 , ConstructableADT
19 , ConstructableADT
@@ -27,7 +27,9 @@ where
27
27
28-- base
28-- base
29import Data.Kind ( Constraint, Type )
29import Data.Kind ( Constraint, Type )
30+import Data.Proxy ( Proxy( Proxy ) )
31+import Data.Type.Equality ( (:~:)( Refl ) )
30−import GHC.Generics ( Generic, from, to )
32+import GHC.Generics ( Generic, Rep, from, to )
31import GHC.TypeLits ( Symbol )
33import GHC.TypeLits ( Symbol )
32import Prelude
34import Prelude
33
35
@@ -44,65 +46,95 @@ import Rel8.Generic.Construction
44 , GGName, ggname
46 , GGName, ggname
45 , GGAggregate, ggaggregate
47 , GGAggregate, ggaggregate
46 )
48 )
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 )
48import Rel8.Generic.Rel8able
51import Rel8.Generic.Rel8able
49 ( Rel8able
52 ( Rel8able
50 , GRep, GColumns, gfromColumns, gtoColumns
53 , GRep, GColumns, gfromColumns, gtoColumns
51− , GFromExprs, gfromResult, gtoResult
54+ , greify, gunreify
52− , TSerialize, deserialize, serialize
53 )
55 )
54import qualified Rel8.Generic.Table.ADT as G
56import qualified Rel8.Generic.Table.ADT as G
55import qualified Rel8.Kind.Algebra as K
57import qualified Rel8.Kind.Algebra as K
58+import Rel8.Schema.Context ( Col )
56import Rel8.Schema.HTable ( HTable )
59import Rel8.Schema.HTable ( HTable )
57import qualified Rel8.Schema.Kind as K
60import qualified Rel8.Schema.Kind as K
58import Rel8.Schema.Name ( Name )
61import Rel8.Schema.Name ( Name )
62+import Rel8.Schema.Reify ( Col( Reify ), Reify, hreify, hunreify )
59import Rel8.Schema.Result ( Result )
63import 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+ )
61
69
62
70
63type ADT :: K.Rel8able -> K.Rel8able
71type ADT :: K.Rel8able -> K.Rel8able
64−newtype ADT t context = ADT (GColumnsADT t context)
72+newtype ADT t context = ADT (GColumnsADT t (Col context))
65
73
66
74
67instance ADTable t => Rel8able (ADT t) where
75instance ADTable t => Rel8able (ADT t) where
68 type GColumns (ADT t) = GColumnsADT t
76 type GColumns (ADT t) = GColumnsADT t
69− type GFromExprs (ADT t) = t Result
70
77
71− gfromColumns _ = ADT
78+ gfromColumns = ADT
72− gtoColumns _ (ADT a) = a
79+ gtoColumns (ADT a) = a
80+
81+ greify (ADT a) = ADT (hreify a)
82+ gunreify (ADT a) = ADT (hunreify a)
73
83
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)
83
84
84− gtoResult =
85+instance (ADTable t, context ~ Result) => Generic (ADT t context) where
85− G.gtoResultADT
86+ type Rep (ADT t context) = Rep (t context)
86− @TSerialize
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))
87 @TColumns
105 @TColumns
88− @(Eval (ADTRep t Expr))
106+ (\(Reify a) -> a)
89− @(Eval (ADTRep t Result))
107+ Reify
90− (\(_ :: proxy x) -> serialize @_ @x) .
108+ toColumns .
91− from .
109+ grecord @(Rep (t (Reify Result))) .
92− Record
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
93
119
94
120
95type ADTable :: K.Rel8able -> Constraint
121type ADTable :: K.Rel8able -> Constraint
96class
122class
97− ( Generic (Record (t Result))
123+ ( Generic (t Result)
98 , HTable (GColumnsADT t)
124 , 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)
100 )
129 )
101 => ADTable t
130 => ADTable t
102instance
131instance
103− ( Generic (Record (t Result))
132+ ( Generic (t Result)
104 , HTable (GColumnsADT t)
133 , 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)
106 )
138 )
107 => ADTable t
139 => ADTable t
108
140
@@ -170,4 +202,4 @@ type instance Eval (ADTRep t context) = GRep t context
170
202
171
203
172type GColumnsADT :: K.Rel8able -> K.HTable
204type 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 ) )
16import Prelude
16import Prelude
17
17
18-- rel8
18-- rel8
19−import Rel8.Aggregate ( Aggregate, Aggregates )
19+import Rel8.Aggregate ( Aggregate, Aggregates, Col( A ) )
20−import Rel8.Expr ( Expr )
20+import Rel8.Expr ( Expr, Col( E ) )
21import Rel8.Expr.Aggregate
21import Rel8.Expr.Aggregate
22 ( groupByExpr
22 ( groupByExpr
23 , slistAggExpr
23 , slistAggExpr
@@ -26,8 +26,8 @@ import Rel8.Expr.Aggregate
26import Rel8.Schema.Dict ( Dict( Dict ) )
26import Rel8.Schema.Dict ( Dict( Dict ) )
27import Rel8.Schema.HTable ( HTable, hfield, htabulate )
27import Rel8.Schema.HTable ( HTable, hfield, htabulate )
28import Rel8.Schema.HTable.Vectorize ( hvectorize )
28import Rel8.Schema.HTable.Vectorize ( hvectorize )
29−import Rel8.Schema.Null ( Sql )
29+import Rel8.Schema.Spec ( SSpec( SSpec, info ) )
30−import Rel8.Schema.Spec ( Spec( Spec, info ) )
30+import Rel8.Schema.Spec.ConstrainDBType ( ConstrainDBType )
31import Rel8.Table ( toColumns, fromColumns )
31import Rel8.Table ( toColumns, fromColumns )
32import Rel8.Table.Eq ( EqTable, eqTable )
32import Rel8.Table.Eq ( EqTable, eqTable )
33import Rel8.Table.List ( ListTable )
33import Rel8.Table.List ( ListTable )
@@ -37,28 +37,17 @@ import Rel8.Type.Eq ( DBEq )
37
37
38-- | Group equal tables together. This works by aggregating each column in the
38-- | Group equal tables together. This works by aggregating each column in the
39-- given table with 'groupByExpr'.
39-- 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−-- @
52groupBy :: forall exprs aggregates. (EqTable exprs, Aggregates aggregates exprs)
40groupBy :: forall exprs aggregates. (EqTable exprs, Aggregates aggregates exprs)
53 => exprs -> aggregates
41 => exprs -> aggregates
54groupBy = fromColumns . hgroupBy (eqTable @exprs) . toColumns
42groupBy = fromColumns . hgroupBy (eqTable @exprs) . toColumns
55
43
56
44
45+hgroupBy :: HTable t
57−hgroupBy :: HTable t => t (Dict (Sql DBEq)) -> t Expr -> t Aggregate
46+ => t (Dict (ConstrainDBType DBEq)) -> t (Col Expr) -> t (Col Aggregate)
58−hgroupBy eqs exprs = htabulate $ \field ->
47+hgroupBy eqs exprs = fromColumns $ htabulate $ \field ->
59 case hfield eqs field of
48 case hfield eqs field of
60 Dict -> case hfield exprs field of
49 Dict -> case hfield exprs field of
61− expr -> groupByExpr expr
50+ E expr -> A $ groupByExpr expr
62
51
63
52
64-- | Aggregate rows into a single row containing an array of all aggregated
53-- | Aggregate rows into a single row containing an array of all aggregated
@@ -71,22 +60,22 @@ hgroupBy eqs exprs = htabulate $ \field ->
71-- items:
60-- items:
72--
61--
73-- @
62-- @
74−-- ordersWithItems :: Query (Order Expr, ListTable Expr (Item Expr))
63+-- ordersWithItems :: Query (Order Expr, ListTable (Item Expr))
75-- ordersWithItems = do
64-- ordersWithItems = do
76-- order <- each orderSchema
65-- order <- each orderSchema
77-- items <- aggregate $ listAgg <$> itemsFromOrder order
66-- items <- aggregate $ listAgg <$> itemsFromOrder order
78-- return (order, items)
67-- return (order, items)
79-- @
68-- @
80−listAgg :: Aggregates aggregates exprs => exprs -> ListTable Aggregate aggregates
69+listAgg :: Aggregates aggregates exprs => exprs -> ListTable aggregates
81listAgg (toColumns -> exprs) = fromColumns $
70listAgg (toColumns -> exprs) = fromColumns $
82 hvectorize
71 hvectorize
83− (\Spec {info} (Identity a) -> slistAggExpr info a)
72+ (\SSpec {info} (Identity (E a)) -> A $ slistAggExpr info a)
84 (pure exprs)
73 (pure exprs)
85
74
86
75
87-- | Like 'listAgg', but the result is guaranteed to be a non-empty list.
76-- | 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
89nonEmptyAgg (toColumns -> exprs) = fromColumns $
78nonEmptyAgg (toColumns -> exprs) = fromColumns $
90 hvectorize
79 hvectorize
91− (\Spec {info} (Identity a) -> snonEmptyAggExpr info a)
80+ (\SSpec {info} (Identity (E a)) -> A $ snonEmptyAggExpr info a)
92 (pure exprs)
81 (pure exprs)
src/Rel8/Table/Bool.hs+6−5
@@ -13,7 +13,7 @@ where
13import Prelude
13import Prelude
14
14
15-- rel8
15-- rel8
16−import Rel8.Expr ( Expr )
16+import Rel8.Expr ( Expr, Col( E, unE ) )
17import Rel8.Expr.Bool ( boolExpr, caseExpr )
17import Rel8.Expr.Bool ( boolExpr, caseExpr )
18import Rel8.Expr.Null ( isNull, unsafeUnnullify )
18import Rel8.Expr.Null ( isNull, unsafeUnnullify )
19import Rel8.Schema.HTable ( htabulate, hfield )
19import Rel8.Schema.HTable ( htabulate, hfield )
@@ -28,7 +28,8 @@ bool :: Table Expr a => a -> a -> Expr Bool -> a
28bool (toColumns -> false) (toColumns -> true) condition =
28bool (toColumns -> false) (toColumns -> true) condition =
29 fromColumns $ htabulate $ \field ->
29 fromColumns $ htabulate $ \field ->
30 case (hfield false field, hfield true field) of
30 case (hfield false field, hfield true field) of
31+ (E falseExpr, E trueExpr) ->
31− (falseExpr, trueExpr) -> boolExpr falseExpr trueExpr condition
32+ E (boolExpr falseExpr trueExpr condition)
32{-# INLINABLE bool #-}
33{-# INLINABLE bool #-}
33
34
34
35
@@ -38,9 +39,9 @@ bool (toColumns -> false) (toColumns -> true) condition =
38case_ :: Table Expr a => [(Expr Bool, a)] -> a -> a
39case_ :: Table Expr a => [(Expr Bool, a)] -> a -> a
39case_ (map (fmap toColumns) -> branches) (toColumns -> fallback) =
40case_ (map (fmap toColumns) -> branches) (toColumns -> fallback) =
40 fromColumns $ htabulate $ \field -> case hfield fallback field of
41 fromColumns $ htabulate $ \field -> case hfield fallback field of
41− fallbackExpr ->
42+ E fallbackExpr ->
42− case map (fmap (`hfield` field)) branches of
43+ case map (fmap (unE . (`hfield` field))) branches of
43− branchExprs -> caseExpr branchExprs fallbackExpr
44+ branchExprs -> E (caseExpr branchExprs fallbackExpr)
44
45
45
46
46-- | Like 'maybe', but to eliminate @null@.
47-- | 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
18 ( EitherTable(..)
18 ( EitherTable(..)
19 , eitherTable, leftTable, rightTable
19 , eitherTable, leftTable, rightTable
20 , isLeftTable, isRightTable
20 , isLeftTable, isRightTable
21− , aggregateEitherTable
22 , nameEitherTable
21 , nameEitherTable
23 )
22 )
24where
23where
25
24
26-- base
25-- base
26+import Control.Applicative ( liftA2 )
27import Data.Bifunctor ( Bifunctor, bimap )
27import Data.Bifunctor ( Bifunctor, bimap )
28−import Data.Functor.Identity ( Identity( Identity ) )
28+import Data.Functor.Identity ( runIdentity )
29import Data.Kind ( Type )
29import Data.Kind ( Type )
30import Prelude hiding ( undefined )
30import Prelude hiding ( undefined )
31
31
32−-- comonad
33−import Control.Comonad ( extract )
34−
35-- rel8
32-- rel8
36−import Rel8.Aggregate ( Aggregate )
37import Rel8.Expr ( Expr )
33import Rel8.Expr ( Expr )
38−import Rel8.Expr.Aggregate ( groupByExpr )
39import Rel8.Expr.Serialize ( litExpr )
34import Rel8.Expr.Serialize ( litExpr )
40−import Rel8.Kind.Context ( Reifiable )
35+import Rel8.Schema.Context.Label
36+ ( Labelable
37+ , HLabelable, hlabeler, hunlabeler
38+ )
41−import Rel8.Schema.Context.Nullify ( Nullifiable )
39+import Rel8.Schema.Context.Nullify
40+ ( Nullifiable, ConstrainTag
41+ , HNullifiable, HConstrainTag
42+ , hencodeTag, hdecodeTag
43+ , hnullifier, hunnullifier
44+ )
42−import Rel8.Schema.Dict ( Dict( Dict ) )
45+import Rel8.Schema.HTable ( HTable )
43import Rel8.Schema.HTable.Either ( HEitherTable(..) )
46import Rel8.Schema.HTable.Either ( HEitherTable(..) )
44import Rel8.Schema.HTable.Identity ( HIdentity(..) )
47import Rel8.Schema.HTable.Identity ( HIdentity(..) )
45import Rel8.Schema.HTable.Label ( hlabel, hunlabel )
48import Rel8.Schema.HTable.Label ( hlabel, hunlabel )
46−import qualified Rel8.Schema.Kind as K
49+import Rel8.Schema.HTable.Nullify ( hnullify, hunnullify )
47import Rel8.Schema.Name ( Name )
50import Rel8.Schema.Name ( Name )
48import Rel8.Table
51import Rel8.Table
49 ( Table, Columns, Context, fromColumns, toColumns
52 ( Table, Columns, Context, fromColumns, toColumns
50− , FromExprs, fromResult, toResult
53+ , reify, unreify
51− , Transpose
52 )
54 )
53import Rel8.Table.Bool ( bool )
55import Rel8.Table.Bool ( bool )
54import Rel8.Table.Eq ( EqTable, eqTable )
56import Rel8.Table.Eq ( EqTable, eqTable )
55−import Rel8.Table.Nullify ( Nullify, aggregateNullify, guard )
56import Rel8.Table.Ord ( OrdTable, ordTable )
57import Rel8.Table.Ord ( OrdTable, ordTable )
57−import Rel8.Table.Projection ( Biprojectable, Projectable, biproject, project )
58+import Rel8.Table.Recontextualize ( Recontextualize )
58−import Rel8.Table.Serialize ( ToExprs )
59+import Rel8.Table.Serialize ( FromExprs, ToExprs, fromResult, toResult )
60+import Rel8.Table.Tag ( Tag(..), fromExpr, fromName )
59import Rel8.Table.Undefined ( undefined )
61import Rel8.Table.Undefined ( undefined )
60import Rel8.Type.Tag ( EitherTag( IsLeft, IsRight ), isLeft, isRight )
62import Rel8.Type.Tag ( EitherTag( IsLeft, IsRight ), isLeft, isRight )
61
63
@@ -70,162 +72,119 @@ import Data.Functor.Bind ( Bind, (>>-) )
70--
72--
71-- An @EitherTable@ is operationally the same as Haskell's 'Either' type, but
73-- An @EitherTable@ is operationally the same as Haskell's 'Either' type, but
72-- adapted to work with Rel8.
74-- adapted to work with Rel8.
73−type EitherTable :: K.Context -> Type -> Type -> Type
75+type EitherTable :: Type -> Type -> Type
74−data EitherTable context a b = EitherTable
76+data EitherTable a b = EitherTable
75− { tag :: context EitherTag
77+ { tag :: Tag "isRight" EitherTag
76− , left :: Nullify context a
78+ , left :: a
77− , right :: Nullify context b
79+ , right :: b
78 }
80 }
79 deriving stock Functor
81 deriving stock Functor
80
82
81
83
82−instance Biprojectable (EitherTable context) where
84+instance Bifunctor EitherTable where
83− biproject f g (EitherTable tag a b) =
85+ bimap f g (EitherTable tag a b) = EitherTable tag (f a) (g b)
84− EitherTable tag (project f a) (project g b)
85−
86
86
87−instance Nullifiable context => Bifunctor (EitherTable context) where
88− bimap f g (EitherTable tag a b) = EitherTable tag (fmap f a) (fmap g b)
89
87
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
96 EitherTable tag l1 f <.> EitherTable tag' l2 a =
89 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)
98
91
99
92
100−instance (context ~ Expr, Table Expr a) => Applicative (EitherTable context a) where
93+instance Table Expr a => Applicative (EitherTable a) where
101 pure = rightTable
94 pure = rightTable
102 (<*>) = (<.>)
95 (<*>) = (<.>)
103
96
104
97
105−instance (context ~ Expr, Table Expr a) => Bind (EitherTable context a) where
98+instance Table Expr a => Bind (EitherTable a) where
106− EitherTable tag l1 a >>- f = case f (extract a) of
99+ EitherTable tag l1 a >>- f = case f a of
107 EitherTable tag' l2 b ->
100 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
109
102
110
103
111−instance (context ~ Expr, Table Expr a) => Monad (EitherTable context a) where
104+instance Table Expr a => Monad (EitherTable a) where
112 (>>=) = (>>-)
105 (>>=) = (>>-)
113
106
114
107
115−instance (context ~ Expr, Table Expr a, Table Expr b) =>
108+instance (Table Expr a, Table Expr b) => Semigroup (EitherTable a b) where
116− Semigroup (EitherTable context a b)
117− where
118 a <> b = bool a b (isRightTable a)
109 a <> b = bool a b (isRightTable a)
119
110
120
111
121instance
112instance
122 ( Table context a, Table context b
113 ( Table context a, Table context b
123− , Reifiable context, context ~ context'
114+ , Labelable context, Nullifiable context, ConstrainTag context EitherTag
124− )
115+ ) =>
125− => Table context' (EitherTable context a b)
116+ Table context (EitherTable 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)
167 where
117 where
168− eqTable = HEitherTable
118+ type Columns (EitherTable a b) = HEitherTable (Columns a) (Columns b)
169− { htag = hlabel (HIdentity Dict)
119+ type Context (EitherTable a b) = Context a
170− , hleft = hlabel (eqTable @(Nullify context a))
171− , hright = hlabel (eqTable @(Nullify context b))
172− }
173
120
121+ toColumns = toColumns2 toColumns toColumns
122+ fromColumns = fromColumns2 fromColumns fromColumns
123+ reify = liftA2 bimap reify reify
124+ unreify = liftA2 bimap unreify unreify
174
125
175−instance (OrdTable a, OrdTable b, context ~ Expr) =>
126+
176− OrdTable (EitherTable context a b)
127+instance
177− where
128+ ( Nullifiable from, Labelable from, ConstrainTag from EitherTag
178− ordTable = HEitherTable
129+ , Nullifiable to, Labelable to, ConstrainTag to EitherTag
179− { htag = hlabel (HIdentity Dict)
130+ , Recontextualize from to a1 b1
180− , hleft = hlabel (ordTable @(Nullify context a))
131+ , Recontextualize from to a2 b2
181− , hright = hlabel (ordTable @(Nullify context b))
132+ )
182− }
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))
183
138
184
139
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+
185−instance (ToExprs exprs1 a, ToExprs exprs2 b, x ~ EitherTable Expr exprs1 exprs2) =>
147+instance (ToExprs exprs1 a, ToExprs exprs2 b, x ~ EitherTable exprs1 exprs2) =>
186 ToExprs x (Either a b)
148 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)
187
156
188
157
189-- | Test if an 'EitherTable' is a 'leftTable'.
158-- | Test if an 'EitherTable' is a 'leftTable'.
190−isLeftTable :: EitherTable Expr a b -> Expr Bool
159+isLeftTable :: EitherTable a b -> Expr Bool
191−isLeftTable EitherTable {tag} = isLeft tag
160+isLeftTable = isLeft . expr . tag
192
161
193
162
194-- | Test if an 'EitherTable' is a 'rightTable'.
163-- | Test if an 'EitherTable' is a 'rightTable'.
195−isRightTable :: EitherTable Expr a b -> Expr Bool
164+isRightTable :: EitherTable a b -> Expr Bool
196−isRightTable EitherTable {tag} = isRight tag
165+isRightTable = isRight . expr . tag
197
166
198
167
199-- | Pattern match/eliminate an 'EitherTable', by providing mappings from a
168-- | Pattern match/eliminate an 'EitherTable', by providing mappings from a
200-- 'leftTable' and 'rightTable'.
169-- 'leftTable' and 'rightTable'.
201eitherTable :: Table Expr c
170eitherTable :: Table Expr c
202− => (a -> c) -> (b -> c) -> EitherTable Expr a b -> c
171+ => (a -> c) -> (b -> c) -> EitherTable a b -> c
203eitherTable f g EitherTable {tag, left, right} =
172eitherTable 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))
205
174
206
175
207-- | Construct a left 'EitherTable'. Like 'Left'.
176-- | Construct a left 'EitherTable'. Like 'Left'.
208−leftTable :: Table Expr b => a -> EitherTable Expr a b
177+leftTable :: Table Expr b => a -> EitherTable a b
209−leftTable a = EitherTable (litExpr IsLeft) (pure a) undefined
178+leftTable a = EitherTable (fromExpr (litExpr IsLeft)) a undefined
210
179
211
180
212-- | Construct a right 'EitherTable'. Like 'Right'.
181-- | Construct a right 'EitherTable'. Like 'Right'.
213−rightTable :: Table Expr a => b -> EitherTable Expr a b
182+rightTable :: Table Expr a => b -> EitherTable a b
183+rightTable = rightTableWith undefined
184+
185+
186+rightTableWith :: a -> b -> EitherTable a b
214−rightTable = EitherTable (litExpr IsRight) undefined . pure
187+rightTableWith = EitherTable (fromExpr (litExpr IsRight))
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− }
229
188
230
189
231-- | Construct a 'EitherTable' in the 'Name' context. This can be useful if you
190-- | Construct a 'EitherTable' in the 'Name' context. This can be useful if you
@@ -239,5 +198,51 @@ nameEitherTable
239 -- ^ Names of the columns in the @a@ table.
198 -- ^ Names of the columns in the @a@ table.
240 -> b
199 -> b
241 -- ^ Names of the columns in the @b@ table.
200 -- ^ Names of the columns in the @b@ table.
242− -> EitherTable Name a b
201+ -> EitherTable a b
243−nameEitherTable tag left right = EitherTable tag (pure left) (pure right)
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 @@
11{-# language TypeFamilies #-}
11{-# language TypeFamilies #-}
12{-# language TypeOperators #-}
12{-# language TypeOperators #-}
13{-# language UndecidableInstances #-}
13{-# language UndecidableInstances #-}
14+{-# language UndecidableSuperClasses #-}
14{-# language ViewPatterns #-}
15{-# language ViewPatterns #-}
15
16
16module Rel8.Table.Eq
17module Rel8.Table.Eq
@@ -27,16 +28,24 @@ import GHC.Generics ( Rep )
27import Prelude
28import Prelude
28
29
29-- rel8
30-- rel8
30−import Rel8.Expr ( Expr )
31+import Rel8.Expr ( Expr, Col( E ) )
31import Rel8.Expr.Bool ( (||.), (&&.) )
32import Rel8.Expr.Bool ( (||.), (&&.) )
32import Rel8.Expr.Eq ( (==.), (/=.) )
33import Rel8.Expr.Eq ( (==.), (/=.) )
33import Rel8.FCF ( Eval, Exp )
34import Rel8.FCF ( Eval, Exp )
34import Rel8.Generic.Record ( Record )
35import 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 )
36import Rel8.Schema.Dict ( Dict( Dict ) )
41import Rel8.Schema.Dict ( Dict( Dict ) )
37−import Rel8.Schema.HTable ( htabulateA, hfield )
42+import Rel8.Schema.HTable
43+ ( HTable, HConstrainTable
44+ , htabulateA, hfield, hdicts
45+ )
38−import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) )
46+import Rel8.Schema.HTable.Identity ( HIdentity( HType ) )
39import Rel8.Schema.Null ( Sql )
47import Rel8.Schema.Null ( Sql )
48+import Rel8.Schema.Spec.ConstrainDBType ( ConstrainDBType, nullifier )
40import Rel8.Table ( Table, Columns, toColumns, TColumns )
49import Rel8.Table ( Table, Columns, toColumns, TColumns )
41import Rel8.Type.Eq ( DBEq )
50import Rel8.Type.Eq ( DBEq )
42
51
@@ -46,14 +55,22 @@ import Rel8.Type.Eq ( DBEq )
46-- means "all columns in a 'Table' have an instance of 'DBEq'".
55-- means "all columns in a 'Table' have an instance of 'DBEq'".
47type EqTable :: Type -> Constraint
56type EqTable :: Type -> Constraint
48class Table Expr a => EqTable a where
57class Table Expr a => EqTable a where
49− eqTable :: Columns a (Dict (Sql DBEq))
58+ eqTable :: Columns a (Dict (ConstrainDBType DBEq))
50
59
51 default eqTable ::
60 default eqTable ::
61+ ( KnownAlgebra (GAlgebra (Rep (Record a)))
52− ( GTable TEqTable TColumns (Rep (Record a))
62+ , Eval (GGTable (GAlgebra (Rep (Record a))) TEqTable TColumns (Dict (ConstrainDBType DBEq)) (Rep (Record a)))
53− , Columns a ~ GColumns TColumns (Rep (Record a))
63+ , Columns a ~ Eval (GGColumns (GAlgebra (Rep (Record a))) TColumns (Rep (Record a)))
54 )
64 )
55− => Columns a (Dict (Sql DBEq))
65+ => Columns a (Dict (ConstrainDBType DBEq))
56− eqTable = gtable @TEqTable @TColumns @(Rep (Record a)) table
66+ eqTable =
67+ ggtable
68+ @(GAlgebra (Rep (Record a)))
69+ @TEqTable
70+ @TColumns
71+ @(Rep (Record a))
72+ table
73+ nullifier
57 where
74 where
58 table (_ :: proxy x) = eqTable @x
75 table (_ :: proxy x) = eqTable @x
59
76
@@ -62,8 +79,18 @@ data TEqTable :: Type -> Exp Constraint
62type instance Eval (TEqTable a) = EqTable a
79type instance Eval (TEqTable a) = EqTable a
63
80
64
81
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+
65instance Sql DBEq a => EqTable (Expr a) where
92instance Sql DBEq a => EqTable (Expr a) where
66− eqTable = HIdentity Dict
93+ eqTable = HType Dict
67
94
68
95
69instance (EqTable a, EqTable b) => EqTable (a, b)
96instance (EqTable a, EqTable b) => EqTable (a, b)
@@ -97,7 +124,7 @@ instance
97(toColumns -> as) ==: (toColumns -> bs) =
124(toColumns -> as) ==: (toColumns -> bs) =
98 foldl1' (&&.) $ getConst $ htabulateA $ \field ->
125 foldl1' (&&.) $ getConst $ htabulateA $ \field ->
99 case (hfield as field, hfield bs field) of
126 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
101 Dict -> Const (pure (a ==. b))
128 Dict -> Const (pure (a ==. b))
102infix 4 ==:
129infix 4 ==:
103
130
@@ -109,7 +136,7 @@ infix 4 ==:
109(toColumns -> as) /=: (toColumns -> bs) =
136(toColumns -> as) /=: (toColumns -> bs) =
110 foldl1' (||.) $ getConst $ htabulateA $ \field ->
137 foldl1' (||.) $ getConst $ htabulateA $ \field ->
111 case (hfield as field, hfield bs field) of
138 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
113 Dict -> Const (pure (a /=. b))
140 Dict -> Const (pure (a /=. b))
114infix 4 /=:
141infix 4 /=:
115
142
src/Rel8/Table/HKD.hs+105−65
@@ -13,7 +13,7 @@
13
13
14module Rel8.Table.HKD
14module Rel8.Table.HKD
15 ( HKD( HKD )
15 ( HKD( HKD )
16− , HKDable
16+ , HKDable, fromHKD, toHKD, HKDT(..)
17 , BuildableHKD
17 , BuildableHKD
18 , BuildHKD, buildHKD
18 , BuildHKD, buildHKD
19 , ConstructableHKD
19 , ConstructableHKD
@@ -27,6 +27,9 @@ where
27
27
28-- base
28-- base
29import Data.Kind ( Constraint, Type )
29import Data.Kind ( Constraint, Type )
30+import Data.Proxy ( Proxy( Proxy ) )
31+import Data.Type.Equality ( (:~:)( Refl ) )
32+import Data.Void ( Void )
30import GHC.Generics ( Generic, Rep, from, to )
33import GHC.Generics ( Generic, Rep, from, to )
31import GHC.TypeLits ( Symbol )
34import GHC.TypeLits ( Symbol )
32import Prelude
35import Prelude
@@ -46,118 +49,129 @@ import Rel8.Generic.Construction
46 , GGName, ggname
49 , GGName, ggname
47 , GGAggregate, ggaggregate
50 , GGAggregate, ggaggregate
48 )
51 )
49−import Rel8.Generic.Map ( GMap )
52+import Rel8.Generic.Map ( GMap, GMappable, gmap, gunmap )
50−import Rel8.Generic.Record
51− ( GRecord, GRecordable, grecord, gunrecord
53+import Rel8.Generic.Record ( GRecord, GRecordable, grecord, gunrecord )
52− , Record( Record ), unrecord
53− )
54import Rel8.Generic.Rel8able
54import Rel8.Generic.Rel8able
55 ( Rel8able
55 ( Rel8able
56 , GColumns, gfromColumns, gtoColumns
56 , GColumns, gfromColumns, gtoColumns
57− , GFromExprs, gfromResult, gtoResult
57+ , greify, gunreify
58+ , TUnreifyContext
58 )
59 )
59import Rel8.Generic.Table
60import Rel8.Generic.Table
60− ( GGSerialize, GGColumns, GAlgebra, ggfromResult, ggtoResult
61+ ( GGTable, GGColumns, GGContext, ggfromColumns, ggtoColumns
62+ , GAlgebra
61 )
63 )
62−import Rel8.Generic.Table.Record ( GTable, GContext )
64+import Rel8.Schema.Context ( Col )
63−import qualified Rel8.Generic.Table.Record as G
64import qualified Rel8.Schema.Kind as K
65import qualified Rel8.Schema.Kind as K
65import Rel8.Schema.HTable ( HTable )
66import Rel8.Schema.HTable ( HTable )
66import Rel8.Schema.Name ( Name )
67import Rel8.Schema.Name ( Name )
68+import Rel8.Schema.Reify ( Col( Reify ), Reify, hreify, hunreify, notReify )
67import Rel8.Schema.Result ( Result )
69import Rel8.Schema.Result ( Result )
68import Rel8.Table
70import Rel8.Table
71+ ( Table, Columns, Context, Unreify
69− ( Table, fromColumns, toColumns, fromResult, toResult
72+ , fromColumns, toColumns, reify, unreify
70− , TTable, TColumns, TContext
73+ , TTable, TColumns, TUnreify
71− , TSerialize
72 )
74 )
75+import Rel8.Table.Serialize ( ToExprs, fromResult, toResult )
73
76
74
77
75type GColumnsHKD :: Type -> K.HTable
78type GColumnsHKD :: Type -> K.HTable
76type GColumnsHKD a =
79type 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))))
78
81
79
82
80type HKD :: Type -> K.Rel8able
83type HKD :: Type -> K.Rel8able
81−newtype HKD a f = HKD (GColumnsHKD a f)
84+newtype HKD a f = HKD (GColumnsHKD a (Col f))
82
85
83
86
84instance HKDable a => Rel8able (HKD a) where
87instance HKDable a => Rel8able (HKD a) where
85 type GColumns (HKD a) = GColumnsHKD a
88 type GColumns (HKD a) = GColumnsHKD a
86− type GFromExprs (HKD a) = a
87
89
88− gfromColumns _ = HKD
90+ gfromColumns = HKD
89− gtoColumns _ (HKD a) = a
91+ gtoColumns (HKD a) = a
90
92
91− gfromResult =
93+ greify (HKD a) = HKD (hreify a)
92− unrecord .
94+ gunreify (HKD a) = HKD (hunreify a)
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
112
95
113
96
114instance
97instance
98+ ( KnownAlgebra (GAlgebra (Rep a))
99+ , HTable (GColumnsHKD a)
115− ( GTable (TTable f) TColumns (GRecord (GMap (TColumn f) (Rep a)))
100+ , Eval (GGTable (GAlgebra (Rep a)) (TTable (Reify f)) TColumns (Col (Reify f)) (GRecord (GMap (TColumn (Reify f)) (Rep a))))
116− , G.GColumns TColumns (GRecord (GMap (TColumn f) (Rep a))) ~ GColumnsHKD a
101+ , Eval (GGColumns (GAlgebra (Rep a)) TColumns (GRecord (GMap (TColumn (Reify f)) (Rep a)))) ~ GColumnsHKD a
117− , GContext TContext (GRecord (GMap (TColumn f) (Rep a))) ~ f
102+ , Eval (GGContext (GAlgebra (Rep a)) TUnreifyContext (GRecord (GMap (TColumn (Reify f)) (Rep a)))) ~ f
118− , GRecordable (GMap (TColumn f) (Rep a))
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)
119 )
106 )
120 => Generic (HKD a f)
107 => Generic (HKD a f)
121 where
108 where
122 type Rep (HKD a f) = GMap (TColumn f) (Rep a)
109 type Rep (HKD a f) = GMap (TColumn f) (Rep a)
123
110
124 from =
111 from =
112+ gmap @(TTable (Reify f)) (Proxy @TUnreify) (unreify Refl) .
125− gunrecord @(GMap (TColumn f) (Rep a)) .
113+ gunrecord @(GMap (TColumn (Reify f)) (Rep a)) .
126− G.gfromColumns
114+ ggfromColumns
115+ @(GAlgebra (Rep a))
127− @(TTable f)
116+ @(TTable (Reify f))
128 @TColumns
117 @TColumns
118+ (\(Reify a) -> a)
119+ Reify
129 fromColumns .
120 fromColumns .
121+ hreify .
130 (\(HKD a) -> a)
122 (\(HKD a) -> a)
131
123
132 to =
124 to =
133 HKD .
125 HKD .
134− G.gtoColumns
126+ hunreify .
127+ ggtoColumns
128+ @(GAlgebra (Rep a))
135− @(TTable f)
129+ @(TTable (Reify f))
136 @TColumns
130 @TColumns
131+ (\(Reify a) -> a)
132+ Reify
137 toColumns .
133 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
139
153
140
154
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
150instance
155instance
151− ( Generic (Record a)
156+ ( Table Expr (HKD a Expr)
152− , HTable (GColumns (HKD a))
157+ , Columns (HKD a Expr) ~ GColumns (HKD a)
153− , KnownAlgebra (GAlgebra (Rep a))
158+ , HKDable a
154− , Eval (GGSerialize (GAlgebra (Rep a)) TSerialize TColumns (Eval (HKDRep a Expr)) (Eval (HKDRep a Result)))
159+ , x ~ HKD a Expr
155− , GRecord (GMap (TColumn Result) (Rep a)) ~ Rep (Record a)
156 )
160 )
161+ => ToExprs x (HKDT a)
162+ where
163+ toResult = (\(HKD a) -> a) . toHKD . (\(HKDT a) -> a)
164+ fromResult = HKDT . fromHKD . HKD
165+
166+
157− => HKDable a
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
158
173
159
174
160−type Top_ :: Constraint
161class Top_
175class Top_
162instance Top_
176instance Top_
163
177
@@ -166,6 +180,32 @@ data Top :: Type -> Exp Constraint
166type instance Eval (Top _) = Top_
180type instance Eval (Top _) = Top_
167
181
168
182
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+
169type BuildableHKD :: Type -> Symbol -> Constraint
209type BuildableHKD :: Type -> Symbol -> Constraint
170class GGBuildable (GAlgebra (Rep a)) name (HKDRep a) => BuildableHKD a name
210class GGBuildable (GAlgebra (Rep a)) name (HKDRep a) => BuildableHKD a name
171instance GGBuildable (GAlgebra (Rep a)) name (HKDRep a) => BuildableHKD a name
211instance GGBuildable (GAlgebra (Rep a)) name (HKDRep a) => BuildableHKD a name
src/Rel8/Table/List.hs+55−65
@@ -1,4 +1,3 @@
1−{-# language DataKinds #-}
2{-# language FlexibleContexts #-}
1{-# language FlexibleContexts #-}
3{-# language FlexibleInstances #-}
2{-# language FlexibleInstances #-}
4{-# language MultiParamTypeClasses #-}
3{-# language MultiParamTypeClasses #-}
@@ -11,36 +10,30 @@
11
10
12module Rel8.Table.List
11module Rel8.Table.List
13 ( ListTable(..)
12 ( ListTable(..)
14− , ($*)
15− , listTable
16− , nameListTable
13+ , listTable, nameListTable
17 )
14 )
18where
15where
19
16
20-- base
17-- base
21import Data.Functor.Identity ( Identity( Identity ) )
18import Data.Functor.Identity ( Identity( Identity ) )
22import Data.Kind ( Type )
19import Data.Kind ( Type )
20+import Data.Type.Equality ( (:~:)( Refl ) )
23import Prelude
21import Prelude
24
22
25-- rel8
23-- rel8
26−import Rel8.Expr ( Expr )
24+import Rel8.Expr ( Expr, Col( E, unE ) )
27import Rel8.Expr.Array ( sappend, sempty, slistOf )
25import Rel8.Expr.Array ( sappend, sempty, slistOf )
28import Rel8.Schema.Dict ( Dict( Dict ) )
26import Rel8.Schema.Dict ( Dict( Dict ) )
29import Rel8.Schema.HTable.List ( HListTable )
27import Rel8.Schema.HTable.List ( HListTable )
30−import Rel8.Schema.HTable.Vectorize
28+import Rel8.Schema.HTable.Vectorize ( happend, hempty, hvectorize )
31− ( hvectorize, hunvectorize
32− , happend, hempty
33− , hproject, hcolumn
34− )
35−import qualified Rel8.Schema.Kind as K
29+import Rel8.Schema.Name ( Col( N ), Name( Name ) )
36−import Rel8.Schema.Name ( Name( Name ) )
37import Rel8.Schema.Null ( Nullity( Null, NotNull ) )
30import Rel8.Schema.Null ( Nullity( Null, NotNull ) )
38−import Rel8.Schema.Result ( vectorizer, unvectorizer )
31+import Rel8.Schema.Spec ( SSpec(..) )
39−import Rel8.Schema.Spec ( Spec(..) )
32+import Rel8.Schema.Spec.ConstrainDBType ( dbTypeDict, dbTypeNullity )
33+import Rel8.Schema.Reify ( hreify, hunreify )
40import Rel8.Table
34import Rel8.Table
41 ( Table, Context, Columns, fromColumns, toColumns
35 ( Table, Context, Columns, fromColumns, toColumns
42− , FromExprs, fromResult, toResult
36+ , reify, unreify
43− , Transpose
44 )
37 )
45import Rel8.Table.Alternative
38import Rel8.Table.Alternative
46 ( AltTable, (<|>:)
39 ( AltTable, (<|>:)
@@ -48,90 +41,87 @@ import Rel8.Table.Alternative
48 )
41 )
49import Rel8.Table.Eq ( EqTable, eqTable )
42import Rel8.Table.Eq ( EqTable, eqTable )
50import Rel8.Table.Ord ( OrdTable, ordTable )
43import Rel8.Table.Ord ( OrdTable, ordTable )
51−import Rel8.Table.Projection
44+import Rel8.Table.Recontextualize ( Recontextualize )
52− ( Projectable, Projecting, Projection, project, apply
53− )
54−import Rel8.Table.Serialize ( ToExprs )
45+import Rel8.Table.Serialize ( FromExprs, ToExprs, fromResult, toResult )
46+import Rel8.Table.Unreify ( Unreifies )
55
47
56
48
57-- | A @ListTable@ value contains zero or more instances of @a@. You construct
49-- | A @ListTable@ value contains zero or more instances of @a@. You construct
58-- @ListTable@s with 'Rel8.many' or 'Rel8.listAgg'.
50-- @ListTable@s with 'Rel8.many' or 'Rel8.listAgg'.
59−type ListTable :: K.Context -> Type -> Type
51+type ListTable :: Type -> Type
60−newtype ListTable context a =
61− ListTable (HListTable (Columns a) (Context a))
52+newtype ListTable a = ListTable (HListTable (Columns a) (Col (Context a)))
62−
63−
64−instance Projectable (ListTable context) where
65− project f (ListTable a) = ListTable (hproject (apply f) a)
66
53
67
54
68−instance (Table context a, context ~ context') =>
55+instance (Table context a, Unreifies context a) =>
69− Table context' (ListTable context a)
56+ Table context (ListTable a)
70 where
57 where
71− type Columns (ListTable context a) = HListTable (Columns a)
58+ type Columns (ListTable a) = HListTable (Columns a)
72− type Context (ListTable context a) = Context a
59+ type Context (ListTable a) = Context a
73− type FromExprs (ListTable context a) = [FromExprs a]
74− type Transpose to (ListTable context a) = ListTable to (Transpose to a)
75
60
76 fromColumns = ListTable
61 fromColumns = ListTable
77 toColumns (ListTable a) = a
62 toColumns (ListTable a) = a
78− fromResult = fmap (fromResult @_ @a) . hunvectorize unvectorizer
63+
79− toResult = hvectorize vectorizer . fmap (toResult @_ @a)
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)
80
73
81
74
82−instance (EqTable a, context ~ Expr) => EqTable (ListTable context a) where
75+instance EqTable a => EqTable (ListTable a) where
83 eqTable =
76 eqTable =
84 hvectorize
77 hvectorize
85− (\Spec {nullity} (Identity Dict) -> case nullity of
78+ (\SSpec {} (Identity dict) -> case dbTypeDict dict of
79+ Dict -> case dbTypeNullity dict of
86− Null -> Dict
80+ Null -> Dict
87− NotNull -> Dict)
81+ NotNull -> Dict)
88 (Identity (eqTable @a))
82 (Identity (eqTable @a))
89
83
90
84
91−instance (OrdTable a, context ~ Expr) => OrdTable (ListTable context a) where
85+instance OrdTable a => OrdTable (ListTable a) where
92 ordTable =
86 ordTable =
93 hvectorize
87 hvectorize
94− (\Spec {nullity} (Identity Dict) -> case nullity of
88+ (\SSpec {} (Identity dict) -> case dbTypeDict dict of
89+ Dict -> case dbTypeNullity dict of
95− Null -> Dict
90+ Null -> Dict
96− NotNull -> Dict)
91+ NotNull -> Dict)
97 (Identity (ordTable @a))
92 (Identity (ordTable @a))
98
93
99
94
100−instance (ToExprs exprs a, context ~ Expr) =>
95+type instance FromExprs (ListTable a) = [FromExprs a]
101− ToExprs (ListTable context exprs) [a]
102
96
103
97
104−instance context ~ Expr => AltTable (ListTable context) where
98+instance ToExprs exprs a => ToExprs (ListTable exprs) [a] where
105− (<|>:) = (<>)
99+ fromResult = fmap (fromResult @exprs) . fromColumns
100+ toResult = toColumns . fmap (toResult @exprs)
106
101
107
102
108−instance context ~ Expr => AlternativeTable (ListTable context) where
103+instance AltTable ListTable where
109− emptyTable = mempty
104+ (<|>:) = (<>)
110
105
111
106
112−instance (context ~ Expr, Table Expr a) => Semigroup (ListTable context a)
107+instance AlternativeTable ListTable where
113− where
108+ emptyTable = mempty
114− ListTable as <> ListTable bs = ListTable $ happend (const sappend) as bs
115
109
116
110
117−instance (context ~ Expr, Table Expr a) =>
111+instance Table Expr a => Semigroup (ListTable a) where
118− Monoid (ListTable context a)
112+ ListTable as <> ListTable bs = ListTable $
119− where
113+ happend (\_ _ (E a) (E b) -> E (sappend a b)) as bs
120− mempty = ListTable $ hempty $ \Spec {info} -> sempty info
121
114
122
115
123−-- | Project a single expression out of a 'ListTable'.
116+instance Table Expr a => Monoid (ListTable a) where
124−($*) :: Projecting a (Expr b)
117+ mempty = ListTable $ hempty $ \_ -> E . sempty
125− => Projection a (Expr b) -> ListTable Expr a -> Expr [b]
126−f $* ListTable a = hcolumn $ hproject (apply f) a
127−infixl 4 $*
128
118
129
119
130-- | Construct a @ListTable@ from a list of expressions.
120-- | 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
132listTable =
122listTable =
133 ListTable .
123 ListTable .
134− hvectorize (\Spec {info} -> slistOf info) .
124+ hvectorize (\SSpec {info} -> E . slistOf info . fmap unE) .
135 fmap toColumns
125 fmap toColumns
136
126
137
127
@@ -141,9 +131,9 @@ listTable =
141nameListTable
131nameListTable
142 :: Table Name a
132 :: Table Name a
143 => a -- ^ The names of the columns of elements of the list.
133 => a -- ^ The names of the columns of elements of the list.
144− -> ListTable Name a
134+ -> ListTable a
145nameListTable =
135nameListTable =
146 ListTable .
136 ListTable .
147− hvectorize (\_ (Identity (Name a)) -> Name a) .
137+ hvectorize (\_ (Identity (N (Name a))) -> N (Name a)) .
148 pure .
138 pure .
149 toColumns
139 toColumns
src/Rel8/Table/Maybe.hs+118−110
@@ -3,6 +3,7 @@
3{-# language DerivingStrategies #-}
3{-# language DerivingStrategies #-}
4{-# language FlexibleContexts #-}
4{-# language FlexibleContexts #-}
5{-# language FlexibleInstances #-}
5{-# language FlexibleInstances #-}
6+{-# language LambdaCase #-}
6{-# language MultiParamTypeClasses #-}
7{-# language MultiParamTypeClasses #-}
7{-# language NamedFieldPuns #-}
8{-# language NamedFieldPuns #-}
8{-# language ScopedTypeVariables #-}
9{-# language ScopedTypeVariables #-}
@@ -15,42 +16,39 @@ module Rel8.Table.Maybe
15 ( MaybeTable(..)
16 ( MaybeTable(..)
16 , maybeTable, nothingTable, justTable
17 , maybeTable, nothingTable, justTable
17 , isNothingTable, isJustTable
18 , isNothingTable, isJustTable
18− , fromMaybeTable
19 , ($?)
19 , ($?)
20− , aggregateMaybeTable
21 , nameMaybeTable
20 , nameMaybeTable
22 )
21 )
23where
22where
24
23
25-- base
24-- base
26−import Data.Functor ( ($>) )
25+import Data.Functor.Identity ( runIdentity )
27−import Data.Functor.Identity ( Identity( Identity ) )
28import Data.Kind ( Type )
26import Data.Kind ( Type )
29−import Data.Maybe ( fromMaybe, isJust )
30import Prelude hiding ( null, undefined )
27import Prelude hiding ( null, undefined )
31
28
32−-- comonad
33−import Control.Comonad ( extract )
34−
35-- rel8
29-- rel8
36−import Rel8.Aggregate ( Aggregate )
37import Rel8.Expr ( Expr )
30import Rel8.Expr ( Expr )
38−import Rel8.Expr.Aggregate ( groupByExpr )
39import Rel8.Expr.Bool ( boolExpr )
31import Rel8.Expr.Bool ( boolExpr )
40import Rel8.Expr.Null ( isNull, isNonNull, null, nullify )
32import Rel8.Expr.Null ( isNull, isNonNull, null, nullify )
41−import Rel8.Kind.Context ( Reifiable )
33+import Rel8.Schema.Context.Label
34+ ( Labelable, HLabelable, hlabeler, hunlabeler
35+ )
42−import Rel8.Schema.Dict ( Dict( Dict ) )
36+import Rel8.Schema.Context.Nullify
37+ ( Nullifiable, ConstrainTag
38+ , HNullifiable, HConstrainTag
39+ , hencodeTag, hdecodeTag
40+ , hnullifier, hunnullifier
41+ )
43−import qualified Rel8.Schema.Kind as K
42+import Rel8.Schema.HTable ( HTable )
44import Rel8.Schema.HTable.Identity ( HIdentity(..) )
43import Rel8.Schema.HTable.Identity ( HIdentity(..) )
45import Rel8.Schema.HTable.Label ( hlabel, hunlabel )
44import Rel8.Schema.HTable.Label ( hlabel, hunlabel )
46import Rel8.Schema.HTable.Maybe ( HMaybeTable(..) )
45import Rel8.Schema.HTable.Maybe ( HMaybeTable(..) )
46+import Rel8.Schema.HTable.Nullify ( hnullify, hunnullify )
47import Rel8.Schema.Name ( Name )
47import Rel8.Schema.Name ( Name )
48−import Rel8.Schema.Null ( Nullity( Null, NotNull ), Sql, nullable )
48+import Rel8.Schema.Null ( Nullify, Nullity( Null, NotNull ), Sql, nullable )
49−import qualified Rel8.Schema.Null as N
50import Rel8.Table
49import Rel8.Table
51 ( Table, Columns, Context, fromColumns, toColumns
50 ( Table, Columns, Context, fromColumns, toColumns
52− , FromExprs, fromResult, toResult
51+ , reify, unreify
53− , Transpose
54 )
52 )
55import Rel8.Table.Alternative
53import Rel8.Table.Alternative
56 ( AltTable, (<|>:)
54 ( AltTable, (<|>:)
@@ -59,12 +57,12 @@ import Rel8.Table.Alternative
59import Rel8.Table.Bool ( bool )
57import Rel8.Table.Bool ( bool )
60import Rel8.Table.Eq ( EqTable, eqTable )
58import Rel8.Table.Eq ( EqTable, eqTable )
61import Rel8.Table.Ord ( OrdTable, ordTable )
59import Rel8.Table.Ord ( OrdTable, ordTable )
62−import Rel8.Table.Projection ( Projectable, project )
60+import Rel8.Table.Recontextualize ( Recontextualize )
63−import Rel8.Table.Nullify ( Nullify, aggregateNullify, guard )
64−import Rel8.Table.Serialize ( ToExprs )
61+import Rel8.Table.Serialize ( FromExprs, ToExprs, fromResult, toResult )
62+import Rel8.Table.Tag ( Tag(..), fromExpr, fromName )
65import Rel8.Table.Undefined ( undefined )
63import Rel8.Table.Undefined ( undefined )
66import Rel8.Type ( DBType )
64import Rel8.Type ( DBType )
67−import Rel8.Type.Tag ( MaybeTag( IsJust ) )
65+import Rel8.Type.Tag ( MaybeTag )
68
66
69-- semigroupoids
67-- semigroupoids
70import Data.Functor.Apply ( Apply, (<.>) )
68import Data.Functor.Apply ( Apply, (<.>) )
@@ -79,160 +77,135 @@ import Data.Functor.Bind ( Bind, (>>-) )
79-- entirely of nulls, you can't distinguish if you matched an all null row, or
77-- entirely of nulls, you can't distinguish if you matched an all null row, or
80-- if the match failed. For this reason @MaybeTable@ contains an extra field -
78-- if the match failed. For this reason @MaybeTable@ contains an extra field -
81-- a "nullTag" - to track whether or not the outer join produced any rows.
79-- a "nullTag" - to track whether or not the outer join produced any rows.
82−type MaybeTable :: K.Context -> Type -> Type
80+type MaybeTable :: Type -> Type
83−data MaybeTable context a = MaybeTable
81+data MaybeTable a = MaybeTable
84− { tag :: context (Maybe MaybeTag)
82+ { tag :: Tag "isJust" (Maybe MaybeTag)
85− , just :: Nullify context a
83+ , just :: a
86 }
84 }
87 deriving stock Functor
85 deriving stock Functor
88
86
89
87
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
88+instance Apply MaybeTable where
95− MaybeTable tag f <.> MaybeTable tag' a = MaybeTable (tag <> tag') (f <.> a)
89+ MaybeTable tag f <.> MaybeTable tag' a = MaybeTable (tag <> tag') (f a)
96
90
97
91
98-- | Has the same behavior as the @Applicative@ instance for @Maybe@. See also:
92-- | Has the same behavior as the @Applicative@ instance for @Maybe@. See also:
99-- 'Rel8.traverseMaybeTable'.
93-- 'Rel8.traverseMaybeTable'.
100−instance context ~ Expr => Applicative (MaybeTable context) where
94+instance Applicative MaybeTable where
101 (<*>) = (<.>)
95 (<*>) = (<.>)
102 pure = justTable
96 pure = justTable
103
97
104
98
105−instance context ~ Expr => Bind (MaybeTable context) where
99+instance Bind MaybeTable where
106− MaybeTable tag a >>- f = case f (extract a) of
100+ MaybeTable tag a >>- f = case f a of
107 MaybeTable tag' b -> MaybeTable (tag <> tag') b
101 MaybeTable tag' b -> MaybeTable (tag <> tag') b
108
102
109
103
110-- | Has the same behavior as the @Monad@ instance for @Maybe@.
104-- | Has the same behavior as the @Monad@ instance for @Maybe@.
111−instance context ~ Expr => Monad (MaybeTable context) where
105+instance Monad MaybeTable where
112 (>>=) = (>>-)
106 (>>=) = (>>-)
113
107
114
108
115−instance context ~ Expr => AltTable (MaybeTable context) where
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
116− ma <|>: mb = bool ma mb (isNothingTable ma)
117+ condition = isNothingTable ma
117
118
118
119
119−instance context ~ Expr => AlternativeTable (MaybeTable context) where
120+instance AlternativeTable MaybeTable where
120 emptyTable = nothingTable
121 emptyTable = nothingTable
121
122
122
123
123−instance (context ~ Expr, Table Expr a, Semigroup a) =>
124+instance (Table Expr a, Semigroup a) => Semigroup (MaybeTable a) where
124− Semigroup (MaybeTable context a)
125− where
126 ma <> mb = maybeTable mb (\a -> maybeTable ma (justTable . (a <>)) mb) ma
125 ma <> mb = maybeTable mb (\a -> maybeTable ma (justTable . (a <>)) mb) ma
127
126
128
127
129−instance (context ~ Expr, Table Expr a, Semigroup a) =>
128+instance (Table Expr a, Semigroup a) => Monoid (MaybeTable a) where
130− Monoid (MaybeTable context a)
131− where
132 mempty = nothingTable
129 mempty = nothingTable
133
130
134
131
132+instance
135−instance (Table context a, Reifiable context, context ~ context') =>
133+ ( Table context a
134+ , Labelable context, Nullifiable context
135+ , ConstrainTag context MaybeTag
136− Table context' (MaybeTable context a)
136+ ) => Table context (MaybeTable a)
137 where
137 where
138− type Columns (MaybeTable context a) = HMaybeTable (Columns a)
138+ type Columns (MaybeTable a) = HMaybeTable (Columns a)
139− type Context (MaybeTable context a) = Context a
139+ type Context (MaybeTable 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− }
147
140
148− fromColumns HMaybeTable {htag, hjust} = MaybeTable
141+ toColumns = toColumns1 toColumns
149− { tag = unHIdentity $ hunlabel htag
142+ fromColumns = fromColumns1 fromColumns
150− , just = fromColumns $ hunlabel hjust
143+ reify = fmap fmap reify
151− }
144+ unreify = fmap fmap unreify
152
145
153− toResult ma = HMaybeTable
154− { htag = hlabel (HIdentity (Identity (IsJust <$ ma)))
155− , hjust = hlabel (toResult @_ @(Nullify context a) ma)
156− }
157
146
158− fromResult HMaybeTable {htag, hjust} = case hunlabel htag of
147+instance
159− HIdentity (Identity tag) -> tag $>
148+ ( Labelable from, Nullifiable from, ConstrainTag from MaybeTag
160− fromMaybe err (fromResult @_ @(Nullify context a) (hunlabel hjust))
149+ , Labelable to, Nullifiable to, ConstrainTag to MaybeTag
161− where
150+ , Recontextualize from to a b
162− err = error "Maybe.fromColumns: mismatch between tag and data"
151+ )
152+ => Recontextualize from to (MaybeTable a) (MaybeTable b)
163
153
164
154
165−instance (EqTable a, context ~ Expr) => EqTable (MaybeTable context a) where
155+instance EqTable a => EqTable (MaybeTable a) where
166− eqTable = HMaybeTable
156+ eqTable = toColumns1 id (justTable (eqTable @a))
167− { htag = hlabel (HIdentity Dict)
168− , hjust = hlabel (eqTable @(Nullify context a))
169− }
170
157
171
158
172−instance (OrdTable a, context ~ Expr) => OrdTable (MaybeTable context a) where
159+instance OrdTable a => OrdTable (MaybeTable a) where
173− ordTable = HMaybeTable
160+ ordTable = toColumns1 id (justTable (ordTable @a))
174− { htag = hlabel (HIdentity Dict)
161+
175− , hjust = hlabel (ordTable @(Nullify context a))
176− }
177
162
163+type instance FromExprs (MaybeTable a) = Maybe (FromExprs a)
178
164
179−instance (ToExprs exprs a, context ~ Expr) =>
165+
180− ToExprs (MaybeTable context exprs) (Maybe a)
166+instance ToExprs exprs a => ToExprs (MaybeTable exprs) (Maybe a) where
167+ fromResult = fmap (fromResult @exprs) . fromColumns
168+ toResult = toColumns . fmap (toResult @exprs)
181
169
182
170
183-- | Check if a @MaybeTable@ is absent of any row. Like 'Data.Maybe.isNothing'.
171-- | Check if a @MaybeTable@ is absent of any row. Like 'Data.Maybe.isNothing'.
184−isNothingTable :: MaybeTable Expr a -> Expr Bool
172+isNothingTable :: MaybeTable a -> Expr Bool
185−isNothingTable (MaybeTable tag _) = isNull tag
173+isNothingTable (MaybeTable tag _) = isNull (expr tag)
186
174
187
175
188-- | Check if a @MaybeTable@ contains a row. Like 'Data.Maybe.isJust'.
176-- | Check if a @MaybeTable@ contains a row. Like 'Data.Maybe.isJust'.
189−isJustTable :: MaybeTable Expr a -> Expr Bool
177+isJustTable :: MaybeTable a -> Expr Bool
190−isJustTable (MaybeTable tag _) = isNonNull tag
178+isJustTable (MaybeTable tag _) = isNonNull (expr tag)
191
179
192
180
193-- | Perform case analysis on a 'MaybeTable'. Like 'maybe'.
181-- | Perform case analysis on a 'MaybeTable'. Like 'maybe'.
194−maybeTable :: Table Expr b => b -> (a -> b) -> MaybeTable Expr a -> b
182+maybeTable :: Table Expr b => b -> (a -> b) -> MaybeTable a -> b
195−maybeTable b f ma@(MaybeTable _ a) = bool (f (extract a)) b (isNothingTable ma)
183+maybeTable b f ma@(MaybeTable _ a) = bool (f a) b (isNothingTable ma)
196{-# INLINABLE maybeTable #-}
184{-# INLINABLE maybeTable #-}
197
185
198
186
199-- | The null table. Like 'Nothing'.
187-- | The null table. Like 'Nothing'.
200−nothingTable :: Table Expr a => MaybeTable Expr a
188+nothingTable :: Table Expr a => MaybeTable a
201−nothingTable = MaybeTable null (pure undefined)
189+nothingTable = MaybeTable (fromExpr null) undefined
202
190
203
191
204-- | Lift any table into 'MaybeTable'. Like 'Just'. Note you can also use
192-- | Lift any table into 'MaybeTable'. Like 'Just'. Note you can also use
205-- 'pure'.
193-- 'pure'.
206−justTable :: a -> MaybeTable Expr a
194+justTable :: a -> MaybeTable a
207−justTable = MaybeTable mempty . pure
195+justTable = MaybeTable (fromExpr mempty)
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
213
196
214
197
215-- | Project a single expression out of a 'MaybeTable'. You can think of this
198-- | Project a single expression out of a 'MaybeTable'. You can think of this
216-- operator like the '$' operator, but it also has the ability to return
199-- operator like the '$' operator, but it also has the ability to return
217-- @null@.
200-- @null@.
218($?) :: forall a b. Sql DBType b
201($?) :: 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)
220f $? ma@(MaybeTable _ a) = case nullable @b of
203f $? ma@(MaybeTable _ a) = case nullable @b of
221− Null -> boolExpr (f (extract a)) null (isNothingTable ma)
204+ Null -> boolExpr (f a) null (isNothingTable ma)
222− NotNull -> boolExpr (nullify (f (extract a))) null (isNothingTable ma)
205+ NotNull -> boolExpr (nullify (f a)) null (isNothingTable ma)
223infixl 4 $?
206infixl 4 $?
224
207
225
208
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−
236-- | Construct a 'MaybeTable' in the 'Name' context. This can be useful if you
209-- | Construct a 'MaybeTable' in the 'Name' context. This can be useful if you
237-- have a 'MaybeTable' that you are storing in a table and need to construct a
210-- have a 'MaybeTable' that you are storing in a table and need to construct a
238-- 'TableSchema'.
211-- 'TableSchema'.
@@ -242,5 +215,40 @@ nameMaybeTable
242 -- 'nothingTable'.
215 -- 'nothingTable'.
243 -> a
216 -> a
244 -- ^ Names of the columns in @a@.
217 -- ^ Names of the columns in @a@.
245− -> MaybeTable Name a
218+ -> MaybeTable a
246−nameMaybeTable tag = MaybeTable tag . pure
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 #-}
2{-# language FlexibleContexts #-}
1{-# language FlexibleContexts #-}
3−{-# language FlexibleInstances #-}
4−{-# language MultiParamTypeClasses #-}
5{-# language NamedFieldPuns #-}
2{-# language NamedFieldPuns #-}
6{-# language ScopedTypeVariables #-}
3{-# language ScopedTypeVariables #-}
7{-# language TypeApplications #-}
4{-# language TypeApplications #-}
8{-# language TypeFamilies #-}
5{-# language TypeFamilies #-}
9−{-# language UndecidableInstances #-}
10{-# language ViewPatterns #-}
6{-# language ViewPatterns #-}
11
7
12module Rel8.Table.Name
8module Rel8.Table.Name
13 ( namesFromLabels
9 ( namesFromLabels
14 , namesFromLabelsWith
10 , namesFromLabelsWith
11+ , showExprs
15 , showLabels
12 , showLabels
16 , showNames
13 , showNames
17 )
14 )
@@ -20,15 +17,20 @@ where
20-- base
17-- base
21import Data.Foldable ( fold )
18import Data.Foldable ( fold )
22import Data.Functor.Const ( Const( Const ), getConst )
19import Data.Functor.Const ( Const( Const ), getConst )
23−import Data.List.NonEmpty ( NonEmpty, intersperse, nonEmpty )
20+import Data.List.NonEmpty ( NonEmpty, intersperse )
24−import Data.Maybe ( fromMaybe )
25import Prelude
21import Prelude
26
22
23+-- opaleye
24+import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
25+
27-- rel8
26-- rel8
27+import Rel8.Expr ( Expr, Col( E ) )
28+import Rel8.Expr.Opaleye ( toPrimExpr )
29+import Rel8.Kind.Labels ( renderLabels )
28import Rel8.Schema.HTable ( htabulate, htabulateA, hfield, hspecs )
30import Rel8.Schema.HTable ( htabulate, htabulateA, hfield, hspecs )
29−import Rel8.Schema.Name ( Name( Name ) )
31+import Rel8.Schema.Name ( Name( Name ), Col( N ) )
30−import Rel8.Schema.Spec ( Spec(..) )
32+import Rel8.Schema.Spec ( SSpec(..) )
31−import Rel8.Table ( Table(..) )
33+import Rel8.Table ( Table, Columns, Context, fromColumns, toColumns )
32
34
33
35
34-- | Construct a table in the 'Name' context containing the names of all
36-- | Construct a table in the 'Name' context containing the names of all
@@ -60,20 +62,23 @@ namesFromLabelsWith :: Table Name a
60 => (NonEmpty String -> String) -> a
62 => (NonEmpty String -> String) -> a
61namesFromLabelsWith f = fromColumns $ htabulate $ \field ->
63namesFromLabelsWith f = fromColumns $ htabulate $ \field ->
62 case hfield hspecs field of
64 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)]
64
73
65
74
66showLabels :: forall a. Table (Context a) a => a -> [NonEmpty String]
75showLabels :: forall a. Table (Context a) a => a -> [NonEmpty String]
67showLabels _ = getConst $
76showLabels _ = getConst $
68 htabulateA @(Columns a) $ \field -> case hfield hspecs field of
77 htabulateA @(Columns a) $ \field -> case hfield hspecs field of
69− Spec {labels} -> Const (pure (renderLabels labels))
78+ SSpec {labels} -> Const [renderLabels labels]
70
79
71
80
72−showNames :: forall a. Table Name a => a -> NonEmpty String
81+showNames :: forall a. Table Name a => a -> [String]
73showNames (toColumns -> names) = getConst $
82showNames (toColumns -> names) = getConst $
74 htabulateA @(Columns a) $ \field -> case hfield names field of
83 htabulateA @(Columns a) $ \field -> case hfield names field of
75− Name name -> Const (pure name)
84+ N (Name name) -> Const [name]
76−
77−
78−renderLabels :: [String] -> NonEmpty String
79−renderLabels labels = fromMaybe (pure "anon") (nonEmpty labels )
src/Rel8/Table/NonEmpty.hs+54−66
@@ -1,4 +1,3 @@
1−{-# language DataKinds #-}
2{-# language FlexibleContexts #-}
1{-# language FlexibleContexts #-}
3{-# language FlexibleInstances #-}
2{-# language FlexibleInstances #-}
4{-# language MultiParamTypeClasses #-}
3{-# language MultiParamTypeClasses #-}
@@ -11,9 +10,7 @@
11
10
12module Rel8.Table.NonEmpty
11module Rel8.Table.NonEmpty
13 ( NonEmptyTable(..)
12 ( NonEmptyTable(..)
14− , ($+)
15− , nonEmptyTable
16− , nameNonEmptyTable
13+ , nonEmptyTable, nameNonEmptyTable
17 )
14 )
18where
15where
19
16
@@ -21,111 +18,102 @@ where
21import Data.Functor.Identity ( Identity( Identity ) )
18import Data.Functor.Identity ( Identity( Identity ) )
22import Data.Kind ( Type )
19import Data.Kind ( Type )
23import Data.List.NonEmpty ( NonEmpty )
20import Data.List.NonEmpty ( NonEmpty )
21+import Data.Type.Equality ( (:~:)( Refl ) )
24−import Prelude hiding ( id )
22+import Prelude
25
23
26-- rel8
24-- rel8
27−import Rel8.Expr ( Expr )
25+import Rel8.Expr ( Expr, Col( E, unE ) )
28import Rel8.Expr.Array ( sappend1, snonEmptyOf )
26import Rel8.Expr.Array ( sappend1, snonEmptyOf )
29import Rel8.Schema.Dict ( Dict( Dict ) )
27import Rel8.Schema.Dict ( Dict( Dict ) )
30import Rel8.Schema.HTable.NonEmpty ( HNonEmptyTable )
28import Rel8.Schema.HTable.NonEmpty ( HNonEmptyTable )
31−import Rel8.Schema.HTable.Vectorize
29+import Rel8.Schema.HTable.Vectorize ( happend, hvectorize )
32− ( hvectorize, hunvectorize
33− , happend
34− , hproject, hcolumn
35− )
36−import qualified Rel8.Schema.Kind as K
30+import Rel8.Schema.Name ( Col( N ), Name( Name ) )
37−import Rel8.Schema.Name ( Name( Name ) )
38import Rel8.Schema.Null ( Nullity( Null, NotNull ) )
31import Rel8.Schema.Null ( Nullity( Null, NotNull ) )
39−import Rel8.Schema.Result ( vectorizer, unvectorizer )
32+import Rel8.Schema.Reify ( hreify, hunreify )
40−import Rel8.Schema.Spec ( Spec(..) )
33+import Rel8.Schema.Spec ( SSpec(..) )
34+import Rel8.Schema.Spec.ConstrainDBType ( dbTypeDict, dbTypeNullity )
41import Rel8.Table
35import Rel8.Table
42 ( Table, Context, Columns, fromColumns, toColumns
36 ( Table, Context, Columns, fromColumns, toColumns
43− , FromExprs, fromResult, toResult
37+ , reify, unreify
44− , Transpose
45 )
38 )
46import Rel8.Table.Alternative ( AltTable, (<|>:) )
39import Rel8.Table.Alternative ( AltTable, (<|>:) )
47import Rel8.Table.Eq ( EqTable, eqTable )
40import Rel8.Table.Eq ( EqTable, eqTable )
48import Rel8.Table.Ord ( OrdTable, ordTable )
41import Rel8.Table.Ord ( OrdTable, ordTable )
49−import Rel8.Table.Projection
42+import Rel8.Table.Recontextualize ( Recontextualize )
50− ( Projectable, Projecting, Projection, project, apply
51− )
52−import Rel8.Table.Serialize ( ToExprs )
43+import Rel8.Table.Serialize ( FromExprs, ToExprs, fromResult, toResult )
44+import Rel8.Table.Unreify ( Unreifies )
53
45
54
46
55-- | A @NonEmptyTable@ value contains one or more instances of @a@. You
47-- | A @NonEmptyTable@ value contains one or more instances of @a@. You
56-- construct @NonEmptyTable@s with 'Rel8.some' or 'nonEmptyAgg'.
48-- construct @NonEmptyTable@s with 'Rel8.some' or 'nonEmptyAgg'.
57−type NonEmptyTable :: K.Context -> Type -> Type
49+type NonEmptyTable :: Type -> Type
58−newtype NonEmptyTable context a =
50+newtype NonEmptyTable a =
59− NonEmptyTable (HNonEmptyTable (Columns a) (Context a))
51+ NonEmptyTable (HNonEmptyTable (Columns a) (Col (Context a)))
60−
61−
62−instance Projectable (NonEmptyTable context) where
63− project f (NonEmptyTable a) = NonEmptyTable (hproject (apply f) a)
64
52
65
53
66−instance (Table context a, context ~ context') =>
54+instance (Table context a, Unreifies context a) =>
67− Table context' (NonEmptyTable context a)
55+ Table context (NonEmptyTable a)
68 where
56 where
69− type Columns (NonEmptyTable context a) = HNonEmptyTable (Columns a)
57+ type Columns (NonEmptyTable a) = HNonEmptyTable (Columns a)
70− type Context (NonEmptyTable context a) = Context a
58+ type Context (NonEmptyTable 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)
74
59
75 fromColumns = NonEmptyTable
60 fromColumns = NonEmptyTable
76 toColumns (NonEmptyTable a) = a
61 toColumns (NonEmptyTable a) = a
77− fromResult = fmap (fromResult @_ @a) . hunvectorize unvectorizer
78− toResult = hvectorize vectorizer . fmap (toResult @_ @a)
79
62
63+ reify Refl (NonEmptyTable a) = NonEmptyTable (hreify a)
64+ unreify Refl (NonEmptyTable a) = NonEmptyTable (hunreify a)
80
65
66+
81−instance (EqTable a, context ~ Expr) =>
67+instance
82− EqTable (NonEmptyTable context a)
68+ ( Unreifies from a, Unreifies to b
83− where
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
84 eqTable =
75 eqTable =
85 hvectorize
76 hvectorize
86− (\Spec {nullity} (Identity Dict) -> case nullity of
77+ (\SSpec {} (Identity dict) -> case dbTypeDict dict of
78+ Dict -> case dbTypeNullity dict of
87− Null -> Dict
79+ Null -> Dict
88− NotNull -> Dict)
80+ NotNull -> Dict)
89 (Identity (eqTable @a))
81 (Identity (eqTable @a))
90
82
91
83
92−instance (OrdTable a, context ~ Expr) =>
93− OrdTable (NonEmptyTable context a)
84+instance OrdTable a => OrdTable (NonEmptyTable a) where
94− where
95 ordTable =
85 ordTable =
96 hvectorize
86 hvectorize
97− (\Spec {nullity} (Identity Dict) -> case nullity of
87+ (\SSpec {} (Identity dict) -> case dbTypeDict dict of
88+ Dict -> case dbTypeNullity dict of
98− Null -> Dict
89+ Null -> Dict
99− NotNull -> Dict)
90+ NotNull -> Dict)
100 (Identity (ordTable @a))
91 (Identity (ordTable @a))
101
92
102
93
103−instance (ToExprs exprs a, context ~ Expr) =>
104− ToExprs (NonEmptyTable context exprs) (NonEmpty a)
94+type instance FromExprs (NonEmptyTable a) = NonEmpty (FromExprs a)
105
95
106
96
107−instance context ~ Expr => AltTable (NonEmptyTable context) where
97+instance ToExprs exprs a => ToExprs (NonEmptyTable exprs) (NonEmpty a)
108− (<|>:) = (<>)
98+ where
99+ fromResult = fmap (fromResult @exprs) . fromColumns
100+ toResult = toColumns . fmap (toResult @exprs)
109
101
110
102
111−instance (Table Expr a, context ~ Expr) => Semigroup (NonEmptyTable context a)
103+instance AltTable NonEmptyTable where
112− where
104+ (<|>:) = (<>)
113− NonEmptyTable as <> NonEmptyTable bs = NonEmptyTable $
114− happend (const sappend1) as bs
115
105
116
106
117−-- | Project a single expression out of a 'NonEmptyTable'.
107+instance Table Expr a => Semigroup (NonEmptyTable a) where
118−($+) :: Projecting a (Expr b)
108+ NonEmptyTable as <> NonEmptyTable bs = NonEmptyTable $
119− => Projection a (Expr b) -> NonEmptyTable Expr a -> Expr (NonEmpty b)
109+ happend (\_ _ (E a) (E b) -> E (sappend1 a b)) as bs
120−f $+ NonEmptyTable a = hcolumn $ hproject (apply f) a
121−infixl 4 $+
122
110
123
111
124-- | Construct a @NonEmptyTable@ from a non-empty list of expressions.
112-- | 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
126nonEmptyTable =
114nonEmptyTable =
127 NonEmptyTable .
115 NonEmptyTable .
128− hvectorize (\Spec {info} -> snonEmptyOf info) .
116+ hvectorize (\SSpec {info} -> E . snonEmptyOf info . fmap unE) .
129 fmap toColumns
117 fmap toColumns
130
118
131
119
@@ -135,9 +123,9 @@ nonEmptyTable =
135nameNonEmptyTable
123nameNonEmptyTable
136 :: Table Name a
124 :: Table Name a
137 => a -- ^ The names of the columns of elements of the list.
125 => a -- ^ The names of the columns of elements of the list.
138− -> NonEmptyTable Name a
126+ -> NonEmptyTable a
139nameNonEmptyTable =
127nameNonEmptyTable =
140 NonEmptyTable .
128 NonEmptyTable .
141− hvectorize (\_ (Identity (Name a)) -> Name a) .
129+ hvectorize (\_ (Identity (N (Name a))) -> N (Name a)) .
142 pure .
130 pure .
143 toColumns
131 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 @@
1{-# language BlockArguments #-}
1{-# language BlockArguments #-}
2−{-# language DataKinds #-}
3{-# language DisambiguateRecordFields #-}
2{-# language DisambiguateRecordFields #-}
4{-# language FlexibleContexts #-}
3{-# language FlexibleContexts #-}
4+{-# language LambdaCase #-}
5{-# language NamedFieldPuns #-}
5{-# language NamedFieldPuns #-}
6−{-# language RankNTypes #-}
7{-# language TypeFamilies #-}
6{-# language TypeFamilies #-}
8{-# language ViewPatterns #-}
7{-# language ViewPatterns #-}
9
8
10−{-# options_ghc -Wno-deprecations #-}
11−
12module Rel8.Table.Opaleye
9module Rel8.Table.Opaleye
13 ( aggregator
10 ( aggregator
14− , attributes
15 , binaryspec
11 , binaryspec
16 , distinctspec
12 , distinctspec
17− , exprs
18− , exprsWithNames
19 , table
13 , table
20 , tableFields
14 , tableFields
21 , unpackspec
15 , unpackspec
22 , valuesspec
16 , valuesspec
23− , view
24 , castTable
17 , castTable
25 )
18 )
26where
19where
27
20
28-- base
21-- base
29−import Data.Functor.Const ( Const( Const ), getConst )
22+import Prelude hiding ( undefined )
30−import Data.List.NonEmpty ( NonEmpty )
31−import Prelude
32
23
33-- opaleye
24-- opaleye
34−import qualified Opaleye.Adaptors as Opaleye
35−import qualified Opaleye.Field as Opaleye ( Field_ )
36import qualified Opaleye.Internal.Aggregate as Opaleye
25import qualified Opaleye.Internal.Aggregate as Opaleye
26+import qualified Opaleye.Internal.Binary as Opaleye
27+import qualified Opaleye.Internal.Distinct as Opaleye
37import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
28import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
29+import qualified Opaleye.Internal.PackMap as Opaleye
30+import qualified Opaleye.Internal.Unpackspec as Opaleye
38import qualified Opaleye.Internal.Values as Opaleye
31import qualified Opaleye.Internal.Values as Opaleye
39−import qualified Opaleye.Table as Opaleye
32+import qualified Opaleye.Internal.Table as Opaleye
40
33
41-- profunctors
34-- profunctors
42import Data.Profunctor ( dimap, lmap )
35import Data.Profunctor ( dimap, lmap )
43
36
44-- rel8
37-- rel8
45−import Rel8.Aggregate ( Aggregate( Aggregate ), Aggregates )
38+import Rel8.Aggregate ( Col( A ), Aggregate( Aggregate ), Aggregates )
46−import Rel8.Expr ( Expr )
39+import Rel8.Expr ( Expr, Col(..) )
47import Rel8.Expr.Opaleye
40import Rel8.Expr.Opaleye
48 ( fromPrimExpr, toPrimExpr
41 ( fromPrimExpr, toPrimExpr
42+ , traversePrimExpr
43+ , fromColumn, toColumn
49− , scastExpr, traverseFieldP
44+ , scastExpr
50 )
45 )
51−import Rel8.Schema.HTable ( htabulateA, hfield, hspecs, htabulate,
46+import Rel8.Schema.HTable ( htabulateA, hfield, htraverse, hspecs, htabulate )
52− htraverseP, htraversePWithField )
53−import Rel8.Schema.Name ( Name( Name ), Selects, ppColumn )
47+import Rel8.Schema.Name ( Col( N ), Name( Name ), Selects )
54−import Rel8.Schema.Spec ( Spec(..) )
48+import Rel8.Schema.Spec ( SSpec(..) )
55−import Rel8.Schema.Table ( TableSchema(..), ppTable )
49+import Rel8.Schema.Table ( TableSchema(..) )
56import Rel8.Table ( Table, fromColumns, toColumns )
50import Rel8.Table ( Table, fromColumns, toColumns )
57−import Rel8.Type.Information ( typeName )
51+import Rel8.Table.Undefined ( undefined )
58
52
59-- semigroupoids
53-- semigroupoids
60import Data.Functor.Apply ( WrappedApplicative(..) )
54import Data.Functor.Apply ( WrappedApplicative(..) )
61−import Data.Profunctor.Product ( ProductProfunctor )
62
55
63
56
64aggregator :: Aggregates aggregates exprs => Opaleye.Aggregator aggregates exprs
57aggregator :: Aggregates aggregates exprs => Opaleye.Aggregator aggregates exprs
65−aggregator = dimap toColumns fromColumns $
58+aggregator = Opaleye.Aggregator $ Opaleye.PackMap $ \f aggregates ->
66− htraverseP $
59+ fmap fromColumns $ unwrapApplicative $ htabulateA $ \field ->
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
60+ WrapApplicative $ case hfield (toColumns aggregates) field of
73− Name column -> fromPrimExpr $ Opaleye.ConstExpr $
61+ A (Aggregate (Opaleye.Aggregator (Opaleye.PackMap inner))) ->
74− Opaleye.OtherLit $
62+ E <$> inner f ()
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))
83
63
84
64
85binaryspec :: Table Expr a => Opaleye.Binaryspec a a
65binaryspec :: 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)
87
71
88
72
89distinctspec :: Table Expr a => Opaleye.Distinctspec a a
73distinctspec :: Table Expr a => Opaleye.Distinctspec a a
90−distinctspec = fromOpaleyespec Opaleye.distinctspecField
74+distinctspec =
91−
75+ Opaleye.Distinctspec $ Opaleye.Aggregator $ Opaleye.PackMap $ \f ->
92−
76+ fmap fromColumns .
93−exprs :: Table Expr a => a -> NonEmpty Opaleye.PrimExpr
77+ unwrapApplicative .
94−exprs (toColumns -> as) = getConst $ htabulateA $ \field ->
78+ htraverse
95− case hfield as field of
79+ (\(E a) ->
96− expr -> Const (pure (toPrimExpr expr))
80+ WrapApplicative $ E . fromPrimExpr <$> f (Nothing, toPrimExpr a)) .
97−
81+ toColumns
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))
104
82
105
83
106−table :: Selects names exprs => TableSchema names -> Opaleye.Table exprs exprs
84+table ::Selects names exprs => TableSchema names -> Opaleye.Table exprs exprs
107table (TableSchema name schema columns) =
85table (TableSchema name schema columns) =
108 case schema of
86 case schema of
109− Nothing -> Opaleye.table name (tableFields columns)
87+ Nothing -> Opaleye.Table name (tableFields columns)
110− Just schemaName -> Opaleye.tableWithSchema schemaName name (tableFields columns)
88+ Just schemaName -> Opaleye.TableWithSchema schemaName name (tableFields columns)
111
89
112
90
113−tableFields :: Selects names exprs
91+tableFields ::Selects names exprs
114 => names -> Opaleye.TableFields exprs exprs
92 => names -> Opaleye.TableFields exprs exprs
115tableFields (toColumns -> names) = dimap toColumns fromColumns $
93tableFields (toColumns -> names) = dimap toColumns fromColumns $
116 unwrapApplicative $ htabulateA $ \field -> WrapApplicative $
94 unwrapApplicative $ htabulateA $ \field -> WrapApplicative $
117 case hfield names field of
95 case hfield names field of
118 name -> lmap (`hfield` field) (go name)
96 name -> lmap (`hfield` field) (go name)
119 where
97 where
120− go :: Name a -> Opaleye.TableFields (Expr a) (Expr a)
98+ go :: Col Name spec -> Opaleye.TableFields (Col Expr spec) (Col Expr spec)
121− go (Name name) =
99+ go (N (Name name)) =
122− traverseFieldP $
100+ lmap (\(E a) -> toColumn $ toPrimExpr a) $
101+ E . fromPrimExpr . fromColumn <$>
123− Opaleye.requiredTableField name
102+ Opaleye.requiredTableField name
124
103
125
104
126unpackspec :: Table Expr a => Opaleye.Unpackspec a a
105unpackspec :: 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
128{-# INLINABLE unpackspec #-}
111{-# INLINABLE unpackspec #-}
129
112
130
113
131−valuesspec :: Table Expr a => Opaleye.Valuesspec a a
114+valuesspec :: Table Expr a => Opaleye.ValuesspecSafe a a
132−valuesspec = dimap toColumns fromColumns $
115+valuesspec = Opaleye.ValuesspecSafe (toPackMap undefined) unpackspec
133− htraversePWithField (traverseFieldP . Opaleye.valuesspecFieldType . typeName)
134− where typeName = Rel8.Type.Information.typeName . info . hfield hspecs
135
116
136
117
137−view :: Selects names exprs => names -> exprs
118+toPackMap :: Table Expr a
119+ => a -> Opaleye.PackMap Opaleye.PrimExpr Opaleye.PrimExpr () a
120+toPackMap as = Opaleye.PackMap $ \f () ->
138−view columns = fromColumns $ htabulate $ \field ->
121+ fmap fromColumns $
122+ unwrapApplicative .
123+ htraverse (\(E a) -> WrapApplicative $ E <$> traversePrimExpr f a) $
139− case hfield (toColumns columns) field of
124+ toColumns as
140− Name column -> fromPrimExpr $ Opaleye.BaseTableAttrExpr column
141
125
142
126
143-- | Transform a table by adding 'CAST' to all columns. This is most useful for
127-- | Transform a table by adding 'CAST' to all columns. This is most useful for
144-- finalising a SELECT or RETURNING statement, guaranteed that the output
128-- finalising a SELECT or RETURNING statement, guaranteed that the output
145-- matches what is encoded in each columns TypeInformation.
129-- matches what is encoded in each columns TypeInformation.
146castTable :: Table Expr a => a -> a
130castTable :: Table Expr a => a -> a
147−castTable (toColumns -> as) = fromColumns $ htabulate \field ->
131+castTable (toColumns -> as) = fromColumns $ htabulate \i ->
148− case hfield hspecs field of
132+ case hfield hspecs i of
133+ SSpec{info} ->
149− Spec {info} -> case hfield as field of
134+ case hfield as i of
135+ E expr ->
150− expr -> scastExpr info expr
136+ E (scastExpr info expr)
src/Rel8/Table/Ord.hs+45−16
@@ -10,6 +10,7 @@
10{-# language TypeFamilies #-}
10{-# language TypeFamilies #-}
11{-# language TypeOperators #-}
11{-# language TypeOperators #-}
12{-# language UndecidableInstances #-}
12{-# language UndecidableInstances #-}
13+{-# language UndecidableSuperClasses #-}
13{-# language ViewPatterns #-}
14{-# language ViewPatterns #-}
14
15
15module Rel8.Table.Ord
16module Rel8.Table.Ord
@@ -24,20 +25,29 @@ import GHC.Generics ( Rep )
24import Prelude hiding ( seq )
25import Prelude hiding ( seq )
25
26
26-- rel8
27-- rel8
27−import Rel8.Expr ( Expr )
28+import Rel8.Expr ( Expr, Col( E ) )
28import Rel8.Expr.Bool ( (||.), (&&.), false, true )
29import Rel8.Expr.Bool ( (||.), (&&.), false, true )
29import Rel8.Expr.Eq ( (==.) )
30import Rel8.Expr.Eq ( (==.) )
30import Rel8.Expr.Ord ( (<.), (>.) )
31import Rel8.Expr.Ord ( (<.), (>.) )
31import Rel8.FCF ( Eval, Exp )
32import Rel8.FCF ( Eval, Exp )
32import Rel8.Generic.Record ( Record )
33import 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 )
34import Rel8.Schema.Dict ( Dict( Dict ) )
39import Rel8.Schema.Dict ( Dict( Dict ) )
35−import Rel8.Schema.HTable ( htabulateA, hfield )
40+import Rel8.Schema.HTable
41+ ( HTable, HConstrainTable
42+ , htabulateA, hfield, hdicts
43+ )
36−import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) )
44+import Rel8.Schema.HTable.Identity ( HIdentity( HType ) )
37import Rel8.Schema.Null (Sql)
45import Rel8.Schema.Null (Sql)
46+import Rel8.Schema.Spec.ConstrainDBType ( ConstrainDBType, nullifier )
38import Rel8.Table ( Columns, toColumns, TColumns )
47import Rel8.Table ( Columns, toColumns, TColumns )
39import Rel8.Table.Bool ( bool )
48import Rel8.Table.Bool ( bool )
40import Rel8.Table.Eq ( EqTable )
49import Rel8.Table.Eq ( EqTable )
50+import Rel8.Type.Eq ( DBEq )
41import Rel8.Type.Ord ( DBOrd )
51import Rel8.Type.Ord ( DBOrd )
42
52
43
53
@@ -46,14 +56,22 @@ import Rel8.Type.Ord ( DBOrd )
46-- columns in a 'Table' have an instance of 'DBOrd'".
56-- columns in a 'Table' have an instance of 'DBOrd'".
47type OrdTable :: Type -> Constraint
57type OrdTable :: Type -> Constraint
48class EqTable a => OrdTable a where
58class EqTable a => OrdTable a where
49− ordTable :: Columns a (Dict (Sql DBOrd))
59+ ordTable :: Columns a (Dict (ConstrainDBType DBOrd))
50
60
51 default ordTable ::
61 default ordTable ::
62+ ( KnownAlgebra (GAlgebra (Rep (Record a)))
52− ( GTable TOrdTable TColumns (Rep (Record a))
63+ , Eval (GGTable (GAlgebra (Rep (Record a))) TOrdTable TColumns (Dict (ConstrainDBType DBOrd)) (Rep (Record a)))
53− , Columns a ~ GColumns TColumns (Rep (Record a))
64+ , Columns a ~ Eval (GGColumns (GAlgebra (Rep (Record a))) TColumns (Rep (Record a)))
54 )
65 )
55− => Columns a (Dict (Sql DBOrd))
66+ => Columns a (Dict (ConstrainDBType DBOrd))
56− ordTable = gtable @TOrdTable @TColumns @(Rep (Record a)) table
67+ ordTable =
68+ ggtable
69+ @(GAlgebra (Rep (Record a)))
70+ @TOrdTable
71+ @TColumns
72+ @(Rep (Record a))
73+ table
74+ nullifier
57 where
75 where
58 table (_ :: proxy x) = ordTable @x
76 table (_ :: proxy x) = ordTable @x
59
77
@@ -62,8 +80,19 @@ data TOrdTable :: Type -> Exp Constraint
62type instance Eval (TOrdTable a) = OrdTable a
80type instance Eval (TOrdTable a) = OrdTable a
63
81
64
82
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+
65instance Sql DBOrd a => OrdTable (Expr a) where
94instance Sql DBOrd a => OrdTable (Expr a) where
66− ordTable = HIdentity Dict
95+ ordTable = HType Dict
67
96
68
97
69instance (OrdTable a, OrdTable b) => OrdTable (a, b)
98instance (OrdTable a, OrdTable b) => OrdTable (a, b)
@@ -98,7 +127,7 @@ instance
98(toColumns -> as) <: (toColumns -> bs) =
127(toColumns -> as) <: (toColumns -> bs) =
99 foldr @[] go false $ getConst $ htabulateA $ \field ->
128 foldr @[] go false $ getConst $ htabulateA $ \field ->
100 case (hfield as field, hfield bs field) of
129 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
102 Dict -> Const [(a <. b, a ==. b)]
131 Dict -> Const [(a <. b, a ==. b)]
103 where
132 where
104 go (lt, eq) a = lt ||. (eq &&. a)
133 go (lt, eq) a = lt ||. (eq &&. a)
@@ -111,7 +140,7 @@ infix 4 <:
111(toColumns -> as) <=: (toColumns -> bs) =
140(toColumns -> as) <=: (toColumns -> bs) =
112 foldr @[] go true $ getConst $ htabulateA $ \field ->
141 foldr @[] go true $ getConst $ htabulateA $ \field ->
113 case (hfield as field, hfield bs field) of
142 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
115 Dict -> Const [(a <. b, a ==. b)]
144 Dict -> Const [(a <. b, a ==. b)]
116 where
145 where
117 go (lt, eq) a = lt ||. (eq &&. a)
146 go (lt, eq) a = lt ||. (eq &&. a)
@@ -124,7 +153,7 @@ infix 4 <=:
124(toColumns -> as) >: (toColumns -> bs) =
153(toColumns -> as) >: (toColumns -> bs) =
125 foldr @[] go false $ getConst $ htabulateA $ \field ->
154 foldr @[] go false $ getConst $ htabulateA $ \field ->
126 case (hfield as field, hfield bs field) of
155 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
128 Dict -> Const [(a >. b, a ==. b)]
157 Dict -> Const [(a >. b, a ==. b)]
129 where
158 where
130 go (gt, eq) a = gt ||. (eq &&. a)
159 go (gt, eq) a = gt ||. (eq &&. a)
@@ -137,7 +166,7 @@ infix 4 >:
137(toColumns -> as) >=: (toColumns -> bs) =
166(toColumns -> as) >=: (toColumns -> bs) =
138 foldr @[] go true $ getConst $ htabulateA $ \field ->
167 foldr @[] go true $ getConst $ htabulateA $ \field ->
139 case (hfield as field, hfield bs field) of
168 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
141 Dict -> Const [(a >. b, a ==. b)]
170 Dict -> Const [(a >. b, a ==. b)]
142 where
171 where
143 go (gt, eq) a = gt ||. (eq &&. a)
172 go (gt, eq) a = gt ||. (eq &&. a)
@@ -146,9 +175,9 @@ infix 4 >=:
146
175
147-- | Given two 'Table's, return the table that sorts before the other.
176-- | Given two 'Table's, return the table that sorts before the other.
148least :: OrdTable a => a -> a -> a
177least :: OrdTable a => a -> a -> a
149−least a b = bool b a (a <: b)
178+least a b = bool a b (a <: b)
150
179
151
180
152-- | Given two 'Table's, return the table that sorts after the other.
181-- | Given two 'Table's, return the table that sorts after the other.
153greatest :: OrdTable a => a -> a -> a
182greatest :: 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 @@
1{-# language DataKinds #-}
1{-# language DataKinds #-}
2−{-# language NamedFieldPuns #-}
3{-# language ScopedTypeVariables #-}
2{-# language ScopedTypeVariables #-}
4{-# language TypeApplications #-}
3{-# language TypeApplications #-}
5{-# language TypeFamilies #-}
4{-# language TypeFamilies #-}
@@ -16,14 +15,16 @@ import Data.Functor.Contravariant ( (>$<), contramap )
16import Prelude
15import Prelude
17
16
18-- rel8
17-- rel8
18+import Rel8.Expr ( unE )
19import Rel8.Expr.Order ( asc, desc, nullsFirst, nullsLast )
19import Rel8.Expr.Order ( asc, desc, nullsFirst, nullsLast )
20import Rel8.Order ( Order )
20import Rel8.Order ( Order )
21import Rel8.Schema.Dict ( Dict( Dict ) )
21import Rel8.Schema.Dict ( Dict( Dict ) )
22import Rel8.Schema.HTable (htabulateA, hfield, hspecs)
22import Rel8.Schema.HTable (htabulateA, hfield, hspecs)
23import Rel8.Schema.Null ( Nullity( Null, NotNull ) )
23import 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 )
25import Rel8.Table ( Columns, toColumns )
26import Rel8.Table ( Columns, toColumns )
26−import Rel8.Table.Ord ( OrdTable, ordTable )
27+import Rel8.Table.Ord
27
28
28
29
29-- | Construct an 'Order' for a 'Table' by sorting all columns into ascending
30-- | Construct an 'Order' for a 'Table' by sorting all columns into ascending
@@ -31,11 +32,12 @@ import Rel8.Table.Ord ( OrdTable, ordTable )
31ascTable :: forall a. OrdTable a => Order a
32ascTable :: forall a. OrdTable a => Order a
32ascTable = contramap toColumns $ getConst $
33ascTable = contramap toColumns $ getConst $
33 htabulateA @(Columns a) $ \field -> case hfield hspecs field of
34 htabulateA @(Columns a) $ \field -> case hfield hspecs field of
34− Spec {nullity} -> case hfield (ordTable @a) field of
35+ SSpec {} -> case hfield (ordTable @a) field of
36+ dict@Dict -> case dbTypeDict dict of
35− Dict -> Const $ (`hfield` field) >$<
37+ Dict -> Const $ unE . (`hfield` field) >$<
36− case nullity of
38+ case dbTypeNullity dict of
37− Null -> nullsFirst asc
39+ Null -> nullsFirst asc
38− NotNull -> asc
40+ NotNull -> asc
39
41
40
42
41-- | Construct an 'Order' for a 'Table' by sorting all columns into descending
43-- | Construct an 'Order' for a 'Table' by sorting all columns into descending
@@ -43,8 +45,9 @@ ascTable = contramap toColumns $ getConst $
43descTable :: forall a. OrdTable a => Order a
45descTable :: forall a. OrdTable a => Order a
44descTable = contramap toColumns $ getConst $
46descTable = contramap toColumns $ getConst $
45 htabulateA @(Columns a) $ \field -> case hfield hspecs field of
47 htabulateA @(Columns a) $ \field -> case hfield hspecs field of
46− Spec {nullity} -> case hfield (ordTable @a) field of
48+ SSpec {} -> case hfield (ordTable @a) field of
49+ dict@Dict -> case dbTypeDict dict of
47− Dict -> Const $ (`hfield` field) >$<
50+ Dict -> Const $ unE . (`hfield` field) >$<
48− case nullity of
51+ case dbTypeNullity dict of
49− Null -> nullsLast desc
52+ Null -> nullsLast desc
50− NotNull -> 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 @@
1{-# language DataKinds #-}
1{-# language DataKinds #-}
2{-# language FlexibleInstances #-}
2{-# language FlexibleInstances #-}
3−{-# language MultiParamTypeClasses #-}
3+{-# language FunctionalDependencies #-}
4{-# language ScopedTypeVariables #-}
4{-# language ScopedTypeVariables #-}
5{-# language StandaloneKindSignatures #-}
5{-# language StandaloneKindSignatures #-}
6{-# language TypeApplications #-}
6{-# language TypeApplications #-}
@@ -15,78 +15,134 @@ module Rel8.Table.Rel8able
15where
15where
16
16
17-- base
17-- base
18+import Data.Kind ( Constraint, Type )
19+import Data.Type.Equality ( (:~:)( Refl ) )
18−import Prelude ()
20+import Prelude
19
21
20-- rel8
22-- rel8
21import Rel8.Expr ( Expr )
23import Rel8.Expr ( Expr )
22import qualified Rel8.Kind.Algebra as K
24import 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+ )
24import Rel8.Generic.Rel8able
30import Rel8.Generic.Rel8able
25 ( Rel8able, Algebra
31 ( Rel8able, Algebra
26 , GColumns, gfromColumns, gtoColumns
32 , GColumns, gfromColumns, gtoColumns
27− , GFromExprs, gfromResult, gtoResult
33+ , greify, gunreify
28 )
34 )
35+import Rel8.Schema.Context ( Col )
36+import Rel8.Schema.Context.Label ( Labelable )
37+import Rel8.Schema.Dict ( Dict( Dict ) )
29import qualified Rel8.Schema.Kind as K
38import qualified Rel8.Schema.Kind as K
30import Rel8.Schema.HTable ( HConstrainTable, hdicts )
39import Rel8.Schema.HTable ( HConstrainTable, hdicts )
31−import Rel8.Schema.Null ( Sql )
40+import Rel8.Schema.Reify ( hreify, hunreify, UnwrapReify )
32import Rel8.Schema.Result ( Result )
41import Rel8.Schema.Result ( Result )
33import Rel8.Table
42import Rel8.Table
34− ( Table, Columns, Context, fromColumns, toColumns
43+ ( Table, Columns, Context, Congruent, fromColumns, toColumns
35− , FromExprs, fromResult, toResult
44+ , Unreify, reify, unreify
36− , Transpose
37 )
45 )
38−import Rel8.Table.ADT ( ADT )
46+import Rel8.Schema.Spec.ConstrainDBType ( ConstrainDBType )
47+import Rel8.Table.ADT ( ADT( ADT ), ADTable, fromADT, toADT )
39import Rel8.Table.Eq ( EqTable, eqTable )
48import Rel8.Table.Eq ( EqTable, eqTable )
49+import Rel8.Table.HKD ( HKD )
40import Rel8.Table.Ord ( OrdTable, ordTable )
50import 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 )
42import Rel8.Type.Eq ( DBEq )
53import Rel8.Type.Eq ( DBEq )
43import Rel8.Type.Ord ( DBOrd )
54import Rel8.Type.Ord ( DBOrd )
44
55
45
56
46−instance (Rel8able t, Reifiable context, context ~ context') =>
57+instance (Rel8able t, Labelable context, Reifiable context) =>
47− Table context' (t context)
58+ Table context (t context)
48 where
59 where
49 type Columns (t context) = GColumns t
60 type Columns (t context) = GColumns t
50 type Context (t context) = context
61 type Context (t context) = context
51− type FromExprs (t context) = GFromExprs t
62+ type Unreify (t context) = t (UnwrapReify context)
52− type Transpose to (t context) = t to
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+
53
77
54− fromColumns = gfromColumns contextSing
78+instance
55− toColumns = gtoColumns contextSing
79+ ( Rel8able t
56− fromResult = gfromResult @t
80+ , Labelable from, Reifiable from
57− toResult = gtoResult @t
81+ , Labelable to, Reifiable to
82+ , Congruent (t from) (t to)
83+ )
84+ => Recontextualize from to (t from) (t to)
58
85
59
86
60instance
87instance
61 ( context ~ Expr
88 ( context ~ Expr
62 , Rel8able t
89 , Rel8able t
63− , HConstrainTable (Columns (t context)) (Sql DBEq)
90+ , HConstrainTable (Columns (t context)) (ConstrainDBType DBEq)
64 )
91 )
65 => EqTable (t context)
92 => EqTable (t context)
66 where
93 where
67− eqTable = hdicts @(Columns (t context)) @(Sql DBEq)
94+ eqTable = hdicts @(Columns (t context)) @(ConstrainDBType DBEq)
68
95
69
96
70instance
97instance
71 ( context ~ Expr
98 ( context ~ Expr
72 , Rel8able t
99 , Rel8able t
73− , HConstrainTable (Columns (t context)) (Sql DBEq)
100+ , HConstrainTable (Columns (t context)) (ConstrainDBType DBEq)
74− , HConstrainTable (Columns (t context)) (Sql DBOrd)
101+ , HConstrainTable (Columns (t context)) (ConstrainDBType DBOrd)
75 )
102 )
76 => OrdTable (t context)
103 => OrdTable (t context)
77 where
104 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
79
109
80
110
81instance
111instance
82− ( Rel8able t', t' ~ Choose (Algebra t) t
83− , x ~ t' Expr
112+ ( x ~ t' Expr
84 , result ~ Result
113 , result ~ Result
114+ , ToExprs' (Algebra t) t' t
85 )
115 )
86 => ToExprs x (t result)
116 => ToExprs x (t result)
117+ where
118+ fromResult = fromResult' @(Algebra t) @t'
119+ toResult = toResult' @(Algebra t) @t'
87
120
88
121
89−type Choose :: K.Algebra -> K.Rel8able -> K.Rel8able
122+type FromExprs' :: K.Rel8able -> Type
90−type family Choose algebra t where
123+type family FromExprs' t where
91− Choose 'K.Sum t = ADT t
124+ FromExprs' (ADT t) = t Result
92− Choose 'K.Product t = t
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 @@
1{-# language AllowAmbiguousTypes #-}
1{-# language AllowAmbiguousTypes #-}
2+{-# language DataKinds #-}
3+{-# language DefaultSignatures #-}
4+{-# language DisambiguateRecordFields #-}
2{-# language FlexibleContexts #-}
5{-# language FlexibleContexts #-}
3{-# language FlexibleInstances #-}
6{-# language FlexibleInstances #-}
4{-# language FunctionalDependencies #-}
7{-# language FunctionalDependencies #-}
@@ -7,31 +10,41 @@
7{-# language StandaloneKindSignatures #-}
10{-# language StandaloneKindSignatures #-}
8{-# language TypeApplications #-}
11{-# language TypeApplications #-}
9{-# language TypeFamilies #-}
12{-# language TypeFamilies #-}
13+{-# language TypeOperators #-}
10{-# language UndecidableInstances #-}
14{-# language UndecidableInstances #-}
11
15
12module Rel8.Table.Serialize
16module Rel8.Table.Serialize
13− ( Serializable, lit, litHTable, parse
17+ ( Serializable, lit, parse
14− , ToExprs
18+ , ToExprs(..), FromExprs
19+ , TToExprs
15 )
20 )
16where
21where
17
22
18-- base
23-- base
19−import Data.Functor.Identity ( Identity( Identity ) )
20import Data.Kind ( Constraint, Type )
24import Data.Kind ( Constraint, Type )
21import Data.List.NonEmpty ( NonEmpty )
25import Data.List.NonEmpty ( NonEmpty )
26+import GHC.Generics ( Generic, Rep, from, to )
22import Prelude
27import Prelude
23
28
24-- hasql
29-- hasql
25import qualified Hasql.Decoders as Hasql
30import qualified Hasql.Decoders as Hasql
26
31
27-- rel8
32-- rel8
28−import Rel8.Expr ( Expr )
33+import Rel8.Expr ( Expr, Col( E ) )
29import Rel8.Expr.Serialize ( slitExpr, sparseValue )
34import 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 )
30import Rel8.Schema.HTable ( HTable, htabulate, htabulateA, hfield, hspecs )
42import Rel8.Schema.HTable ( HTable, htabulate, htabulateA, hfield, hspecs )
43+import Rel8.Schema.HTable.Identity ( HIdentity( HType ) )
31import Rel8.Schema.Null ( NotNull, Sql )
44import Rel8.Schema.Null ( NotNull, Sql )
32−import Rel8.Schema.Result ( Result )
45+import Rel8.Schema.Result ( Col( R ), Result )
33−import Rel8.Schema.Spec ( Spec(..) )
46+import Rel8.Schema.Spec ( SSpec(..), KnownSpec )
34−import Rel8.Table ( Table, fromColumns, FromExprs, fromResult, toResult )
47+import Rel8.Table ( Table, Columns, fromColumns, toColumns, TColumns )
35import Rel8.Type ( DBType )
48import Rel8.Type ( DBType )
36
49
37-- semigroupoids
50-- semigroupoids
@@ -40,21 +53,71 @@ import Data.Functor.Apply ( WrappedApplicative(..) )
40
53
41-- | @ToExprs exprs a@ is evidence that the types @exprs@ and @a@ describe
54-- | @ToExprs exprs a@ is evidence that the types @exprs@ and @a@ describe
42-- essentially the same type, but @exprs@ is in the 'Expr' context, and @a@ is
55-- essentially the same type, but @exprs@ is in the 'Expr' context, and @a@ is
43−-- a normal Haskell type.
56+-- in the 'Result' context.
44type ToExprs :: Type -> Type -> Constraint
57type ToExprs :: Type -> Type -> Constraint
45−class Table Expr exprs => ToExprs exprs a
58+class Table Expr exprs => ToExprs exprs a where
46−
59+ fromResult :: Columns exprs (Col Result) -> a
47−
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+
48−instance {-# OVERLAPPABLE #-} (Sql DBType a, x ~ Expr a) => ToExprs x a
101+instance {-# OVERLAPPABLE #-} (Sql DBType a, x ~ Expr a) => ToExprs x a where
49−
102+ fromResult (HType (R a)) = a
50−
103+ toResult = HType . R
104+
105+
51−instance (Sql DBType a, x ~ [a]) => ToExprs (Expr x) [a]
106+instance (Sql DBType a, x ~ [a]) => ToExprs (Expr x) [a] where
107+ fromResult (HType (R a)) = a
108+ toResult = HType . R
52
109
53
110
54instance (Sql DBType a, NotNull a, x ~ Maybe a) => ToExprs (Expr x) (Maybe a)
111instance (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
55
115
56
116
57instance (Sql DBType a, NotNull a, x ~ NonEmpty a) => ToExprs (Expr x) (NonEmpty a)
117instance (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
58
121
59
122
60instance (ToExprs exprs1 a, ToExprs exprs2 b, x ~ (exprs1, exprs2)) =>
123instance (ToExprs exprs1 a, ToExprs exprs2 b, x ~ (exprs1, exprs2)) =>
@@ -116,6 +179,43 @@ instance
116 => ToExprs x (a, b, c, d, e, f, g)
179 => ToExprs x (a, b, c, d, e, f, g)
117
180
118
181
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+
119-- | @Serializable@ witnesses the one-to-one correspondence between the type
219-- | @Serializable@ witnesses the one-to-one correspondence between the type
120-- @sql@, which contains SQL expressions, and the type @haskell@, which
220-- @sql@, which contains SQL expressions, and the type @haskell@, which
121-- contains the Haskell decoding of rows containing @sql@ SQL expressions.
221-- contains the Haskell decoding of rows containing @sql@ SQL expressions.
@@ -128,21 +228,21 @@ instance {-# OVERLAPPING #-} Sql DBType a => Serializable (Expr a) a
128-- | Use @lit@ to turn literal Haskell values into expressions. @lit@ is
228-- | Use @lit@ to turn literal Haskell values into expressions. @lit@ is
129-- capable of lifting single @Expr@s to full tables.
229-- capable of lifting single @Expr@s to full tables.
130lit :: forall exprs a. Serializable exprs a => a -> exprs
230lit :: forall exprs a. Serializable exprs a => a -> exprs
131−lit = fromColumns . litHTable . toResult @_ @exprs
231+lit = fromColumns . litHTable . toResult @exprs
132
232
133
233
134parse :: forall exprs a. Serializable exprs a => Hasql.Row a
234parse :: forall exprs a. Serializable exprs a => Hasql.Row a
135−parse = fromResult @_ @exprs <$> parseHTable
235+parse = fromResult @exprs <$> parseHTable
136
236
137
237
138−litHTable :: HTable t => t Result -> t Expr
238+litHTable :: HTable t => t (Col Result) -> t (Col Expr)
139litHTable as = htabulate $ \field ->
239litHTable as = htabulate $ \field ->
140 case hfield hspecs field of
240 case hfield hspecs field of
141− Spec {nullity, info} -> case hfield as field of
241+ SSpec {nullity, info} -> case hfield as field of
142− Identity value -> slitExpr nullity info value
242+ R value -> E (slitExpr nullity info value)
143
243
144
244
145−parseHTable :: HTable t => Hasql.Row (t Result)
245+parseHTable :: HTable t => Hasql.Row (t (Col Result))
146parseHTable = unwrapApplicative $ htabulateA $ \field ->
246parseHTable = unwrapApplicative $ htabulateA $ \field ->
147 WrapApplicative $ case hfield hspecs field of
247 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 @@
5{-# language FlexibleInstances #-}
5{-# language FlexibleInstances #-}
6{-# language MultiParamTypeClasses #-}
6{-# language MultiParamTypeClasses #-}
7{-# language NamedFieldPuns #-}
7{-# language NamedFieldPuns #-}
8+{-# language RecordWildCards #-}
8{-# language ScopedTypeVariables #-}
9{-# language ScopedTypeVariables #-}
9{-# language StandaloneKindSignatures #-}
10{-# language StandaloneKindSignatures #-}
10{-# language TupleSections #-}
11{-# language TupleSections #-}
@@ -20,49 +21,52 @@ module Rel8.Table.These
20 , isThisTable, isThatTable, isThoseTable
21 , isThisTable, isThatTable, isThoseTable
21 , hasHereTable, hasThereTable
22 , hasHereTable, hasThereTable
22 , justHereTable, justThereTable
23 , justHereTable, justThereTable
23− , alignMaybeTable
24− , aggregateTheseTable
25 , nameTheseTable
24 , nameTheseTable
26 )
25 )
27where
26where
28
27
29-- base
28-- base
29+import Control.Applicative ( liftA2 )
30import Data.Bifunctor ( Bifunctor, bimap )
30import Data.Bifunctor ( Bifunctor, bimap )
31+import Data.Functor.Identity ( runIdentity )
31import Data.Kind ( Type )
32import Data.Kind ( Type )
32−import Data.Maybe ( isJust )
33−import Prelude hiding ( null, undefined )
33+import Prelude hiding ( undefined )
34
34
35-- rel8
35-- rel8
36−import Rel8.Aggregate ( Aggregate )
37import Rel8.Expr ( Expr )
36import Rel8.Expr ( Expr )
38−import Rel8.Expr.Bool ( (&&.), (||.), boolExpr, not_ )
37+import Rel8.Expr.Bool ( (&&.), not_ )
39−import Rel8.Expr.Null ( null, isNonNull )
38+import Rel8.Expr.Null ( isNonNull )
40−import Rel8.Kind.Context ( Reifiable )
39+import Rel8.Schema.Context.Label
40+ ( Labelable
41+ , HLabelable, hlabeler, hunlabeler
42+ )
41−import Rel8.Schema.Context.Nullify ( Nullifiable )
43+import Rel8.Schema.Context.Nullify
44+ ( Nullifiable, ConstrainTag
45+ , HNullifiable, HConstrainTag
46+ , hencodeTag, hdecodeTag
47+ , hnullifier, hunnullifier
48+ )
42−import Rel8.Schema.Dict ( Dict( Dict ) )
49+import Rel8.Schema.HTable ( HTable )
43−import Rel8.Schema.HTable.Label ( hlabel, hrelabel, hunlabel )
50+import Rel8.Schema.HTable.Label ( hlabel, hunlabel )
44import Rel8.Schema.HTable.Identity ( HIdentity(..) )
51import Rel8.Schema.HTable.Identity ( HIdentity(..) )
45−import Rel8.Schema.HTable.Maybe ( HMaybeTable(..) )
52+import Rel8.Schema.HTable.Nullify ( hnullify, hunnullify )
46import Rel8.Schema.HTable.These ( HTheseTable(..) )
53import Rel8.Schema.HTable.These ( HTheseTable(..) )
47−import qualified Rel8.Schema.Kind as K
48import Rel8.Schema.Name ( Name )
54import Rel8.Schema.Name ( Name )
49import Rel8.Table
55import Rel8.Table
50 ( Table, Columns, Context, fromColumns, toColumns
56 ( Table, Columns, Context, fromColumns, toColumns
51− , FromExprs, fromResult, toResult
57+ , reify, unreify
52− , Transpose
53 )
58 )
54import Rel8.Table.Eq ( EqTable, eqTable )
59import Rel8.Table.Eq ( EqTable, eqTable )
55import Rel8.Table.Maybe
60import Rel8.Table.Maybe
56 ( MaybeTable(..)
61 ( MaybeTable(..)
57 , maybeTable, justTable, nothingTable
62 , maybeTable, justTable, nothingTable
58 , isJustTable
63 , isJustTable
59− , aggregateMaybeTable
60 , nameMaybeTable
64 , nameMaybeTable
61 )
65 )
62−import Rel8.Table.Nullify ( Nullify, guard )
63import Rel8.Table.Ord ( OrdTable, ordTable )
66import Rel8.Table.Ord ( OrdTable, ordTable )
64−import Rel8.Table.Projection ( Biprojectable, Projectable, biproject, project )
67+import Rel8.Table.Recontextualize ( Recontextualize )
65−import Rel8.Table.Serialize ( ToExprs )
68+import Rel8.Table.Serialize ( FromExprs, ToExprs, fromResult, toResult )
69+import Rel8.Table.Tag ( Tag(..) )
66import Rel8.Table.Undefined ( undefined )
70import Rel8.Table.Undefined ( undefined )
67import Rel8.Type.Tag ( MaybeTag )
71import Rel8.Type.Tag ( MaybeTag )
68
72
@@ -71,8 +75,7 @@ import Data.Functor.Apply ( Apply, (<.>) )
71import Data.Functor.Bind ( Bind, (>>-) )
75import Data.Functor.Bind ( Bind, (>>-) )
72
76
73-- these
77-- these
74−import Data.These ( These( This, That, These ) )
78+import Data.These ( These )
75−import Data.These.Combinators ( justHere, justThere )
76
79
77
80
78-- | @TheseTable a b@ is a Rel8 table that contains either the table @a@, the
81-- | @TheseTable a b@ is a Rel8 table that contains either the table @a@, the
@@ -82,45 +85,32 @@ import Data.These.Combinators ( justHere, justThere )
82--
85--
83-- @TheseTable@ is operationally the same as Haskell's 'These' type, but
86-- @TheseTable@ is operationally the same as Haskell's 'These' type, but
84-- adapted to work with Rel8.
87-- adapted to work with Rel8.
85−type TheseTable :: K.Context -> Type -> Type -> Type
88+type TheseTable :: Type -> Type -> Type
86−data TheseTable context a b = TheseTable
89+data TheseTable a b = TheseTable
87− { here :: MaybeTable context a
90+ { here :: MaybeTable a
88− , there :: MaybeTable context b
91+ , there :: MaybeTable b
89 }
92 }
90 deriving stock Functor
93 deriving stock Functor
91
94
92
95
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
98 bimap f g (TheseTable a b) = TheseTable (fmap f a) (fmap g b)
97 bimap f g (TheseTable a b) = TheseTable (fmap f a) (fmap g b)
99
98
100
99
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) =>
100+instance (Table Expr a, Semigroup a) => Apply (TheseTable a) where
106− Apply (TheseTable context a)
107− where
108 fs <.> as = TheseTable
101 fs <.> as = TheseTable
109 { here = here fs <> here as
102 { here = here fs <> here as
110 , there = there fs <.> there as
103 , there = there fs <.> there as
111 }
104 }
112
105
113
106
114−instance (context ~ Expr, Table Expr a, Semigroup a) =>
107+instance (Table Expr a, Semigroup a) => Applicative (TheseTable a)
115− Applicative (TheseTable context a)
116 where
108 where
117 pure = thatTable
109 pure = thatTable
118 (<*>) = (<.>)
110 (<*>) = (<.>)
119
111
120
112
121−instance (context ~ Expr, Table Expr a, Semigroup a) =>
113+instance (Table Expr a, Semigroup a) => Bind (TheseTable a) where
122− Bind (TheseTable context a)
123− where
124 TheseTable here1 ma >>- f = case ma >>- f' of
114 TheseTable here1 ma >>- f = case ma >>- f' of
125 mtb -> TheseTable
115 mtb -> TheseTable
126 { here = maybeTable here1 ((here1 <>) . fst) mtb
116 { here = maybeTable here1 ((here1 <>) . fst) mtb
@@ -131,14 +121,12 @@ instance (context ~ Expr, Table Expr a, Semigroup a) =>
131 TheseTable here2 mb -> (here2,) <$> mb
121 TheseTable here2 mb -> (here2,) <$> mb
132
122
133
123
134−instance (context ~ Expr, Table Expr a, Semigroup a) =>
124+instance (Table Expr a, Semigroup a) => Monad (TheseTable a) where
135− Monad (TheseTable context a)
136− where
137 (>>=) = (>>-)
125 (>>=) = (>>-)
138
126
139
127
140−instance (context ~ Expr, Table Expr a, Table Expr b, Semigroup a, Semigroup b) =>
128+instance (Table Expr a, Table Expr b, Semigroup a, Semigroup b) =>
141− Semigroup (TheseTable context a b)
129+ Semigroup (TheseTable a b)
142 where
130 where
143 a <> b = TheseTable
131 a <> b = TheseTable
144 { here = here a <> here b
132 { here = here a <> here b
@@ -148,175 +136,124 @@ instance (context ~ Expr, Table Expr a, Table Expr b, Semigroup a, Semigroup b)
148
136
149instance
137instance
150 ( Table context a, Table context b
138 ( Table context a, Table context b
151− , Reifiable context, context ~ context'
139+ , Labelable context, Nullifiable context, ConstrainTag context MaybeTag
152− )
153− => Table context' (TheseTable context a b)
140+ ) => Table context (TheseTable a b)
154 where
141 where
155− type Columns (TheseTable context a b) = HTheseTable (Columns a) (Columns b)
142+ type Columns (TheseTable a b) = HTheseTable (Columns a) (Columns b)
156− type Context (TheseTable context a b) = Context a
143+ type Context (TheseTable 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− }
170
144
171− fromColumns HTheseTable {hhereTag, hhere, hthereTag, hthere} = TheseTable
145+ toColumns = toColumns2 toColumns toColumns
172− { here = MaybeTable
146+ fromColumns = fromColumns2 fromColumns fromColumns
173− { tag = unHIdentity $ hunlabel hhereTag
147+ reify = liftA2 bimap reify reify
174− , just = fromColumns $ hunlabel hhere
148+ unreify = liftA2 bimap unreify unreify
175− }
176− , there = MaybeTable
177− { tag = unHIdentity $ hunlabel hthereTag
178− , just = fromColumns $ hunlabel hthere
179− }
180− }
181
149
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− }
226
150
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))
227
162
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− }
237
163
164+instance (OrdTable a, OrdTable b) => OrdTable (TheseTable a b) where
165+ ordTable = toColumns2 id id (thoseTable (ordTable @a) (ordTable @b))
238
166
167+
168+type instance FromExprs (TheseTable a b) = These (FromExprs a) (FromExprs b)
169+
170+
239−instance (ToExprs exprs1 a, ToExprs exprs2 b, x ~ TheseTable Expr exprs1 exprs2) =>
171+instance (ToExprs exprs1 a, ToExprs exprs2 b, x ~ TheseTable exprs1 exprs2) =>
240 ToExprs x (These a b)
172 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 {..}
241
188
242
189
243-- | Test if a 'TheseTable' was constructed with 'thisTable'.
190-- | Test if a 'TheseTable' was constructed with 'thisTable'.
244--
191--
245-- Corresponds to 'Data.These.Combinators.isThis'.
192-- Corresponds to 'Data.These.Combinators.isThis'.
246−isThisTable :: TheseTable Expr a b -> Expr Bool
193+isThisTable :: TheseTable a b -> Expr Bool
247isThisTable a = hasHereTable a &&. not_ (hasThereTable a)
194isThisTable a = hasHereTable a &&. not_ (hasThereTable a)
248
195
249
196
250-- | Test if a 'TheseTable' was constructed with 'thatTable'.
197-- | Test if a 'TheseTable' was constructed with 'thatTable'.
251--
198--
252-- Corresponds to 'Data.These.Combinators.isThat'.
199-- Corresponds to 'Data.These.Combinators.isThat'.
253−isThatTable :: TheseTable Expr a b -> Expr Bool
200+isThatTable :: TheseTable a b -> Expr Bool
254isThatTable a = not_ (hasHereTable a) &&. hasThereTable a
201isThatTable a = not_ (hasHereTable a) &&. hasThereTable a
255
202
256
203
257-- | Test if a 'TheseTable' was constructed with 'thoseTable'.
204-- | Test if a 'TheseTable' was constructed with 'thoseTable'.
258--
205--
259-- Corresponds to 'Data.These.Combinators.isThese'.
206-- Corresponds to 'Data.These.Combinators.isThese'.
260−isThoseTable :: TheseTable Expr a b -> Expr Bool
207+isThoseTable :: TheseTable a b -> Expr Bool
261isThoseTable a = hasHereTable a &&. hasThereTable a
208isThoseTable a = hasHereTable a &&. hasThereTable a
262
209
263
210
264-- | Test if the @a@ side of @TheseTable a b@ is present.
211-- | Test if the @a@ side of @TheseTable a b@ is present.
265--
212--
266-- Corresponds to 'Data.These.Combinators.hasHere'.
213-- Corresponds to 'Data.These.Combinators.hasHere'.
267−hasHereTable :: TheseTable Expr a b -> Expr Bool
214+hasHereTable :: TheseTable a b -> Expr Bool
268hasHereTable TheseTable {here} = isJustTable here
215hasHereTable TheseTable {here} = isJustTable here
269
216
270
217
271-- | Test if the @b@ table of @TheseTable a b@ is present.
218-- | Test if the @b@ table of @TheseTable a b@ is present.
272--
219--
273-- Corresponds to 'Data.These.Combinators.hasThere'.
220-- Corresponds to 'Data.These.Combinators.hasThere'.
274−hasThereTable :: TheseTable Expr a b -> Expr Bool
221+hasThereTable :: TheseTable a b -> Expr Bool
275hasThereTable TheseTable {there} = isJustTable there
222hasThereTable TheseTable {there} = isJustTable there
276
223
277
224
278-- | Attempt to project out the @a@ table of a @TheseTable a b@.
225-- | Attempt to project out the @a@ table of a @TheseTable a b@.
279--
226--
280-- Corresponds to 'Data.These.Combinators.justHere'.
227-- Corresponds to 'Data.These.Combinators.justHere'.
281−justHereTable :: TheseTable context a b -> MaybeTable context a
228+justHereTable :: TheseTable a b -> MaybeTable a
282justHereTable = here
229justHereTable = here
283
230
284
231
285-- | Attempt to project out the @b@ table of a @TheseTable a b@.
232-- | Attempt to project out the @b@ table of a @TheseTable a b@.
286--
233--
287-- Corresponds to 'Data.These.Combinators.justThere'.
234-- Corresponds to 'Data.These.Combinators.justThere'.
288−justThereTable :: TheseTable context a b -> MaybeTable context b
235+justThereTable :: TheseTable a b -> MaybeTable b
289justThereTable = there
236justThereTable = there
290
237
291
238
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−
302-- | Construct a @TheseTable@. Corresponds to 'This'.
239-- | 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
304thisTable a = TheseTable (justTable a) nothingTable
241thisTable a = TheseTable (justTable a) nothingTable
305
242
306
243
307-- | Construct a @TheseTable@. Corresponds to 'That'.
244-- | 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
309thatTable b = TheseTable nothingTable (justTable b)
246thatTable b = TheseTable nothingTable (justTable b)
310
247
311
248
312-- | Construct a @TheseTable@. Corresponds to 'These'.
249-- | Construct a @TheseTable@. Corresponds to 'These'.
313−thoseTable :: a -> b -> TheseTable Expr a b
250+thoseTable :: a -> b -> TheseTable a b
314thoseTable a b = TheseTable (justTable a) (justTable b)
251thoseTable a b = TheseTable (justTable a) (justTable b)
315
252
316
253
317-- | Pattern match on a 'TheseTable'. Corresponds to 'these'.
254-- | Pattern match on a 'TheseTable'. Corresponds to 'these'.
318theseTable :: Table Expr c
255theseTable :: 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
320theseTable f g h TheseTable {here, there} =
257theseTable f g h TheseTable {here, there} =
321 maybeTable
258 maybeTable
322 (maybeTable undefined f here)
259 (maybeTable undefined f here)
@@ -324,19 +261,6 @@ theseTable f g h TheseTable {here, there} =
324 there
261 there
325
262
326
263
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−
340-- | Construct a 'TheseTable' in the 'Name' context. This can be useful if you
264-- | Construct a 'TheseTable' in the 'Name' context. This can be useful if you
341-- have a 'TheseTable' that you are storing in a table and need to construct a
265-- have a 'TheseTable' that you are storing in a table and need to construct a
342-- 'TableSchema'.
266-- 'TableSchema'.
@@ -349,9 +273,69 @@ nameTheseTable :: ()
349 -- ^ Names of the columns in the @a@ table.
273 -- ^ Names of the columns in the @a@ table.
350 -> b
274 -> b
351 -- ^ Names of the columns in the @b@ table.
275 -- ^ Names of the columns in the @b@ table.
352− -> TheseTable Name a b
276+ -> TheseTable a b
353nameTheseTable here there a b =
277nameTheseTable here there a b =
354 TheseTable
278 TheseTable
355 { here = nameMaybeTable here a
279 { here = nameMaybeTable here a
356 , there = nameMaybeTable there b
280 , there = nameMaybeTable there b
357 }
281 }
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
11import Prelude hiding ( undefined )
11import Prelude hiding ( undefined )
12
12
13-- rel8
13-- rel8
14−import Rel8.Expr ( Expr )
14+import Rel8.Expr ( Expr, Col( E ) )
15import Rel8.Expr.Null ( snull, unsafeUnnullify )
15import Rel8.Expr.Null ( snull, unsafeUnnullify )
16import Rel8.Schema.HTable ( htabulate, hfield, hspecs )
16import Rel8.Schema.HTable ( htabulate, hfield, hspecs )
17import Rel8.Schema.Null ( Nullity( Null, NotNull ) )
17import Rel8.Schema.Null ( Nullity( Null, NotNull ) )
18−import Rel8.Schema.Spec ( Spec(..) )
18+import Rel8.Schema.Spec ( SSpec(..) )
19import Rel8.Table ( Table, fromColumns )
19import Rel8.Table ( Table, fromColumns )
20
20
21
21
22undefined :: Table Expr a => a
22undefined :: Table Expr a => a
23undefined = fromColumns $ htabulate $ \field -> case hfield hspecs field of
23undefined = fromColumns $ htabulate $ \field -> case hfield hspecs field of
24− Spec {nullity, info} -> case nullity of
24+ SSpec {nullity, info} -> case nullity of
25− Null -> snull info
25+ Null -> E (snull info)
26− NotNull -> unsafeUnnullify (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 #-}
2{-# language FlexibleInstances #-}
1{-# language FlexibleInstances #-}
3{-# language MonoLocalBinds #-}
2{-# language MonoLocalBinds #-}
4{-# language MultiWayIf #-}
3{-# language MultiWayIf #-}
@@ -128,10 +127,10 @@ instance DBType Int64 where
128instance DBType Float where
127instance DBType Float where
129 typeInformation = TypeInformation
128 typeInformation = TypeInformation
130 { encode = \x -> Opaleye.ConstExpr
129 { encode = \x -> Opaleye.ConstExpr
131− if | x == (1 / 0) -> Opaleye.OtherLit "'Infinity'"
130+ if | x == (1 /0) -> Opaleye.OtherLit "'Infinity'"
132− | isNaN x -> Opaleye.OtherLit "'NaN'"
131+ | isNaN x -> Opaleye.OtherLit "'NaN'"
133− | x == (-1 / 0) -> Opaleye.OtherLit "'-Infinity'"
132+ | x == (-1/0) -> Opaleye.OtherLit "'-Infinity'"
134− | otherwise -> Opaleye.NumericLit $ realToFrac x
133+ | otherwise -> Opaleye.NumericLit $ realToFrac x
135 , decode = Hasql.float4
134 , decode = Hasql.float4
136 , typeName = "float4"
135 , typeName = "float4"
137 }
136 }
@@ -141,10 +140,10 @@ instance DBType Float where
141instance DBType Double where
140instance DBType Double where
142 typeInformation = TypeInformation
141 typeInformation = TypeInformation
143 { encode = \x -> Opaleye.ConstExpr
142 { encode = \x -> Opaleye.ConstExpr
144− if | x == (1 / 0) -> Opaleye.OtherLit "'Infinity'"
143+ if | x == (1 /0) -> Opaleye.OtherLit "'Infinity'"
145− | isNaN x -> Opaleye.OtherLit "'NaN'"
144+ | isNaN x -> Opaleye.OtherLit "'NaN'"
146− | x == (-1 / 0) -> Opaleye.OtherLit "'-Infinity'"
145+ | x == (-1/0) -> Opaleye.OtherLit "'-Infinity'"
147− | otherwise -> Opaleye.NumericLit $ realToFrac x
146+ | otherwise -> Opaleye.NumericLit $ realToFrac x
148 , decode = Hasql.float8
147 , decode = Hasql.float8
149 , typeName = "float8"
148 , typeName = "float8"
150 }
149 }
src/Rel8/Type/Composite.hs+19−20
@@ -20,7 +20,6 @@ where
20
20
21-- base
21-- base
22import Data.Functor.Const ( Const( Const ), getConst )
22import Data.Functor.Const ( Const( Const ), getConst )
23−import Data.Functor.Identity ( Identity( Identity ) )
24import Data.Kind ( Constraint, Type )
23import Data.Kind ( Constraint, Type )
25import Prelude
24import Prelude
26
25
@@ -31,19 +30,19 @@ import qualified Hasql.Decoders as Hasql
31import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
30import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
32
31
33-- rel8
32-- rel8
34−import Rel8.Expr ( Expr )
33+import Rel8.Expr ( Col( E ), Expr )
35import Rel8.Expr.Opaleye ( castExpr, fromPrimExpr, toPrimExpr )
34import Rel8.Expr.Opaleye ( castExpr, fromPrimExpr, toPrimExpr )
36−import Rel8.Schema.HTable ( HTable, hfield, hspecs, htabulate, htabulateA )
35+import Rel8.Schema.HTable ( hfield, hspecs, htabulate, htabulateA )
37−import Rel8.Schema.Name ( Name( Name ) )
36+import Rel8.Schema.Name ( Col( N ), Name( Name ) )
38import Rel8.Schema.Null ( Nullity( Null, NotNull ) )
37import Rel8.Schema.Null ( Nullity( Null, NotNull ) )
39−import Rel8.Schema.Result ( Result )
38+import Rel8.Schema.Result ( Col( R ), Result )
40−import Rel8.Schema.Spec ( Spec( Spec, nullity, info ) )
39+import Rel8.Schema.Spec ( SSpec( SSpec, nullity, info ) )
41−import Rel8.Table ( fromColumns, toColumns, fromResult, toResult )
40+import Rel8.Table ( Table, fromColumns, toColumns )
42import Rel8.Table.Eq ( EqTable )
41import Rel8.Table.Eq ( EqTable )
43−import Rel8.Table.HKD ( HKD, HKDable )
42+import Rel8.Table.HKD ( HKD, HKDable, fromHKD, toHKD )
44import Rel8.Table.Ord ( OrdTable )
43import Rel8.Table.Ord ( OrdTable )
45import Rel8.Table.Rel8able ()
44import Rel8.Table.Rel8able ()
46−import Rel8.Table.Serialize ( litHTable )
45+import Rel8.Table.Serialize ( lit )
47import Rel8.Type ( DBType, typeInformation )
46import Rel8.Type ( DBType, typeInformation )
48import Rel8.Type.Eq ( DBEq )
47import Rel8.Type.Eq ( DBEq )
49import Rel8.Type.Information ( TypeInformation(..) )
48import Rel8.Type.Information ( TypeInformation(..) )
@@ -68,8 +67,8 @@ newtype Composite a = Composite
68
67
69instance DBComposite a => DBType (Composite a) where
68instance DBComposite a => DBType (Composite a) where
70 typeInformation = TypeInformation
69 typeInformation = TypeInformation
71− { decode = Hasql.composite (Composite . fromResult @_ @(HKD a Expr) <$> decoder)
70+ { decode = Hasql.composite (Composite . fromHKD <$> decoder)
72− , encode = encoder . litHTable . toResult @_ @(HKD a Expr) . unComposite
71+ , encode = encoder . lit . toColumns . toHKD . unComposite
73 , typeName = compositeTypeName @a
72 , typeName = compositeTypeName @a
74 }
73 }
75
74
@@ -104,7 +103,7 @@ class (DBType a, HKDable a) => DBComposite a where
104-- single column expression, by combining them into a PostgreSQL composite
103-- single column expression, by combining them into a PostgreSQL composite
105-- type.
104-- type.
106compose :: DBComposite a => HKD a Expr -> Expr a
105compose :: DBComposite a => HKD a Expr -> Expr a
107−compose = castExpr . fromPrimExpr . encoder . toColumns
106+compose = castExpr . fromPrimExpr . encoder
108
107
109
108
110-- | Expand a composite type into a 'HKD'.
109-- | Expand a composite type into a 'HKD'.
@@ -113,23 +112,23 @@ compose = castExpr . fromPrimExpr . encoder . toColumns
113decompose :: forall a. DBComposite a => Expr a -> HKD a Expr
112decompose :: forall a. DBComposite a => Expr a -> HKD a Expr
114decompose (toPrimExpr -> a) = fromColumns $ htabulate \field ->
113decompose (toPrimExpr -> a) = fromColumns $ htabulate \field ->
115 case hfield names field of
114 case hfield names field of
116− Name name -> case hfield hspecs field of
115+ N (Name name) -> case hfield hspecs field of
117− Spec {} -> fromPrimExpr $ Opaleye.CompositeExpr a name
116+ SSpec {} -> E $ fromPrimExpr $ Opaleye.CompositeExpr a name
118 where
117 where
119 names = toColumns (compositeFields @a)
118 names = toColumns (compositeFields @a)
120
119
121
120
122−decoder :: HTable t => Hasql.Composite (t Result)
121+decoder :: Table Result a => Hasql.Composite a
123−decoder = unwrapApplicative $ htabulateA \field ->
122+decoder = fmap fromColumns $ unwrapApplicative $ htabulateA \field ->
124 case hfield hspecs field of
123 case hfield hspecs field of
125− Spec {nullity, info} -> WrapApplicative $ Identity <$>
124+ SSpec {nullity, info} -> WrapApplicative $ R <$>
126 case nullity of
125 case nullity of
127 Null -> Hasql.field $ Hasql.nullable $ decode info
126 Null -> Hasql.field $ Hasql.nullable $ decode info
128 NotNull -> Hasql.field $ Hasql.nonNullable $ decode info
127 NotNull -> Hasql.field $ Hasql.nonNullable $ decode info
129
128
130
129
131−encoder :: HTable t => t Expr -> Opaleye.PrimExpr
130+encoder :: Table Expr a => a -> Opaleye.PrimExpr
132−encoder a = Opaleye.FunExpr "ROW" exprs
131+encoder (toColumns -> a) = Opaleye.FunExpr "ROW" exprs
133 where
132 where
134 exprs = getConst $ htabulateA \field -> case hfield a field of
133 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
106
106
107-- | Types that are sum types, where each constructor is unary (that is, has no
107-- | Types that are sum types, where each constructor is unary (that is, has no
108-- fields).
108-- fields).
109−type Enumable :: Type -> Constraint
110class (Generic a, GEnumable (Rep a)) => Enumable a
109class (Generic a, GEnumable (Rep a)) => Enumable a
111instance (Generic a, GEnumable (Rep a)) => Enumable a
110instance (Generic a, GEnumable (Rep a)) => Enumable a
112
111
src/Rel8/Type/JSONBEncoded.hs−4
@@ -1,5 +1,3 @@
1−{-# language StandaloneKindSignatures #-}
2−
3module Rel8.Type.JSONBEncoded ( JSONBEncoded(..) ) where
1module Rel8.Type.JSONBEncoded ( JSONBEncoded(..) ) where
4
2
5-- aeson
3-- aeson
@@ -8,7 +6,6 @@ import Data.Aeson.Types ( parseEither )
8
6
9-- base
7-- base
10import Data.Bifunctor ( first )
8import Data.Bifunctor ( first )
11−import Data.Kind ( Type )
12import Prelude
9import Prelude
13
10
14-- hasql
11-- hasql
@@ -23,7 +20,6 @@ import Data.Text ( pack )
23
20
24
21
25-- | Like 'Rel8.JSONEncoded', but works for @jsonb@ columns.
22-- | Like 'Rel8.JSONEncoded', but works for @jsonb@ columns.
26−type JSONBEncoded :: Type -> Type
27newtype JSONBEncoded a = JSONBEncoded { fromJSONBEncoded :: a }
23newtype JSONBEncoded a = JSONBEncoded { fromJSONBEncoded :: a }
28
24
29
25
src/Rel8/Type/JSONEncoded.hs−4
@@ -1,5 +1,3 @@
1−{-# language StandaloneKindSignatures #-}
2−
3module Rel8.Type.JSONEncoded ( JSONEncoded(..) ) where
1module Rel8.Type.JSONEncoded ( JSONEncoded(..) ) where
4
2
5-- aeson
3-- aeson
@@ -7,7 +5,6 @@ import Data.Aeson ( FromJSON, ToJSON, parseJSON, toJSON )
7import Data.Aeson.Types ( parseEither )
5import Data.Aeson.Types ( parseEither )
8
6
9-- base
7-- base
10−import Data.Kind ( Type )
11import Prelude
8import Prelude
12
9
13-- rel8
10-- rel8
@@ -18,7 +15,6 @@ import Rel8.Type.Information ( parseTypeInformation )
18-- | A deriving-via helper type for column types that store a Haskell value
15-- | A deriving-via helper type for column types that store a Haskell value
19-- using a JSON encoding described by @aeson@'s 'ToJSON' and 'FromJSON' type
16-- using a JSON encoding described by @aeson@'s 'ToJSON' and 'FromJSON' type
20-- classes.
17-- classes.
21−type JSONEncoded :: Type -> Type
22newtype JSONEncoded a = JSONEncoded { fromJSONEncoded :: a }
18newtype JSONEncoded a = JSONEncoded { fromJSONEncoded :: a }
23
19
24
20
src/Rel8/Type/Num.hs+1−4
@@ -18,7 +18,6 @@ import Prelude
18
18
19-- rel8
19-- rel8
20import Rel8.Type ( DBType )
20import Rel8.Type ( DBType )
21−import Rel8.Type.Ord ( DBOrd )
22
21
23-- scientific
22-- scientific
24import Data.Scientific ( Scientific )
23import Data.Scientific ( Scientific )
@@ -40,14 +39,13 @@ instance DBNum Scientific
40-- expressions. This is a Rel8 concept, and allows us to provide
39-- expressions. This is a Rel8 concept, and allows us to provide
41-- 'fromIntegral'.
40-- 'fromIntegral'.
42type DBIntegral :: Type -> Constraint
41type DBIntegral :: Type -> Constraint
43−class (DBNum a, DBOrd a) => DBIntegral a
42+class DBNum a => DBIntegral a
44instance DBIntegral Int16
43instance DBIntegral Int16
45instance DBIntegral Int32
44instance DBIntegral Int32
46instance DBIntegral Int64
45instance DBIntegral Int64
47
46
48
47
49-- | The class of database types that support the @/@ operator.
48-- | The class of database types that support the @/@ operator.
50−type DBFractional :: Type -> Constraint
51class DBNum a => DBFractional a
49class DBNum a => DBFractional a
52instance DBFractional Float
50instance DBFractional Float
53instance DBFractional Double
51instance DBFractional Double
@@ -55,7 +53,6 @@ instance DBFractional Scientific
55
53
56
54
57-- | The class of database types that support the @/@ operator.
55-- | The class of database types that support the @/@ operator.
58−type DBFloating :: Type -> Constraint
59class DBFractional a => DBFloating a
56class DBFractional a => DBFloating a
60instance DBFloating Float
57instance DBFloating Float
61instance DBFloating Double
58instance DBFloating Double
src/Rel8/Type/Ord.hs+2
@@ -75,6 +75,7 @@ instance Sql DBOrd a => DBOrd (NonEmpty a)
75-- | The class of database types that support the @max@ aggregation function.
75-- | The class of database types that support the @max@ aggregation function.
76type DBMax :: Type -> Constraint
76type DBMax :: Type -> Constraint
77class DBOrd a => DBMax a
77class DBOrd a => DBMax a
78+instance DBMax Bool
78instance DBMax Char
79instance DBMax Char
79instance DBMax Int16
80instance DBMax Int16
80instance DBMax Int32
81instance DBMax Int32
@@ -100,6 +101,7 @@ instance Sql DBMax a => DBMax (NonEmpty a)
100-- | The class of database types that support the @min@ aggregation function.
101-- | The class of database types that support the @min@ aggregation function.
101type DBMin :: Type -> Constraint
102type DBMin :: Type -> Constraint
102class DBOrd a => DBMin a
103class DBOrd a => DBMin a
104+instance DBMin Bool
103instance DBMin Char
105instance DBMin Char
104instance DBMin Int16
106instance DBMin Int16
105instance DBMin Int32
107instance DBMin Int32
src/Rel8/Type/ReadShow.hs−3
@@ -1,12 +1,10 @@
1{-# language ScopedTypeVariables #-}
1{-# language ScopedTypeVariables #-}
2−{-# language StandaloneKindSignatures #-}
3{-# language TypeApplications #-}
2{-# language TypeApplications #-}
4{-# language ViewPatterns #-}
3{-# language ViewPatterns #-}
5
4
6module Rel8.Type.ReadShow ( ReadShow(..) ) where
5module Rel8.Type.ReadShow ( ReadShow(..) ) where
7
6
8-- base
7-- base
9−import Data.Kind ( Type )
10import Data.Proxy ( Proxy( Proxy ) )
8import Data.Proxy ( Proxy( Proxy ) )
11import Data.Typeable ( Typeable, typeRep )
9import Data.Typeable ( Typeable, typeRep )
12import Prelude
10import Prelude
@@ -22,7 +20,6 @@ import qualified Data.Text as Text
22
20
23-- | A deriving-via helper type for column types that store a Haskell value
21-- | A deriving-via helper type for column types that store a Haskell value
24-- using a Haskell's 'Read' and 'Show' type classes.
22-- using a Haskell's 'Read' and 'Show' type classes.
25−type ReadShow :: Type -> Type
26newtype ReadShow a = ReadShow { fromReadShow :: a }
23newtype ReadShow a = ReadShow { fromReadShow :: a }
27
24
28
25
src/Rel8/Type/Tag.hs+1−2
@@ -21,7 +21,7 @@ import Prelude
21import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
21import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
22
22
23-- rel8
23-- rel8
24−import Rel8.Expr ( Expr )
24+import {-# SOURCE #-} Rel8.Expr ( Expr )
25import Rel8.Expr.Eq ( (==.) )
25import Rel8.Expr.Eq ( (==.) )
26import Rel8.Expr.Opaleye ( zipPrimExprsWith )
26import Rel8.Expr.Opaleye ( zipPrimExprsWith )
27import Rel8.Expr.Serialize ( litExpr )
27import Rel8.Expr.Serialize ( litExpr )
@@ -90,7 +90,6 @@ instance DBMonoid MaybeTag where
90 memptyExpr = litExpr mempty
90 memptyExpr = litExpr mempty
91
91
92
92
93−type Tag :: Type
94newtype Tag = Tag Text
93newtype Tag = Tag Text
95 deriving newtype
94 deriving newtype
96 ( Eq, Ord, Read, Show
95 ( 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 #-}
2{-# language BlockArguments #-}
1{-# language BlockArguments #-}
2+{-# language DataKinds #-}
3{-# language DeriveAnyClass #-}
3{-# language DeriveAnyClass #-}
4{-# language DeriveGeneric #-}
4{-# language DeriveGeneric #-}
5{-# language DerivingStrategies #-}
5{-# language DerivingStrategies #-}
6+{-# language DisambiguateRecordFields #-}
6{-# language FlexibleContexts #-}
7{-# language FlexibleContexts #-}
7{-# language FlexibleInstances #-}
8{-# language FlexibleInstances #-}
8−{-# language MonoLocalBinds #-}
9+{-# language GADTs #-}
9{-# language NamedFieldPuns #-}
10{-# language NamedFieldPuns #-}
10{-# language OverloadedStrings #-}
11{-# language OverloadedStrings #-}
11{-# language RecordWildCards #-}
12{-# language RecordWildCards #-}
@@ -13,15 +14,17 @@
13{-# language StandaloneDeriving #-}
14{-# language StandaloneDeriving #-}
14{-# language TypeApplications #-}
15{-# language TypeApplications #-}
15
16
17+{-# options_ghc -fno-warn-redundant-constraints #-}
18+
16module Main
19module Main
17 ( main
20 ( main
18 )
21 )
19where
22where
20
23
21-- base
24-- base
22−import Control.Applicative ( empty, liftA2, liftA3 )
25+import Control.Applicative ( liftA2, liftA3 )
23−import Control.Exception ( bracket, throwIO )
26+import Control.Monad (void)
24−import Control.Monad ( (>=>), void )
27+import Control.Monad.IO.Class ( MonadIO, liftIO )
25import Data.Bifunctor ( bimap )
28import Data.Bifunctor ( bimap )
26import Data.Foldable ( for_ )
29import Data.Foldable ( for_ )
27import Data.Int ( Int32, Int64 )
30import Data.Int ( Int32, Int64 )
@@ -35,27 +38,25 @@ import GHC.Generics ( Generic )
35import qualified Data.ByteString.Lazy
38import qualified Data.ByteString.Lazy
36
39
37-- case-insensitive
40-- case-insensitive
38−import Data.CaseInsensitive ( mk )
41+import Data.CaseInsensitive (mk)
39
42
40-- containers
43-- containers
41−import Data.Containers.ListUtils ( nubOrdOn )
42import qualified Data.Map.Strict as Map
44import qualified Data.Map.Strict as Map
43
45
44-- hasql
46-- hasql
45−import Hasql.Connection ( Connection, acquire, release )
47+import Hasql.Connection ( Connection, acquire, release )
46import Hasql.Session ( sql, run )
48import Hasql.Session ( sql, run )
47
49
48−-- hasql-transaction
49−import Hasql.Transaction ( Transaction, condemn, statement )
50−import qualified Hasql.Transaction.Sessions as Hasql
51−
52-- hedgehog
50-- hedgehog
53import Hedgehog ( property, (===), forAll, cover, diff, evalM, PropertyT, TestT, test, Gen )
51import Hedgehog ( property, (===), forAll, cover, diff, evalM, PropertyT, TestT, test, Gen )
54import qualified Hedgehog.Gen as Gen
52import qualified Hedgehog.Gen as Gen
55import qualified Hedgehog.Range as Range
53import qualified Hedgehog.Range as Range
56
54
57−-- mmorph
55+-- lifted-base
56+import Control.Exception.Lifted ( bracket, throwIO, bracket_ )
57+
58+-- monad-control
58−import Control.Monad.Morph ( hoist )
59+import Control.Monad.Trans.Control ( MonadBaseControl )
59
60
60-- rel8
61-- rel8
61import Rel8 ( Result )
62import Rel8 ( Result )
@@ -71,16 +72,12 @@ import Test.Tasty
71import Test.Tasty.Hedgehog ( testProperty )
72import Test.Tasty.Hedgehog ( testProperty )
72
73
73-- text
74-- text
74−import Data.Text ( Text, pack, unpack )
75+import Data.Text (Text, pack)
75import qualified Data.Text.Lazy
76import qualified Data.Text.Lazy
76−import Data.Text.Encoding ( decodeUtf8 )
77
77
78-- time
78-- time
79import Data.Time
79import Data.Time
80
80
81−-- transformers
82−import Control.Monad.Trans.Class ( lift )
83−
84-- tmp-postgres
81-- tmp-postgres
85import qualified Database.Postgres.Temp as TmpPostgres
82import qualified Database.Postgres.Temp as TmpPostgres
86
83
@@ -116,17 +113,14 @@ tests =
116 , testCatMaybeTable getTestDatabase
113 , testCatMaybeTable getTestDatabase
117 , testCatMaybe getTestDatabase
114 , testCatMaybe getTestDatabase
118 , testMaybeTable getTestDatabase
115 , testMaybeTable getTestDatabase
119− , testAggregateMaybeTable getTestDatabase
120 , testNestedTables getTestDatabase
116 , testNestedTables getTestDatabase
121 , testMaybeTableApplicative getTestDatabase
117 , testMaybeTableApplicative getTestDatabase
122 , testLogicalFixities getTestDatabase
118 , testLogicalFixities getTestDatabase
123 , testUpdate getTestDatabase
119 , testUpdate getTestDatabase
124 , testDelete getTestDatabase
120 , testDelete getTestDatabase
125− , testUpsert getTestDatabase
126 , testSelectNestedPairs getTestDatabase
121 , testSelectNestedPairs getTestDatabase
127 , testSelectArray getTestDatabase
122 , testSelectArray getTestDatabase
128 , testNestedMaybeTable getTestDatabase
123 , testNestedMaybeTable getTestDatabase
129− , testEvaluate getTestDatabase
130 ]
124 ]
131
125
132 where
126 where
@@ -138,29 +132,25 @@ tests =
138 flip run conn do
132 flip run conn do
139 sql "CREATE EXTENSION citext"
133 sql "CREATE EXTENSION citext"
140 sql "CREATE TABLE test_table ( column1 text not null, column2 bool not null )"
134 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"
143
135
144 return db
136 return db
145
137
146 stopTestDatabase = TmpPostgres.stop
138 stopTestDatabase = TmpPostgres.stop
147
139
148
140
149−connect :: TmpPostgres.DB -> IO Connection
150−connect = acquire . TmpPostgres.toConnectionString >=> either (maybe empty (fail . unpack . decodeUtf8)) pure
151−
152−
153databasePropertyTest
141databasePropertyTest
154 :: TestName
142 :: TestName
155− -> ((TestT Transaction () -> PropertyT IO ()) -> PropertyT IO ())
143+ -> (((Connection -> TestT IO ()) -> PropertyT IO ()) -> PropertyT IO ())
156 -> IO TmpPostgres.DB -> TestTree
144 -> IO TmpPostgres.DB -> TestTree
157databasePropertyTest testName f getTestDatabase =
145databasePropertyTest testName f getTestDatabase =
158− withResource (connect =<< getTestDatabase) release $ \c ->
146+ withResource connect release $ \c ->
159 testProperty testName $ property do
147 testProperty testName $ property do
160− connection <- lift c
148+ connection <- liftIO c
161− f $ test . hoist \m -> do
149+ f \g -> test $ rollingBack connection $ g connection
162− e <- run (Hasql.transaction Hasql.Serializable Hasql.Write (m <* condemn)) connection
150+
163− either throwIO pure e
151+ where
152+
153+ connect = either (error . show) return =<< acquire . TmpPostgres.toConnectionString =<< getTestDatabase
164
154
165
155
166data TestTable f = TestTable
156data TestTable f = TestTable
@@ -192,17 +182,18 @@ testSelectTestTable :: IO TmpPostgres.DB -> TestTree
192testSelectTestTable = databasePropertyTest "Can SELECT TestTable" \transaction -> do
182testSelectTestTable = databasePropertyTest "Can SELECT TestTable" \transaction -> do
193 rows <- forAll $ Gen.list (Range.linear 0 10) genTestTable
183 rows <- forAll $ Gen.list (Range.linear 0 10) genTestTable
194
184
195− transaction do
185+ transaction \connection -> do
196− selected <- lift do
186+ void do
187+ liftIO $ Rel8.insert connection
197− statement () $ Rel8.insert Rel8.Insert
188+ Rel8.Insert
198− { into = testTableSchema
189+ { into = testTableSchema
199− , rows = Rel8.values $ map Rel8.lit rows
190+ , rows = map Rel8.lit rows
200− , onConflict = Rel8.DoNothing
191+ , onConflict = Rel8.DoNothing
201− , returning = pure ()
192+ , returning = Rel8.NumberOfRowsAffected
202− }
193+ }
203
194
204− statement () $ Rel8.select do
195+ selected <- liftIO $ Rel8.select connection do
205− Rel8.each testTableSchema
196+ Rel8.each testTableSchema
206
197
207 sort selected === sort rows
198 sort selected === sort rows
208
199
@@ -219,12 +210,11 @@ testWhere_ = databasePropertyTest "WHERE (Rel8.where_)" \transaction -> do
219
210
220 let expected = filter (\t -> testTableColumn2 t == magicBool) rows
211 let expected = filter (\t -> testTableColumn2 t == magicBool) rows
221
212
222− transaction do
213+ transaction \connection -> do
223− selected <- lift do
224− statement () $ Rel8.select do
214+ selected <- liftIO $ Rel8.select connection do
225− t <- Rel8.values $ Rel8.lit <$> rows
215+ t <- Rel8.values $ Rel8.lit <$> rows
226− Rel8.where_ $ testTableColumn2 t Rel8.==. Rel8.lit magicBool
216+ Rel8.where_ $ testTableColumn2 t Rel8.==. Rel8.lit magicBool
227− return t
217+ return t
228
218
229 sort selected === sort expected
219 sort selected === sort expected
230
220
@@ -237,12 +227,11 @@ testFilter :: IO TmpPostgres.DB -> TestTree
237testFilter = databasePropertyTest "filter" \transaction -> do
227testFilter = databasePropertyTest "filter" \transaction -> do
238 rows <- forAll $ Gen.list (Range.linear 1 10) genTestTable
228 rows <- forAll $ Gen.list (Range.linear 1 10) genTestTable
239
229
240− transaction do
230+ transaction \connection -> do
241 let expected = filter testTableColumn2 rows
231 let expected = filter testTableColumn2 rows
242
232
243− selected <- lift do
233+ selected <- liftIO $ Rel8.select connection
244− statement () $ Rel8.select do
245− Rel8.filter testTableColumn2 =<< Rel8.values (Rel8.lit <$> rows)
234+ $ Rel8.filter testTableColumn2 =<< Rel8.values (Rel8.lit <$> rows)
246
235
247 sort selected === sort expected
236 sort selected === sort expected
248
237
@@ -257,10 +246,9 @@ testLimit = databasePropertyTest "LIMIT (Rel8.limit)" \transaction -> do
257
246
258 n <- forAll $ Gen.integral (Range.linear 0 10)
247 n <- forAll $ Gen.integral (Range.linear 0 10)
259
248
260− transaction do
249+ transaction \connection -> do
261− selected <- lift do
250+ selected <- liftIO $ Rel8.select connection do
262− statement () $ Rel8.select do
263− Rel8.limit n $ Rel8.values (Rel8.lit <$> rows)
251+ Rel8.limit n $ Rel8.values (Rel8.lit <$> rows)
264
252
265 diff (length selected) (<=) (fromIntegral n)
253 diff (length selected) (<=) (fromIntegral n)
266
254
@@ -278,10 +266,9 @@ testUnion = databasePropertyTest "UNION (Rel8.union)" \transaction -> evalM do
278 left <- forAll $ Gen.list (Range.linear 0 10) genTestTable
266 left <- forAll $ Gen.list (Range.linear 0 10) genTestTable
279 right <- forAll $ Gen.list (Range.linear 0 10) genTestTable
267 right <- forAll $ Gen.list (Range.linear 0 10) genTestTable
280
268
281− transaction do
269+ transaction \connection -> do
282− selected <- lift do
270+ selected <- liftIO $ Rel8.select connection do
283− statement () $ Rel8.select do
284− Rel8.values (Rel8.lit <$> nub left) `Rel8.union` Rel8.values (Rel8.lit <$> nub right)
271+ Rel8.values (Rel8.lit <$> nub left) `Rel8.union` Rel8.values (Rel8.lit <$> nub right)
285
272
286 sort selected === sort (nub (left ++ right))
273 sort selected === sort (nub (left ++ right))
287
274
@@ -290,11 +277,9 @@ testDistinct :: IO TmpPostgres.DB -> TestTree
290testDistinct = databasePropertyTest "DISTINCT (Rel8.distinct)" \transaction -> do
277testDistinct = databasePropertyTest "DISTINCT (Rel8.distinct)" \transaction -> do
291 rows <- forAll $ Gen.list (Range.linear 0 10) genTestTable
278 rows <- forAll $ Gen.list (Range.linear 0 10) genTestTable
292
279
293− transaction do
280+ transaction \connection -> do
294− selected <- lift do
295− statement () $ Rel8.select do
296− Rel8.distinct do
281+ selected <- liftIO $ Rel8.select connection $ Rel8.distinct do
297− Rel8.values (Rel8.lit <$> rows)
282+ Rel8.values (Rel8.lit <$> rows)
298
283
299 sort selected === nub (sort rows)
284 sort selected === nub (sort rows)
300
285
@@ -307,10 +292,8 @@ testExists :: IO TmpPostgres.DB -> TestTree
307testExists = databasePropertyTest "EXISTS (Rel8.exists)" \transaction -> do
292testExists = databasePropertyTest "EXISTS (Rel8.exists)" \transaction -> do
308 rows <- forAll $ Gen.list (Range.linear 0 10) genTestTable
293 rows <- forAll $ Gen.list (Range.linear 0 10) genTestTable
309
294
310− transaction do
295+ transaction \connection -> do
311− exists <- lift do
312− statement () $ Rel8.select do
313− Rel8.exists $ Rel8.values $ Rel8.lit <$> rows
296+ exists <- liftIO $ Rel8.select connection $ Rel8.exists $ Rel8.values $ Rel8.lit <$> rows
314
297
315 case rows of
298 case rows of
316 [] -> exists === [False]
299 [] -> exists === [False]
@@ -321,10 +304,9 @@ testOptional :: IO TmpPostgres.DB -> TestTree
321testOptional = databasePropertyTest "Rel8.optional" \transaction -> do
304testOptional = databasePropertyTest "Rel8.optional" \transaction -> do
322 rows <- forAll $ Gen.list (Range.linear 0 10) genTestTable
305 rows <- forAll $ Gen.list (Range.linear 0 10) genTestTable
323
306
324− transaction do
307+ transaction \connection -> do
325− selected <- lift do
308+ selected <- liftIO $ Rel8.select connection do
326− statement () $ Rel8.select do
327− Rel8.optional $ Rel8.values (Rel8.lit <$> rows)
309+ Rel8.optional $ Rel8.values (Rel8.lit <$> rows)
328
310
329 case rows of
311 case rows of
330 [] -> selected === [Nothing]
312 [] -> selected === [Nothing]
@@ -335,10 +317,9 @@ testAnd :: IO TmpPostgres.DB -> TestTree
335testAnd = databasePropertyTest "AND (&&.)" \transaction -> do
317testAnd = databasePropertyTest "AND (&&.)" \transaction -> do
336 (x, y) <- forAll $ liftA2 (,) Gen.bool Gen.bool
318 (x, y) <- forAll $ liftA2 (,) Gen.bool Gen.bool
337
319
338− transaction do
320+ transaction \connection -> do
339− [result] <- lift do
321+ [result] <- liftIO $ Rel8.select connection $ pure $
340− statement () $ Rel8.select do
341− pure $ Rel8.lit x Rel8.&&. Rel8.lit y
322+ Rel8.lit x Rel8.&&. Rel8.lit y
342
323
343 result === (x && y)
324 result === (x && y)
344
325
@@ -347,10 +328,9 @@ testOr :: IO TmpPostgres.DB -> TestTree
347testOr = databasePropertyTest "OR (||.)" \transaction -> do
328testOr = databasePropertyTest "OR (||.)" \transaction -> do
348 (x, y) <- forAll $ liftA2 (,) Gen.bool Gen.bool
329 (x, y) <- forAll $ liftA2 (,) Gen.bool Gen.bool
349
330
350− transaction do
331+ transaction \connection -> do
351− [result] <- lift do
352− statement () $ Rel8.select $ pure $
332+ [result] <- liftIO $ Rel8.select connection $ pure $
353− Rel8.lit x Rel8.||. Rel8.lit y
333+ Rel8.lit x Rel8.||. Rel8.lit y
354
334
355 result === (x || y)
335 result === (x || y)
356
336
@@ -359,10 +339,9 @@ testLogicalFixities :: IO TmpPostgres.DB -> TestTree
359testLogicalFixities = databasePropertyTest "Logical operator fixities" \transaction -> do
339testLogicalFixities = databasePropertyTest "Logical operator fixities" \transaction -> do
360 (u, v, w, x) <- forAll $ (,,,) <$> Gen.bool <*> Gen.bool <*> Gen.bool <*> Gen.bool
340 (u, v, w, x) <- forAll $ (,,,) <$> Gen.bool <*> Gen.bool <*> Gen.bool <*> Gen.bool
361
341
362− transaction do
342+ transaction \connection -> do
363− [result] <- lift do
343+ [result] <- liftIO $ Rel8.select connection $ pure $
364− statement () $ Rel8.select do
365− pure $ Rel8.lit u Rel8.||. Rel8.lit v Rel8.&&. Rel8.lit w Rel8.==. Rel8.lit x
344+ Rel8.lit u Rel8.||. Rel8.lit v Rel8.&&. Rel8.lit w Rel8.==. Rel8.lit x
366
345
367 result === (u || v && w == x)
346 result === (u || v && w == x)
368
347
@@ -371,10 +350,9 @@ testNot :: IO TmpPostgres.DB -> TestTree
371testNot = databasePropertyTest "NOT (not_)" \transaction -> do
350testNot = databasePropertyTest "NOT (not_)" \transaction -> do
372 x <- forAll Gen.bool
351 x <- forAll Gen.bool
373
352
374− transaction do
353+ transaction \connection -> do
375− [result] <- lift do
354+ [result] <- liftIO $ Rel8.select connection $ pure $
376− statement () $ Rel8.select do
377− pure $ Rel8.not_ $ Rel8.lit x
355+ Rel8.not_ $ Rel8.lit x
378
356
379 result === not x
357 result === not x
380
358
@@ -383,10 +361,9 @@ testBool :: IO TmpPostgres.DB -> TestTree
383testBool = databasePropertyTest "ifThenElse_" \transaction -> do
361testBool = databasePropertyTest "ifThenElse_" \transaction -> do
384 (x, y, z) <- forAll $ liftA3 (,,) Gen.bool Gen.bool Gen.bool
362 (x, y, z) <- forAll $ liftA3 (,,) Gen.bool Gen.bool Gen.bool
385
363
386− transaction do
364+ transaction \connection -> do
387− [result] <- lift do
365+ [result] <- liftIO $ Rel8.select connection $ pure $
388− statement () $ Rel8.select do
389− pure $ Rel8.bool (Rel8.lit z) (Rel8.lit y) (Rel8.lit x)
366+ Rel8.bool (Rel8.lit z) (Rel8.lit y) (Rel8.lit x)
390
367
391 result === if x then y else z
368 result === if x then y else z
392
369
@@ -398,10 +375,9 @@ testAp = databasePropertyTest "Cartesian product (<*>)" \transaction -> do
398 (Gen.list (Range.linear 1 10) genTestTable)
375 (Gen.list (Range.linear 1 10) genTestTable)
399 (Gen.list (Range.linear 1 10) genTestTable)
376 (Gen.list (Range.linear 1 10) genTestTable)
400
377
401− transaction do
378+ transaction \connection -> do
402− result <- lift do
379+ result <- liftIO $ Rel8.select connection $ do
403− statement () $ Rel8.select do
404− liftA2 (,) (Rel8.values (Rel8.lit <$> rows1)) (Rel8.values (Rel8.lit <$> rows2))
380+ liftA2 (,) (Rel8.values (Rel8.lit <$> rows1)) (Rel8.values (Rel8.lit <$> rows2))
405
381
406 sort result === sort (liftA2 (,) rows1 rows2)
382 sort result === sort (liftA2 (,) rows1 rows2)
407
383
@@ -430,22 +406,21 @@ testDBType getTestDatabase = testGroup "DBType instances"
430 where
406 where
431 dbTypeTest :: (Eq a, Show a, Rel8.DBType a) => TestName -> Gen a -> TestTree
407 dbTypeTest :: (Eq a, Show a, Rel8.DBType a) => TestName -> Gen a -> TestTree
432 dbTypeTest name generator = testGroup name
408 dbTypeTest name generator = testGroup name
433− [ databasePropertyTest name (t generator) getTestDatabase
409+ [ databasePropertyTest name (t (==) generator) getTestDatabase
434− , databasePropertyTest ("Maybe " <> name) (t (Gen.maybe generator)) getTestDatabase
410+ , databasePropertyTest ("Maybe " <> name) (t (==) (Gen.maybe generator)) getTestDatabase
435 ]
411 ]
436
412
437− t :: forall a b. (Eq a, Show a, Rel8.Sql Rel8.DBType a)
413+ t :: forall a b. (Show a, Rel8.Sql Rel8.DBType a)
414+ => (a -> a -> Bool)
438− => Gen a
415+ -> Gen a
439− -> (TestT Transaction () -> PropertyT IO b)
416+ -> ((Connection -> TestT IO ()) -> PropertyT IO b)
440 -> PropertyT IO b
417 -> PropertyT IO b
441− t generator transaction = do
418+ t eq generator transaction = do
442 x <- forAll generator
419 x <- forAll generator
443
420
444− transaction do
421+ transaction \connection -> do
445− [res] <- lift do
446− statement () $ Rel8.select do
447− pure (Rel8.litExpr x)
422+ [res] <- liftIO $ Rel8.select connection $ pure (Rel8.litExpr x)
448− diff res (==) x
423+ diff res eq x
449
424
450 genDay :: Gen Day
425 genDay :: Gen Day
451 genDay = do
426 genDay = do
@@ -487,38 +462,39 @@ testDBEq getTestDatabase = testGroup "DBEq instances"
487
462
488 t :: forall a. (Eq a, Show a, Rel8.Sql Rel8.DBEq a)
463 t :: forall a. (Eq a, Show a, Rel8.Sql Rel8.DBEq a)
489 => Gen a
464 => Gen a
490− -> (TestT Transaction () -> PropertyT IO ())
465+ -> ((Connection -> TestT IO ()) -> PropertyT IO ())
491 -> PropertyT IO ()
466 -> PropertyT IO ()
492 t generator transaction = do
467 t generator transaction = do
493 (x, y) <- forAll (liftA2 (,) generator generator)
468 (x, y) <- forAll (liftA2 (,) generator generator)
494
469
495− transaction do
470+ transaction \connection -> do
496− [res] <- lift do
497− statement () $ Rel8.select do
498− pure $ Rel8.litExpr x Rel8.==. Rel8.litExpr y
471+ [res] <- liftIO $ Rel8.select connection $ pure $ Rel8.litExpr x Rel8.==. Rel8.litExpr y
499 res === (x == y)
472 res === (x == y)
500
473
474+ cover 1 "Equal" $ x == y
475+ cover 1 "Not Equal" $ x /= y
476+
501
477
502testTableEquality :: IO TmpPostgres.DB -> TestTree
478testTableEquality :: IO TmpPostgres.DB -> TestTree
503testTableEquality = databasePropertyTest "TestTable equality" \transaction -> do
479testTableEquality = databasePropertyTest "TestTable equality" \transaction -> do
504 (x, y) <- forAll $ liftA2 (,) genTestTable genTestTable
480 (x, y) <- forAll $ liftA2 (,) genTestTable genTestTable
505
481
506− transaction do
482+ transaction \connection -> do
507− [eq] <- lift do
483+ [eq] <- liftIO $ Rel8.select connection do
508− statement () $ Rel8.select do
509− pure $ Rel8.lit x Rel8.==: Rel8.lit y
484+ pure $ Rel8.lit x Rel8.==: Rel8.lit y
510
485
511 eq === (x == y)
486 eq === (x == y)
512
487
488+ cover 1 "Equal" $ x == y
489+ cover 1 "Not Equal" $ x /= y
490+
513
491
514testFromString :: IO TmpPostgres.DB -> TestTree
492testFromString :: IO TmpPostgres.DB -> TestTree
515testFromString = databasePropertyTest "FromString" \transaction -> do
493testFromString = databasePropertyTest "FromString" \transaction -> do
516 str <- forAll $ Gen.list (Range.linear 0 10) Gen.unicode
494 str <- forAll $ Gen.list (Range.linear 0 10) Gen.unicode
517
495
518− transaction do
496+ transaction \connection -> do
519− [result] <- lift do
520− statement () $ Rel8.select do
521− pure $ fromString str
497+ [result] <- liftIO $ Rel8.select connection $ pure $ fromString str
522 result === pack str
498 result === pack str
523
499
524
500
@@ -526,11 +502,10 @@ testCatMaybeTable :: IO TmpPostgres.DB -> TestTree
526testCatMaybeTable = databasePropertyTest "catMaybeTable" \transaction -> do
502testCatMaybeTable = databasePropertyTest "catMaybeTable" \transaction -> do
527 rows <- forAll $ Gen.list (Range.linear 0 10) genTestTable
503 rows <- forAll $ Gen.list (Range.linear 0 10) genTestTable
528
504
529− transaction do
505+ transaction \connection -> do
530− selected <- lift do
531− statement () $ Rel8.select do
506+ selected <- liftIO $ Rel8.select connection do
532− testTable <- Rel8.values $ Rel8.lit <$> rows
507+ testTable <- Rel8.values $ Rel8.lit <$> rows
533− Rel8.catMaybeTable $ Rel8.bool Rel8.nothingTable (pure testTable) (testTableColumn2 testTable)
508+ Rel8.catMaybeTable $ Rel8.bool Rel8.nothingTable (pure testTable) (testTableColumn2 testTable)
534
509
535 sort selected === sort (filter testTableColumn2 rows)
510 sort selected === sort (filter testTableColumn2 rows)
536
511
@@ -539,10 +514,9 @@ testCatMaybe :: IO TmpPostgres.DB -> TestTree
539testCatMaybe = databasePropertyTest "catMaybe" \transaction -> evalM do
514testCatMaybe = databasePropertyTest "catMaybe" \transaction -> evalM do
540 rows <- forAll $ Gen.list (Range.linear 0 10) $ Gen.maybe Gen.bool
515 rows <- forAll $ Gen.list (Range.linear 0 10) $ Gen.maybe Gen.bool
541
516
542− transaction do
517+ transaction \connection -> do
543− selected <- lift do
518+ selected <- evalM $ liftIO $ Rel8.select connection do
544− statement () $ Rel8.select do
545− Rel8.catNull =<< Rel8.values (map Rel8.lit rows)
519+ Rel8.catNull =<< Rel8.values (map Rel8.lit rows)
546
520
547 sort selected === sort (catMaybes rows)
521 sort selected === sort (catMaybes rows)
548
522
@@ -551,38 +525,15 @@ testMaybeTable :: IO TmpPostgres.DB -> TestTree
551testMaybeTable = databasePropertyTest "maybeTable" \transaction -> evalM do
525testMaybeTable = databasePropertyTest "maybeTable" \transaction -> evalM do
552 (rows, def) <- forAll $ liftA2 (,) (Gen.list (Range.linear 0 10) genTestTable) genTestTable
526 (rows, def) <- forAll $ liftA2 (,) (Gen.list (Range.linear 0 10) genTestTable) genTestTable
553
527
554− transaction do
528+ transaction \connection -> do
555− selected <- lift do
529+ selected <- liftIO $ Rel8.select connection $
556− statement () $ Rel8.select do
557− Rel8.maybeTable (Rel8.lit def) id <$> Rel8.optional (Rel8.values (Rel8.lit <$> rows))
530+ Rel8.maybeTable (Rel8.lit def) id <$> Rel8.optional (Rel8.values (Rel8.lit <$> rows))
558
531
559 case rows of
532 case rows of
560 [] -> selected === [def]
533 [] -> selected === [def]
561 _ -> sort selected === sort rows
534 _ -> sort selected === sort rows
562
535
563
536
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−
586data TwoTestTables f =
537data TwoTestTables f =
587 TwoTestTables
538 TwoTestTables
588 { testTable1 :: TestTable f
539 { testTable1 :: TestTable f
@@ -603,10 +554,9 @@ testNestedTables = databasePropertyTest "Nested TestTables" \transaction -> eval
603 Gen.list (Range.linear 0 10) $
554 Gen.list (Range.linear 0 10) $
604 liftA2 TwoTestTables genTestTable genTestTable
555 liftA2 TwoTestTables genTestTable genTestTable
605
556
606− transaction do
557+ transaction \connection -> do
607− selected <- lift do
558+ selected <- liftIO $ Rel8.select connection do
608− statement () $ Rel8.select do
609− Rel8.values (Rel8.lit <$> rows)
559+ Rel8.values (Rel8.lit <$> rows)
610
560
611 sort selected === sort rows
561 sort selected === sort rows
612
562
@@ -616,12 +566,11 @@ testMaybeTableApplicative = databasePropertyTest "MaybeTable (<*>)" \transaction
616 rows1 <- genRows
566 rows1 <- genRows
617 rows2 <- genRows
567 rows2 <- genRows
618
568
619− transaction do
569+ transaction \connection -> do
620− selected <- lift do
621− statement () $ Rel8.select do
570+ selected <- liftIO $ Rel8.select connection do
622− as <- Rel8.optional (Rel8.values (Rel8.lit <$> rows1))
571+ as <- Rel8.optional (Rel8.values (Rel8.lit <$> rows1))
623− bs <- Rel8.optional (Rel8.values (Rel8.lit <$> rows2))
572+ bs <- Rel8.optional (Rel8.values (Rel8.lit <$> rows2))
624− pure $ liftA2 (,) as bs
573+ pure $ liftA2 (,) as bs
625
574
626 case (rows1, rows2) of
575 case (rows1, rows2) of
627 ([], []) -> selected === [Nothing]
576 ([], []) -> selected === [Nothing]
@@ -633,6 +582,14 @@ testMaybeTableApplicative = databasePropertyTest "MaybeTable (<*>)" \transaction
633 genRows = forAll do
582 genRows = forAll do
634 Gen.list (Range.linear 0 10) $ liftA2 TestTable (Gen.text (Range.linear 0 10) Gen.unicode) (pure True)
583 Gen.list (Range.linear 0 10) $ liftA2 TestTable (Gen.text (Range.linear 0 10) Gen.unicode) (pure True)
635
584
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+
636
593
637genTestTable :: Gen (TestTable Result)
594genTestTable :: Gen (TestTable Result)
638genTestTable = do
595genTestTable = do
@@ -645,19 +602,19 @@ testUpdate :: IO TmpPostgres.DB -> TestTree
645testUpdate = databasePropertyTest "Can UPDATE TestTable" \transaction -> do
602testUpdate = databasePropertyTest "Can UPDATE TestTable" \transaction -> do
646 rows <- forAll $ Gen.map (Range.linear 0 5) $ liftA2 (,) genTestTable genTestTable
603 rows <- forAll $ Gen.map (Range.linear 0 5) $ liftA2 (,) genTestTable genTestTable
647
604
648− transaction do
605+ transaction \connection -> do
649− selected <- lift do
606+ void $ liftIO $ Rel8.insert connection
650− statement () $ Rel8.insert Rel8.Insert
607+ Rel8.Insert
651 { into = testTableSchema
608 { into = testTableSchema
652− , rows = Rel8.values $ map Rel8.lit $ Map.keys rows
609+ , rows = map Rel8.lit $ Map.keys rows
653 , onConflict = Rel8.DoNothing
610 , onConflict = Rel8.DoNothing
654− , returning = pure ()
611+ , returning = Rel8.NumberOfRowsAffected
655 }
612 }
656
613
614+ void $ liftIO $ Rel8.update connection
657− statement () $ Rel8.update Rel8.Update
615+ Rel8.Update
658 { target = testTableSchema
616 { target = testTableSchema
659− , from = pure ()
660− , set = \_ r ->
617+ , set = \r ->
661 let updates = map (bimap Rel8.lit Rel8.lit) $ Map.toList rows
618 let updates = map (bimap Rel8.lit Rel8.lit) $ Map.toList rows
662 in
619 in
663 foldl
620 foldl
@@ -671,12 +628,12 @@ testUpdate = databasePropertyTest "Can UPDATE TestTable" \transaction -> do
671 )
628 )
672 r
629 r
673 updates
630 updates
674− , updateWhere = \_ _ -> Rel8.lit True
631+ , updateWhere = \_ -> Rel8.lit True
675− , returning = pure ()
632+ , returning = Rel8.NumberOfRowsAffected
676 }
633 }
677
634
678− statement () $ Rel8.select do
635+ selected <- liftIO $ Rel8.select connection do
679− Rel8.each testTableSchema
636+ Rel8.each testTableSchema
680
637
681 sort selected === sort (Map.elems rows)
638 sort selected === sort (Map.elems rows)
682
639
@@ -689,96 +646,29 @@ testDelete :: IO TmpPostgres.DB -> TestTree
689testDelete = databasePropertyTest "Can DELETE TestTable" \transaction -> do
646testDelete = databasePropertyTest "Can DELETE TestTable" \transaction -> do
690 rows <- forAll $ Gen.list (Range.linear 0 5) genTestTable
647 rows <- forAll $ Gen.list (Range.linear 0 5) genTestTable
691
648
692− transaction do
649+ transaction \connection -> do
693− (deleted, selected) <- lift do
650+ void $ liftIO $ Rel8.insert connection
694− statement () $ Rel8.insert Rel8.Insert
651+ Rel8.Insert
695 { into = testTableSchema
652 { into = testTableSchema
696− , rows = Rel8.values $ map Rel8.lit rows
653+ , rows = map Rel8.lit rows
697 , onConflict = Rel8.DoNothing
654 , onConflict = Rel8.DoNothing
698− , returning = pure ()
655+ , returning = Rel8.NumberOfRowsAffected
699 }
656 }
700
657
658+ deleted <-
659+ liftIO $ Rel8.delete connection
701− deleted <- statement () $ Rel8.delete Rel8.Delete
660+ Rel8.Delete
702 { from = testTableSchema
661 { from = testTableSchema
703− , using = pure ()
704− , deleteWhere = const testTableColumn2
662+ , deleteWhere = testTableColumn2
705 , returning = Rel8.Projection id
663 , returning = Rel8.Projection id
706 }
664 }
707
665
708− selected <- statement () $ Rel8.select do
666+ selected <- liftIO $ Rel8.select connection do
709− Rel8.each testTableSchema
667+ Rel8.each testTableSchema
710−
711− pure (deleted, selected)
712
668
713 sort (deleted <> selected) === sort rows
669 sort (deleted <> selected) === sort rows
714
670
715
671
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−
782newtype HKNestedPair f = HKNestedPair { pairOne :: (TestTable f, TestTable f) }
672newtype HKNestedPair f = HKNestedPair { pairOne :: (TestTable f, TestTable f) }
783 deriving stock Generic
673 deriving stock Generic
784 deriving anyclass Rel8.Rel8able
674 deriving anyclass Rel8.Rel8able
@@ -792,10 +682,9 @@ testSelectNestedPairs :: IO TmpPostgres.DB -> TestTree
792testSelectNestedPairs = databasePropertyTest "Can SELECT nested pairs" \transaction -> do
682testSelectNestedPairs = databasePropertyTest "Can SELECT nested pairs" \transaction -> do
793 rows <- forAll $ Gen.list (Range.linear 0 10) $ HKNestedPair <$> liftA2 (,) genTestTable genTestTable
683 rows <- forAll $ Gen.list (Range.linear 0 10) $ HKNestedPair <$> liftA2 (,) genTestTable genTestTable
794
684
795− transaction do
685+ transaction \connection -> do
796− selected <- lift do
686+ selected <- liftIO $ Rel8.select connection do
797− statement () $ Rel8.select do
798− Rel8.values $ map Rel8.lit rows
687+ Rel8.values $ map Rel8.lit rows
799
688
800 sort selected === sort rows
689 sort selected === sort rows
801
690
@@ -804,22 +693,16 @@ testSelectArray :: IO TmpPostgres.DB -> TestTree
804testSelectArray = databasePropertyTest "Can SELECT Arrays (with aggregation)" \transaction -> do
693testSelectArray = databasePropertyTest "Can SELECT Arrays (with aggregation)" \transaction -> do
805 rows <- forAll $ Gen.list (Range.linear 1 10) Gen.bool
694 rows <- forAll $ Gen.list (Range.linear 1 10) Gen.bool
806
695
807− transaction do
696+ transaction \connection -> do
808− selected <- lift do
697+ selected <- liftIO $ Rel8.select connection do
809− statement () $ Rel8.select do
810− Rel8.many $ Rel8.values (map Rel8.lit rows)
698+ Rel8.many $ Rel8.values (map Rel8.lit rows)
811
699
812 selected === [foldMap pure rows]
700 selected === [foldMap pure rows]
813
701
814− selected' <- lift do
702+ selected' <- liftIO $ Rel8.select connection $ Rel8.catListTable =<< do
815− statement () $ Rel8.select do
816− a <- Rel8.catListTable =<< do
817− Rel8.many $ Rel8.values (map Rel8.lit rows)
703+ 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)
821
704
822− selected' === liftA2 (,) rows rows
705+ selected' === rows
823
706
824
707
825data NestedMaybeTable f = NestedMaybeTable
708data NestedMaybeTable f = NestedMaybeTable
@@ -839,51 +722,9 @@ testNestedMaybeTable :: IO TmpPostgres.DB -> TestTree
839testNestedMaybeTable = databasePropertyTest "Can nest MaybeTable within other tables" \transaction -> do
722testNestedMaybeTable = databasePropertyTest "Can nest MaybeTable within other tables" \transaction -> do
840 let example = NestedMaybeTable { nmt1 = True, nmt2 = Just (TestTable "Hi" True) }
723 let example = NestedMaybeTable { nmt1 = True, nmt2 = Just (TestTable "Hi" True) }
841
724
842− transaction do
725+ transaction \connection -> do
843− selected <- lift do
844− statement () $ Rel8.select do
726+ selected <- liftIO $ Rel8.select connection do
845− x <- Rel8.values [Rel8.lit example]
727+ x <- Rel8.values [Rel8.lit example]
846− pure $ Rel8.maybeTable (Rel8.lit False) (\_ -> Rel8.lit True) (nmt2 x)
728+ pure $ Rel8.maybeTable (Rel8.lit False) (\_ -> Rel8.lit True) (nmt2 x)
847
729
848 selected === [True]
730 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