【问题标题】:Servant Implementation仆人式执行
【发布时间】:2017-08-27 18:28:05
【问题描述】:

我想使用servant,特别是实现一个literate haskell 文件。我不知道如何使用 literate haskell 文件。我一直在搜索文档,但没有找到任何有用的信息。

到目前为止,我已经使用扩展名 .lhs 正确命名了文件,并且我已经执行了 runhaskell filename.lhs。我收到以下错误:

servantfinaltest.lhs line 150: unlit: No definitions in file (perhaps you forgot the '>'s?)
`unlit' failed in phase `Literate pre-processor'. (Exit code: 1)

这是我下面的 .lhs 文件:

# Serving an API

Enough chit-chat about type-level combinators and representing an API as a
type. Can we have a webservice already?

## A first example

Equipped with some basic knowledge about the way we represent APIs, let's now
write our first webservice.

The source for this tutorial section is a literate haskell file, so first we
need to have some language extensions and imports:

``` haskell
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}

module Server where

import Prelude ()
import Prelude.Compat

import Control.Monad.Except
import Control.Monad.Reader
import Data.Aeson.Compat
import Data.Aeson.Types
import Data.Attoparsec.ByteString
import Data.ByteString (ByteString)
import Data.List
import Data.Maybe
import Data.String.Conversions
import Data.Time.Calendar
import GHC.Generics
import Lucid
import Network.HTTP.Media ((//), (/:))
import Network.Wai
import Network.Wai.Handler.Warp
import Servant
import System.Directory
import Text.Blaze
import Text.Blaze.Html.Renderer.Utf8
import qualified Data.Aeson.Parser
import qualified Text.Blaze.Html
```

**Important**: the `Servant` module comes from the **servant-server** package,
the one that lets us run webservers that implement a particular API type.  It
reexports all the types from the **servant** package that let you declare API
types as well as everything you need to turn your request handlers into a
fully-fledged webserver. This means that in your applications, you can just add
**servant-server** as a dependency, import `Servant` and not worry about anything
else.

We will write a server that will serve the following API.

``` haskell
type UserAPI1 = "users" :> Get '[JSON] [User]
```

Here's what we would like to see when making a GET request to `/users`.

``` javascript
[ {"name": "Isaac Newton", "age": 372, "email": "isaac@newton.co.uk", "registration_date": "1683-03-01"}
, {"name": "Albert Einstein", "age": 136, "email": "ae@mc2.org", "registration_date": "1905-12-01"}
]
```

Now let's define our `User` data type and write some instances for it.

``` haskell
data User = User
  { name :: String
  , age :: Int
  , email :: String
  , registration_date :: Day
  } deriving (Eq, Show, Generic)

instance ToJSON User
```

Nothing funny going on here. But we now can define our list of two users.

``` haskell
users1 :: [User]
users1 =
  [ User "Isaac Newton"    372 "isaac@newton.co.uk" (fromGregorian 1683  3 1)
  , User "Albert Einstein" 136 "ae@mc2.org"         (fromGregorian 1905 12 1)
  ]
```

Let's also write our API type.

``` haskell ignore
type UserAPI1 = "users" :> Get '[JSON] [User]
```

We can now take care of writing the actual webservice that will handle requests
to such an API. This one will be very simple, being reduced to just a single
endpoint. The type of the web application is determined by the API type,
through a *type family* named `Server`. (Type families are just functions that
take types as input and return types.)  The `Server` type family will compute
the right type that a bunch of request handlers should have just from the
corresponding API type.

The first thing to know about the `Server` type family is that behind the
scenes it will drive the routing, letting you focus only on the business
logic. The second thing to know is that for each endpoint, your handlers will
by default run in the `Handler` monad. This is overridable very
easily, as explained near the end of this guide. Third thing, the type of the
value returned in that monad must be the same as the second argument of the
HTTP method combinator used for the corresponding endpoint. In our case, it
means we must provide a handler of type `Handler [User]`. Well,
we have a monad, let's just `return` our list:

``` haskell
server1 :: Server UserAPI1
server1 = return users1
```

