Tidy up tutorial

This commit is contained in:
David Turner 2017-05-17 05:50:38 +00:00
parent a3c5f17749
commit aa3716b6aa
5 changed files with 35 additions and 15 deletions

View file

@ -328,14 +328,25 @@ type ProtectedAPI11
### Empty APIs
TODO motivation...
Sometimes it is useful to be able to generalise an API over the type of some
part of it:
``` haskell ignore
type UserAPI12
= UserAPI
:<|> EmptyAPI -- this adds nothing to the API
``` haskell
type UserAPI12 innerAPI
= UserAPI -- this is the fixed bit of the API
:<|> "inner" :> innerAPI -- this lets us put various other APIs under /inner
```
If there is a case where you do not have anything extra to serve, you can use
the `EmptyAPI` combinator to indicate this:
``` haskell
type UserAPI12Alone = UserAPI12 EmptyAPI
```
This also works well as a placeholder for unfinished parts of an API while it
is under development.
### Interoperability with `wai`: `Raw`
Finally, we also include a combinator named `Raw` that provides an escape hatch

View file

@ -62,7 +62,6 @@ Enough chitchat, let's see an example. Consider the following API type from the
type API = "position" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] Position
:<|> "hello" :> QueryParam "name" String :> Get '[JSON] HelloMessage
:<|> "marketing" :> ReqBody '[JSON] ClientInfo :> Post '[JSON] Email
:<|> EmptyAPI
```
What we are going to get with **servant-client** here is three functions, one to query each endpoint:
@ -89,11 +88,23 @@ the function `client`. It takes one argument:
api :: Proxy API
api = Proxy
position :<|> hello :<|> marketing :<|> EmptyClient = client api
position :<|> hello :<|> marketing = client api
```
`client api` returns client functions for our _entire_ API, combined with `:<|>`, which we can pattern match on as above. You could say `client` "calculates" the correct type and number of client functions for the API type it is given (via a `Proxy`), as well as their implementations.
If there is an `EmptyAPI` within your API, this matches the `EmptyClient`
constructor:
``` haskell ignore
type API' = API :<|> EmptyAPI
api' :: Proxy API'
api' = Proxy
(position' :<|> hello' :<|> marketing') :<|> EmptyClient = client api'
```
``` haskell ignore
-- | URI scheme to use
data Scheme =

View file

@ -38,10 +38,9 @@ Like client function generation, documentation generation amounts to inspecting
This time however, we have to assist **servant**. While it is able to deduce a lot of things about our API, it can't magically come up with descriptions of the various pieces of our APIs that are human-friendly and explain what's going on "at the business-logic level". A good example to study for documentation generation is our webservice with the `/position`, `/hello` and `/marketing` endpoints from earlier:
``` haskell
type ExampleAPI = ("position" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] Position
type ExampleAPI = "position" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] Position
:<|> "hello" :> QueryParam "name" String :> Get '[JSON] HelloMessage
:<|> "marketing" :> ReqBody '[JSON] ClientInfo :> Post '[JSON] Email)
:<|> EmptyAPI
:<|> "marketing" :> ReqBody '[JSON] ClientInfo :> Post '[JSON] Email
exampleAPI :: Proxy ExampleAPI
exampleAPI = Proxy
@ -221,7 +220,7 @@ api :: Proxy DocsAPI
api = Proxy
server :: Server DocsAPI
server = (Server.server3 :<|> emptyServer) :<|> Tagged serveDocs where
server = Server.server3 :<|> Tagged serveDocs where
serveDocs _ respond =
respond $ responseLBS ok200 [plain] docsBS
plain = ("Content-Type", "text/plain")

View file

@ -45,7 +45,6 @@ Now let's have the API type(s) and the accompanying datatypes.
``` haskell
type API = "point" :> Get '[JSON] Point
:<|> "books" :> QueryParam "q" Text :> Get '[JSON] (Search Book)
:<|> EmptyAPI
type API' = API :<|> Raw
@ -134,7 +133,6 @@ api' = Proxy
server :: Server API
server = randomPoint
:<|> searchBook
:<|> emptyServer
server' :: Server API'
server' = server

View file

@ -1020,10 +1020,11 @@ serverFor = error "..."
-- or the mailing list if you get stuck!
```
TODO prose
If the API contains the `EmptyAPI` combinator, the corresponding server is
called `emptyServer`:
``` haskell
type CombinedAPI2 = API :<|> EmptyAPI
type CombinedAPI2 = API :<|> "empty" :> EmptyAPI
server11 :: Server CombinedAPI2
server11 = server3 :<|> emptyServer