mirror of
https://github.com/circuithub/rel8.git
synced 2024-09-11 16:05:41 +03:00
s/Identity/Result/ in readthedocs
This commit is contained in:
parent
2487397a50
commit
6396ea1ceb
@ -11,18 +11,19 @@ though most of these primitive ``DBType``\s should already be available.
|
||||
Combining ``newtype`` and ``DBType``
|
||||
------------------------------------
|
||||
|
||||
You can define new instances of ``DBType`` by using Haskell "generalized newtype
|
||||
deriving" strategy. This is useful when you have a common database type, but
|
||||
need to interpret this type differently in different contexts. A very common
|
||||
example here is with auto-incrementing ``id`` counters. In PostgreSQL, it's
|
||||
common for a table to have a primary key that uses the ``serial`` type, which
|
||||
means the key is an ``int8`` with a default value for ``INSERT``. In Rel8, we
|
||||
could use ``Int64`` (as ``Int64`` is the ``DBType`` instance for ``int8``), but
|
||||
we can be even clearer if we make a ``newtype`` for each *type* of id.
|
||||
You can define new instances of ``DBType`` by using Haskell "generalized
|
||||
newtype deriving" strategy. This is useful when you have a common database
|
||||
type, but need to interpret this type differently in different contexts. A very
|
||||
common example here is with auto-incrementing ``id`` counters. In PostgreSQL,
|
||||
it's common for a table to have a primary key that uses the ``serial`` type,
|
||||
which means the key is an ``int8`` with a default value for ``INSERT``. In
|
||||
Rel8, we could use ``Int64`` (as ``Int64`` is the ``DBType`` instance for
|
||||
``int8``), but we can be even clearer if we make a ``newtype`` for each *type*
|
||||
of id.
|
||||
|
||||
If our database has users and orders, these tables might both have ids, but they
|
||||
are clearly not meant to be treated as a common type. Instead, we can make these
|
||||
types clearly different by writing::
|
||||
If our database has users and orders, these tables might both have ids, but
|
||||
they are clearly not meant to be treated as a common type. Instead, we can make
|
||||
these types clearly different by writing::
|
||||
|
||||
newtype UserId = UserId { getUserId :: Int64 }
|
||||
deriving newtype DBType
|
||||
@ -57,8 +58,8 @@ In our PostgreSQL we have a few choices, but for now we'll assume that they are
|
||||
stored as ``text`` values.
|
||||
|
||||
In order to use this type in Rel8 queries, we need to write an instance of
|
||||
``DBType`` for ``OrderStatus``. One approach is to use ``parseTypeInformation``,
|
||||
which allows you to refine an existing ``DBType``::
|
||||
``DBType`` for ``OrderStatus``. One approach is to use
|
||||
``parseTypeInformation``, which allows you to refine an existing ``DBType``::
|
||||
|
||||
instance DBType OrderStatus where
|
||||
typeInformation = parseTypeInformation parser printer typeInformation
|
||||
@ -100,9 +101,9 @@ There usage is very similar to ``ReadShow`` - simply derive ``DBType via
|
||||
JSONEncoded``, and Rel8 will use ``ToJSON`` and ``FromJSON`` instances (from
|
||||
``aeson``) to serialize the given type.
|
||||
|
||||
For example, a project might use event sourcing with a table of events. Each row
|
||||
in this table is an event, but this event is stored as a JSON object. We can use
|
||||
this type with Rel8 by writing::
|
||||
For example, a project might use event sourcing with a table of events. Each
|
||||
row in this table is an event, but this event is stored as a JSON object. We
|
||||
can use this type with Rel8 by writing::
|
||||
|
||||
data Event = UserCreated UserCreatedData | OrderCreated OrderCreatedData
|
||||
deriving stock Generic
|
||||
|
@ -58,7 +58,7 @@ Through the API reference documentation for Rel8, you might encounter the
|
||||
``Sql`` type class. For example, if we look at the type of ``litExpr``, we
|
||||
have::
|
||||
|
||||
litExpr :: Sql DBType a => a -> Expr a
|
||||
litExpr :: Sql DBType a => a -> Expr a
|
||||
|
||||
Here ``Sql DBType a`` means that ``a`` can either be literally a type that has
|
||||
an instance of ``DBType`` (like ``UserId`` or ``Bool``), *or* that same type
|
||||
@ -69,6 +69,6 @@ Some functions work regardless of whether or not a value is null, and in these
|
||||
cases you'll see ``Sql DBType a``. ``Sql`` can be used with any ``DBType``
|
||||
subtype. For example, the type of ``div`` is::
|
||||
|
||||
div :: Sql DBIntegral a => Expr a -> Expr a -> Expr a
|
||||
div :: Sql DBIntegral a => Expr a -> Expr a -> Expr a
|
||||
|
||||
Which means ``div`` works on any ``DBIntegral a``, including ``Maybe a``.
|
||||
|
@ -5,7 +5,7 @@ Getting Started
|
||||
|
||||
In this section, we'll take a look at using Rel8 to work with a small database
|
||||
for Haskell packages. We'll take a look at idiomatic usage of Rel8, mapping
|
||||
tables to Haskell, and then look at writing some simple queries.
|
||||
tables to Haskell, and then look at writing some simple queries.
|
||||
|
||||
Before we get started, we'll be using the following language extensions and
|
||||
imports throughout this guide::
|
||||
@ -70,7 +70,7 @@ Following these steps for ``author``, we have::
|
||||
{ authorId :: Column f Int64
|
||||
, name :: Column f Text
|
||||
, url :: Column f (Maybe Text)
|
||||
}
|
||||
}
|
||||
deriving stock (Generic)
|
||||
deriving anyclass (Rel8able)
|
||||
|
||||
@ -81,7 +81,7 @@ in our project, it would be too easy to accidentally mix them up and create
|
||||
nonsensical joins. As Haskell programmers, we often solve this problem by
|
||||
creating ``newtype`` wrappers, and we can also use this technique with Rel8::
|
||||
|
||||
newtype AuthorId = AuthorId { toInt64 :: Int64 }
|
||||
newtype AuthorId = AuthorId { toInt64 :: Int64 }
|
||||
deriving newtype (DBEq, DBType, Eq, Show)
|
||||
|
||||
Now we can write our final schema mapping. First, the ``author`` table::
|
||||
@ -90,7 +90,7 @@ Now we can write our final schema mapping. First, the ``author`` table::
|
||||
{ authorId :: Column f AuthorId
|
||||
, authorName :: Column f Text
|
||||
, authorUrl :: Column f (Maybe Text)
|
||||
}
|
||||
}
|
||||
deriving stock (Generic)
|
||||
deriving anyclass (Rel8able)
|
||||
|
||||
@ -99,7 +99,7 @@ And similarly, the ``project`` table::
|
||||
data Project f = Project
|
||||
{ projectAuthorId :: Column f AuthorId
|
||||
, projectName :: Column f Text
|
||||
}
|
||||
}
|
||||
deriving stock (Generic)
|
||||
deriving anyclass (Rel8able
|
||||
|
||||
@ -107,8 +107,8 @@ To show query results in this documentation, we'll also need ``Show`` instances:
|
||||
Unfortunately these definitions look a bit scary, but they are essentially just
|
||||
``deriving (Show)``::
|
||||
|
||||
deriving stock instance f ~ Identity => Show (Author f)
|
||||
deriving stock instance f ~ Identity => Show (Project f)
|
||||
deriving stock instance f ~ Result => Show (Author f)
|
||||
deriving stock instance f ~ Result => Show (Project f)
|
||||
|
||||
These data types describe the structural mapping of the tables, but we also
|
||||
need to specify a ``TableSchema`` for each table. A ``TableSchema`` contains
|
||||
@ -169,7 +169,7 @@ database heavy applications.
|
||||
|
||||
In Rel8, ``SELECT`` statements are built using the ``Query`` monad. You can
|
||||
think of this monad like the ordinary ``[]`` (List) monad - but this isn't
|
||||
required knowledge.
|
||||
required knowledge.
|
||||
|
||||
To start, we'll look at one of the simplest queries possible - a basic ``SELECT
|
||||
* FROM`` statement. To select all rows from a table, we use ``each``, and
|
||||
@ -193,17 +193,17 @@ expressions of type ``a``\".
|
||||
To execute this ``Query``, we pass it to ``select``::
|
||||
|
||||
>>> :t select c (each projectSchema)
|
||||
select c (each projectSchema) :: MonadIO m => m [Project Identity]
|
||||
select c (each projectSchema) :: MonadIO m => m [Project Result]
|
||||
|
||||
When we ``select`` things containing ``Expr``s, Rel8 builds a new response
|
||||
table with the ``Identity`` interpretation. This means you'll get back plain
|
||||
table with the ``Result`` interpretation. This means you'll get back plain
|
||||
Haskell values. Studying ``projectAuthorId`` again, we have::
|
||||
|
||||
>>> let aProjectIdentity = undefined :: Project Identity
|
||||
>>> :t projectAuthorId aProjectIdentity
|
||||
projectAuthorId aProjectIdentity :: AuthorId
|
||||
>>> let aProjectResult = undefined :: Project Result
|
||||
>>> :t projectAuthorId aProjectResult
|
||||
projectAuthorId aProjectResult :: AuthorId
|
||||
|
||||
Here ``Column Identity AuthorId`` reduces to just ``AuthorId``, with no
|
||||
Here ``Column Result AuthorId`` reduces to just ``AuthorId``, with no
|
||||
wrappping type at all.
|
||||
|
||||
Putting this all together, we can run our first query::
|
||||
@ -343,8 +343,8 @@ focus on the /type/ of that query::
|
||||
where
|
||||
|
||||
>>> :t select c authorsAndProjects
|
||||
select c authorsAndProjects
|
||||
:: MonadIO m => m [(Author Identity, Project Identity)]
|
||||
select c authorsAndProjects
|
||||
:: MonadIO m => m [(Author Result, Project Result)]
|
||||
|
||||
|
||||
Our query gives us a single list of pairs of authors and projects. However,
|
||||
@ -352,7 +352,7 @@ with our domain knowledge of the schema, this isn't a great type - what we'd
|
||||
rather have is a list of pairs of authors and /lists/ of projects. That is,
|
||||
what we'd like is::
|
||||
|
||||
[(Author Identity, [Project Identity])]
|
||||
[(Author Result, [Project Result])]
|
||||
|
||||
This would be a much better type! Rel8 can produce a query with this type by
|
||||
simply wrapping the call to ``projectsForAuthor`` with either ``some`` or
|
||||
|
Loading…
Reference in New Issue
Block a user