on db148ff help sign in
rel8/docs/concepts/expr.rst
74 lines 3.4 kB history raw
1``Expr`` and ``Sql``
2====================
4``Expr``
5--------
7Now that we've seen how types are bridged between a database and Haskell via
8``DBType``, we can start looking at how expressions are modelled. In Rel8, any
9expression will be a value of the form ``Expr a``, where ``a`` is the type of
10the expression. For example, the SQL expression ``user.id = 42`` is an ``Expr
11Bool``, and the subexpressions ``user.id`` and ``42`` might be ``Expr
12UserId``\s.
14``Expr``\s are usually created by combining other ``Expr``\s together, like
15using numeric operations like `+` and `*`, or by quoting Haskell values into
16queries with ``lit``. Continuing with the example of ``user.id = 42``, we can
17write this in Rel8 as::
19 userId ==. lit (UserId 42)
21Here it's assumed that a ``userId :: Expr UserId`` expression is already in
22scope, and we can compare that against another ``Expr UserId`` using the ``==.``
23operator (which is like Haskell's normal ``==`` operator, but lifted to work on
24``Expr``). The expression ``lit (UserId 42)`` quotes the Haskell term ``UserId
2542`` literally as the SQL expression ``42``. This particular encoding is chosen
26because ``UserId`` is simply a ``newtype`` around ``Int64``.
28``null``
29--------
31So far we've only talked about ``DBType``, which represents database types
32*excluding* ``null``. Rel8 chooses this encoding because ``null`` is rather
33special in SQL, and doesn't really constitute a distinct type. For example,
34there is no SQL notion of "stacking" nulls - which is to say a type like ``Maybe
35(Maybe UserId))`` has no real analogous type in SQL.
37Of course, as ``null`` is pervasive in SQL queries, Rel8 does support ``null`` -
38simply wrap up your type in ``Maybe``. ``Nothing`` will be translated as
39``null``, and ``Just`` is used to represent non-null values.
41Rel8 comes with a set of functions to work with ``null`` values thash should
42feel familiar to Haskell programmers:
44* ``null`` creates null values (you can also use ``lit Nothing``).
45* ``nullify`` turns an ``Expr a`` into a ``Expr (Maybe a)`` (like ``Just``).
46* ``nullable`` allows you to eliminate null values, like the ``maybe`` function.
47* ``isNull`` and ``isNonNull`` work like ``isNothing`` and ``isJust``,
48 respectively.
49* ``mapNullable`` is like ``fmap`` for ``Maybe``, and allows you to map over
50 non-null values.
51* ``liftOpNullable`` is like ``liftA2`` for ``Maybe``, and allows you to combine
52 expressions together, given a way to combine non-null expressions.
54``SQL`` and null-polymorphic expressions
55----------------------------------------
57Through the API reference documentation for Rel8, you might encounter the
58``Sql`` type class. For example, if we look at the type of ``litExpr``, we
59have::
61 litExpr :: Sql DBType a => a -> Expr a
63Here ``Sql DBType a`` means that ``a`` can either be literally a type that has
64an instance of ``DBType`` (like ``UserId`` or ``Bool``), *or* that same type
65wrapped in ``Maybe`` (so ``Maybe UserId`` or ``Maybe Bool``). ``Maybe`` here
66encodes the SQL concept of ``null``.
68Some functions work regardless of whether or not a value is null, and in these
69cases you'll see ``Sql DBType a``. ``Sql`` can be used with any ``DBType``
70subtype. For example, the type of ``div`` is::
72 div :: Sql DBIntegral a => Expr a -> Expr a -> Expr a
74Which means ``div`` works on any ``DBIntegral a``, including ``Maybe a``.