That's it. Now we can turn `server` into an actual webserver using
[wai](http://hackage.haskell.org/package/wai) and
[warp](http://hackage.haskell.org/package/warp):

``` haskell
userAPI :: Proxy UserAPI1
userAPI = Proxy

-- 'serve' comes from servant and hands you a WAI Application,
-- which you can think of as an "abstract" web application,
-- not yet a webserver.
app1 :: Application
app1 = serve userAPI server1
```

The `userAPI` bit is, alas, boilerplate (we need it to guide type inference).
But that's about as much boilerplate as you get.

And we're done! Let's run our webservice on the port 8081.

``` haskell
main :: IO ()
main = run 8081 app1
```

【问题讨论】:

  • 您好@Jimbo 欢迎来到stackoverflow 的haskell 部分——现在您的问题非常广泛且不清楚。你试过什么?您面临哪些(编译器)错误?此外,stackoverflow 问题应该是独立的,因此请包含您的脚本而不是链接到文件。
  • 你想用servant做什么?编写客户端/服务器/文档/swagger-interface 描述/...关于servant here 有一篇很好的论文,documentation 也相当广泛,包括几个示例和一些教程。
  • 我想使用servant 发出API 请求并用reflex-frp 构建一个GUI。我阅读了 learnyouahaskell.com 的 13 章,但我从未写过 haskell。我正在尝试获得最简单的仆人工作教程,以便我可以使用它。

标签: haskell servant


【解决方案1】:

首先——如果你还没有写过任何haskell代码——从servant开始,我会说它很有野心——因为它利用了几个语言扩展提供的几个高级概念/机制,比如TypeFamiliesDataKinds ...

您正在编写的不是识字的 haskell 文件 - 至少它违反了 here 中描述的语法

我建议要么坚持使用普通的 haskell 文件,要么先阅读我链接到的文档。

这是您的文件翻译成有效的读写haskell:

# Serving an API

Enough chit-chat about type-level combinators and representing an API as a
type. Can we have a webservice already?

## A first example

Equipped with some basic knowledge about the way we represent APIs, let's now write our first webservice.

The source for this tutorial section is a literate haskell file, so first we need to have some language extensions and imports:

``` haskell
> {-# LANGUAGE DataKinds #-}
> {-# LANGUAGE DeriveGeneric #-}
> {-# LANGUAGE FlexibleInstances #-}
> {-# LANGUAGE GeneralizedNewtypeDeriving #-}
> {-# LANGUAGE MultiParamTypeClasses #-}
> {-# LANGUAGE OverloadedStrings #-}
> {-# LANGUAGE ScopedTypeVariables #-}
> {-# LANGUAGE TypeOperators #-}

> module Server where

> import Prelude ()
> import Prelude.Compat

> import Control.Monad.Except
> import Control.Monad.Reader
> import Data.Aeson.Compat
> import Data.Aeson.Types
> import Data.Attoparsec.ByteString
> import Data.ByteString (ByteString)
> import Data.List
> import Data.Maybe
> import Data.String.Conversions
> import Data.Time.Calendar
> import GHC.Generics
> import Lucid
> import Network.HTTP.Media ((//), (/:))
> import Network.Wai
> import Network.Wai.Handler.Warp
> import Servant
> import System.Directory
> import Text.Blaze
> import Text.Blaze.Html.Renderer.Utf8
> import qualified Data.Aeson.Parser
> import qualified Text.Blaze.Html
```

**Important**: the `Servant` module comes from the **servant-server** package, the one that lets us run webservers that implement a particular API type. It reexports all the types from the **servant** package that let you declare API types as well as everything you need to turn your request handlers into a fully-fledged webserver. This means that in your applications, you can just add **servant-server** as a dependency, import `Servant` and not worry about anything else.

We will write a server that will serve the following API.

``` haskell
> type UserAPI1 = "users" :> Get '[JSON] [User]
```

Here's what we would like to see when making a GET request to `/users`.

``` javascript
[ {"name": "Isaac Newton", "age": 372, "email": "isaac@newton.co.uk", "registration_date": "1683-03-01"}
, {"name": "Albert Einstein", "age": 136, "email": "ae@mc2.org", "registration_date": "1905-12-01"}
]
```

Now let's define our `User` data type and write some instances for it.

``` haskell
> data User = User
>   { name :: String
>   , age :: Int
>   , email :: String
>   , registration_date :: Day
>   } deriving (Eq, Show, Generic)

> instance ToJSON User
```

Nothing funny going on here. But we now can define our list of two users.

``` haskell
> users1 :: [User]
> users1 =
>   [ User "Isaac Newton"    372 "isaac@newton.co.uk" (fromGregorian 1683  3 1)
>   , User "Albert Einstein" 136 "ae@mc2.org"         (fromGregorian 1905 12 1)
>   ]
```

Let's also write our API type.

``` haskell ignore
type UserAPI1 = "users" :> Get '[JSON] [User]
```

We can now take care of writing the actual webservice that will handle requests to such an API. This one will be very simple, being reduced to just a single endpoint. The type of the web application is determined by the API type, through a *type family* named `Server`. (Type families are just functions that take types as input and return types.)  The `Server` type family will compute the right type that a bunch of request handlers should have just from the
corresponding API type.

The first thing to know about the `Server` type family is that behind the
scenes it will drive the routing, letting you focus only on the business
logic. The second thing to know is that for each endpoint, your handlers will by default run in the `Handler` monad. This is overridable very
easily, as explained near the end of this guide. Third thing, the type of the value returned in that monad must be the same as the second argument of the HTTP method combinator used for the corresponding endpoint. In our case, it means we must provide a handler of type `Handler [User]`. Well,
we have a monad, let's just `return` our list:

``` haskell
> server1 :: Server UserAPI1
> server1 = return users1
```

That's it. Now we can turn `server` into an actual webserver using
[wai](http://hackage.haskell.org/package/wai) and
[warp](http://hackage.haskell.org/package/warp):

``` haskell
> userAPI :: Proxy UserAPI1
> userAPI = Proxy
```

'serve' comes from servant and hands you a WAI Application,
which you can think of as an "abstract" web application,
not yet a webserver.

```haskell
> app1 :: Application
> app1 = serve userAPI server1
```

The `userAPI` bit is, alas, boilerplate (we need it to guide type inference).
But that's about as much boilerplate as you get.

And we're done! Let's run our webservice on the port 8081.

```haskell
> main :: IO ()
> main = run 8081 app1
```

【讨论】:

  • 感谢您的回复。即使我将脚本缩减为 .hs 文件中的导入,我也无法导入 Servant。
  • 我没有带我的占卜球 - 如果没有具体的错误,我无法帮助你。
  • 对于任何阅读本文的人,我解决了我的问题,因为我不了解堆栈的工作原理! docs.haskellstack.org/en/stable/README
猜你喜欢
  • 2015-03-11
  • 2015-11-06
  • 2015-09-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多