Safe Haskell | None |
---|---|
Language | Haskell2010 |
Rel8.Tabulate provides an alternative API (Tabulation
) for writing
queries that complements the main Rel8 API (Query
).
Synopsis
- data Tabulation k a
- fromQuery :: Query (k, a) -> Tabulation k a
- toQuery :: Table Expr k => Tabulation k a -> Query (k, a)
- liftQuery :: Query a -> Tabulation k a
- through :: (a -> Query b) -> Tabulation k a -> Tabulation k b
- lookup :: EqTable k => k -> Tabulation k a -> Query a
- aggregate :: forall k aggregates exprs. (EqTable k, Aggregates aggregates exprs) => Tabulation k aggregates -> Tabulation k exprs
- distinct :: EqTable k => Tabulation k a -> Tabulation k a
- order :: OrdTable k => Order a -> Tabulation k a -> Tabulation k a
- count :: EqTable k => Tabulation k a -> Tabulation k (Expr Int64)
- optional :: Tabulation k a -> Tabulation k (MaybeTable Expr a)
- many :: (EqTable k, Table Expr a) => Tabulation k a -> Tabulation k (ListTable Expr a)
- some :: (EqTable k, Table Expr a) => Tabulation k a -> Tabulation k (NonEmptyTable Expr a)
- exists :: Tabulation k a -> Tabulation k (Expr Bool)
- present :: Tabulation k a -> Tabulation k ()
- absent :: Tabulation k a -> Tabulation k ()
- align :: EqTable k => Tabulation k a -> Tabulation k b -> Tabulation k (TheseTable Expr a b)
- alignWith :: EqTable k => (TheseTable Expr a b -> c) -> Tabulation k a -> Tabulation k b -> Tabulation k c
- leftAlign :: EqTable k => Tabulation k a -> Tabulation k b -> Tabulation k (a, MaybeTable Expr b)
- leftAlignWith :: EqTable k => (a -> MaybeTable Expr b -> c) -> Tabulation k a -> Tabulation k b -> Tabulation k c
- rightAlign :: EqTable k => Tabulation k a -> Tabulation k b -> Tabulation k (MaybeTable Expr a, b)
- rightAlignWith :: EqTable k => (MaybeTable Expr a -> b -> c) -> Tabulation k a -> Tabulation k b -> Tabulation k c
- zip :: EqTable k => Tabulation k a -> Tabulation k b -> Tabulation k (a, b)
- zipWith :: EqTable k => (a -> b -> c) -> Tabulation k a -> Tabulation k b -> Tabulation k c
- similarity :: EqTable k => Tabulation k a -> Tabulation k b -> Tabulation k a
- difference :: EqTable k => Tabulation k a -> Tabulation k b -> Tabulation k a
Documentation
data Tabulation k a Source #
A
is like a Tabulation
k a
, except that each row also
has a key Query
ak
in addition to the value a
. Tabulation
s can be composed
monadically just like Query
s, but the resulting join is more like a
NATURAL JOIN
(based on the common key column(s) k
) than the
CROSS JOIN
given by Query
.
Another way to think of
is as analogous to Tabulation
k aMap k a
in
the same way
is analogous to Query
a[a]
. However, there's nothing
stopping a Tabulation
from containing multiple rows with the same key, so
technically Map k (NonEmpty a)
is more accurate.
Tabulation
s can be created from Query
s with fromQuery
and liftQuery
and converted back to Query
s with lookup
and toQuery
(though note the
caveats that come with the latter).
Instances
Interfacing with Query
s
fromQuery :: Query (k, a) -> Tabulation k a Source #
Any Query
of key-value pairs (k, a)
can be a
.Tabulation
k a
toQuery :: Table Expr k => Tabulation k a -> Query (k, a) Source #
Convert a
back into a Tabulation
k aQuery
of key-value pairs.
Note that the result of a toQuery
is undefined (will always return zero
rows) on Tabulation
s constructed with liftQuery
or pure
. So while
toQuery . fromQuery
is always id
, fromQuery . toQuery
is not.
A safer, more predictable alternative to toQuery
is to use lookup
with
an explicit set of keys:
do k <- keys a <- lookup k tabulation pure (k, a)
Having said that, in practice, most legitimate uses of Tabulation
will
have a well-defined toQuery
. It would be possible in theory to encode
the necessary invariants at the type level using an indexed monad, but we
would lose the ability to use do
-notation, which is the main benefit
of having Tabulation
as a monad in the first place.
In particular,
is well-defined for any toQuery
tTabulation
t
defined as t = fromQuery _
.
is also well-defined for any
toQuery
tTabulation
t
defined as t = t' >>= _
or t = t' *> _
where
is well-defined. There are other valid permutations too.
Generally, anything that uses toQuery
t'fromQuery
at some point, unless wrapped in
a top-level present
or absent
, will have a well-defined toQuery
.
liftQuery :: Query a -> Tabulation k a Source #
A
can be treated as a Query
a
where the given Tabulation
k aa
values exist at every possible key k
.
through :: (a -> Query b) -> Tabulation k a -> Tabulation k b infixr 1 Source #
Run a Kleisli arrow in the the Query
monad "through" a Tabulation
.
Useful for filter
ing a Tabulation
.
filter
((>=. 30) . userAge) `through
` usersById
lookup :: EqTable k => k -> Tabulation k a -> Query a Source #
returns the value(s) at the key lookup
k tk
in the tabulation t
.
Aggregation and Ordering
aggregate :: forall k aggregates exprs. (EqTable k, Aggregates aggregates exprs) => Tabulation k aggregates -> Tabulation k exprs Source #
aggregate
aggregates the values within each key of a
Tabulation
. There is an implicit GROUP BY
on all the key columns.
distinct :: EqTable k => Tabulation k a -> Tabulation k a Source #
distinct
ensures a Tabulation
has at most one value for
each key, i.e., it drops duplicates. In general it keeps only the
"first" value it encounters for each key, but note that "first" is
undefined unless you first call order
.
order :: OrdTable k => Order a -> Tabulation k a -> Tabulation k a Source #
order
orders the values of a Tabulation
within their
respective keys. This specifies a defined order for distinct
.
It also defines the order of the lists produced by many
and
some
.
Magic Tabulation
s
Some of the following combinators produce "magic" Tabulation
s. Let's
use count
as an example to demonstrate this concept. Consider
the following:
count $ fromQuery $ values [ (lita
, lit True) , (lita
, lit False) , (litb
, lit True) ]
You might expect this to be equivalent to the following Tabulation
:
fromQuery $ values [ (lita
, 2) , (litb
, 1) ]
However, it isn't quite. While the resulting Tabulation
does effectively
contain the above entries, it also behaves as though it contained the value
0
at every other possible key.
This means you can do:
do user <- usersById orderCount <- count ordersByUserId
To see how many orders a user has (getting 0
if they have no orders).
count :: EqTable k => Tabulation k a -> Tabulation k (Expr Int64) Source #
count
returns a count of how many entries are in the given
Tabulation
at each key.
The resulting Tabulation
is "magic" in that the value 0
exists at
every possible key that wasn't in the given Tabulation
.
optional :: Tabulation k a -> Tabulation k (MaybeTable Expr a) Source #
optional
produces a "magic" Tabulation
whereby each
entry in the given Tabulation
is wrapped in justTable
, and every
other possible key contains a single nothingTable
.
This is used to implement leftAlignWith
.
many :: (EqTable k, Table Expr a) => Tabulation k a -> Tabulation k (ListTable Expr a) Source #
many
aggregates each entry with a particular key into a
single entry with all of the values contained in a ListTable
.
order
can be used to give this ListTable
a defined order.
The resulting Tabulation
is "magic" in that the value
'Rel8.listTable []'
exists at every possible key that wasn't in the given
Tabulation
.
some :: (EqTable k, Table Expr a) => Tabulation k a -> Tabulation k (NonEmptyTable Expr a) Source #
some
aggregates each entry with a particular key into a
single entry with all of the values contained in a NonEmptyTable
.
order
can be used to give this NonEmptyTable
a defined order.
exists :: Tabulation k a -> Tabulation k (Expr Bool) Source #
exists
produces a "magic" Tabulation
which contains the
value true
at each key in the given Tabulation
, and the value
false
at every other possible key.
present :: Tabulation k a -> Tabulation k () Source #
present
produces a Tabulation
where a single ()
row
exists for every key that was present in the given Tabulation
.
This is used to implement similarity
.
absent :: Tabulation k a -> Tabulation k () Source #
absent
produces a Tabulation
where a single ()
row exists
at every possible key that absent from the given Tabulation
.
This is used to implement difference
.
Natural joins
align :: EqTable k => Tabulation k a -> Tabulation k b -> Tabulation k (TheseTable Expr a b) Source #
Performs a NATURAL FULL OUTER JOIN
based on the common key columns.
Analogous to align
.
alignWith :: EqTable k => (TheseTable Expr a b -> c) -> Tabulation k a -> Tabulation k b -> Tabulation k c Source #
Performs a NATURAL FULL OUTER JOIN
based on the common key columns.
Analogous to alignWith
.
leftAlign :: EqTable k => Tabulation k a -> Tabulation k b -> Tabulation k (a, MaybeTable Expr b) Source #
Performs a NATURAL LEFT OUTER JOIN
based on the common key columns.
Analogous to rpadZip
.
Note that you can achieve the same effect with optional
and the
Applicative
instance for Tabulation
, i.e., this is just
left right -> liftA2 (,) left (optional right). You can also
use
do@-notation.
leftAlignWith :: EqTable k => (a -> MaybeTable Expr b -> c) -> Tabulation k a -> Tabulation k b -> Tabulation k c Source #
Performs a NATURAL LEFT OUTER JOIN
based on the common key columns.
Analogous to rpadZipWith
.
Note that you can achieve the same effect with optional
and the
Applicative
instance for Tabulation
, i.e., this is just
f left right -> liftA2 f left (optional right). You can also
use
do@-notation.
rightAlign :: EqTable k => Tabulation k a -> Tabulation k b -> Tabulation k (MaybeTable Expr a, b) Source #
Performs a NATURAL RIGHT OUTER JOIN
based on the common key columns.
Analogous to lpadZip
.
Note that you can achieve the same effect with optional
and the
Applicative
instance for Tabulation
, i.e., this is just
left right -> liftA2 (flip (,)) right (optional left). You can
also use
do@-notation.
rightAlignWith :: EqTable k => (MaybeTable Expr a -> b -> c) -> Tabulation k a -> Tabulation k b -> Tabulation k c Source #
Performs a NATURAL RIGHT OUTER JOIN
based on the common key columns.
Analogous to lpadZipWith
.
Note that you can achieve the same effect with optional
and the
Applicative
instance for Tabulation
, i.e., this is just
f left right -> liftA2 (flip f) right (optional left). You can
also use
do@-notation.
zip :: EqTable k => Tabulation k a -> Tabulation k b -> Tabulation k (a, b) Source #
Performs a NATURAL INNER JOIN
based on the common key columns.
Analagous to zip
.
Note that you can achieve the same effect with the Applicative
instance
of Tabulation
, i.e., this is just 'liftA2 (,)'
. You can also use
do
-notation.
zipWith :: EqTable k => (a -> b -> c) -> Tabulation k a -> Tabulation k b -> Tabulation k c Source #
Performs a NATURAL INNER JOIN
based on the common key columns.
Analagous to zipWith
.
Note that you can achieve the same effect with the Applicative
instance
of Tabulation
, i.e., this is just
. You can also use
liftA2
do
-notation.
similarity :: EqTable k => Tabulation k a -> Tabulation k b -> Tabulation k a Source #
Performs a NATURAL SEMI JOIN
based on the common key columns.
The result is a subset of the left tabulation where only entries which have a corresponding entry in the right tabulation are kept.
Note that you can achieve a similar effect with present
and the
Applicative
instance of Tabulation
, i.e., this is just
left right -> left <* present right
. You can also use
do
-notation.
difference :: EqTable k => Tabulation k a -> Tabulation k b -> Tabulation k a Source #
Performs a NATURAL ANTI JOIN
based on the common key columns.
The result is a subset of the left tabulation where only entries which do not have a corresponding entry in the right tabulation are kept.
Note that you can achieve a similar effect with absent
and the
Applicative
instance of Tabulation
, i.e., this is just
left right -> left <* absent right
. You can also use
do
-notation.