2017-08-27 50 views
0

我想使用僕人,特別是實現一個有識字的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": "[email protected]", "registration_date": "1683-03-01"} 
, {"name": "Albert Einstein", "age": 136, "email": "[email protected]", "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 "[email protected]" (fromGregorian 1683 3 1) 
    , User "Albert Einstein" 136 "[email protected]"   (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 
``` 
+1

您好@Jimbo歡迎計算器的哈斯克爾部分 - 現在你的問題非常廣泛,目前還不清楚。你有什麼嘗試?你面臨什麼(編譯器)錯誤?另外stackoverflow的問題應該是獨立的,所以請包括你的腳本,而不是鏈接到一個文件。 – epsilonhalbe

+0

你想用什麼僕人?編寫一個客戶端/服務器/文檔/ swagger接口說明/ ...僕人[這裏]有一個很好的論文(https://haskell-servant.github.io/posts/2015-05-25-servant-paper -wgp-2015.html)[文檔](https://haskell-servant.readthedocs.io/en/stable/)也相當廣泛,包括幾個示例以及一些教程。 – epsilonhalbe

+0

我想使用servant來創建API請求並使用reflex-frp構建GUI。我讀了learnyouahaskell.com的13章,但我從未寫過哈斯克爾。我正在嘗試獲得最簡單的僕役教程,以便我可以玩。 – Jimbo

回答

2

首先的 - 如果你沒有寫任何Haskell代碼 - 首先是僕人,是我說相當雄心勃勃 - 因爲它利用一些高層次的概念/幾種語言擴展一樣TypeFamiliesDataKinds提供的機制......

什麼你寫不識字Haskell的文件 - 至少它違反了語法描述here

我建議要麼堅持正常的哈斯克爾文件,要麼讀我先鏈接的文檔。

這裏是你的文件翻譯成有效的文學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": "[email protected]", "registration_date": "1683-03-01"} 
, {"name": "Albert Einstein", "age": 136, "email": "[email protected]", "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 "[email protected]" (fromGregorian 1683 3 1) 
> , User "Albert Einstein" 136 "[email protected]"   (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 
``` 
+0

感謝您的回覆。即使當我在.hs文件中將腳本截斷爲導入時,我也無法導入Servant。 – Jimbo

+0

我沒有我的球 - 沒有具體的錯誤我不能幫你。 – epsilonhalbe

+0

對於任何人閱讀,我解決了我的問題,因爲我不明白如何堆棧工作! https://docs.haskellstack.org/en/stable/README/ – Jimbo