【Servant】(2) Servantチュートリアル

[【Servant】(1) Wai - Qiita](https://qiita.com/sand/items/16a6ef431a8376e7cbaa)
[【Servant】(2) Servantチュートリアル - Qiita](https://qiita.com/sand/items/fea7c294dd7835692ff7)
[【Servant】(3) エンドポイントを増やす - Qiita](https://qiita.com/sand/items/3af359b1830453d1d0ed)
[【Servant】(4) URLパラメータをハンドラの引数とする - Qiita](https://qiita.com/sand/items/d1c0bfb84487a1dfa9bb)
[【Servant】(5) JSON - Qiita](https://qiita.com/sand/items/fd2ea8acdc56998d5c5e)
[【Servant】(6) HTML - Qiita](https://qiita.com/sand/items/0e9f3c80b5103b966be4)
[【Servant】(7) Post Data - Qiita](https://qiita.com/sand/items/d21aed7c24521e20f1cb)
[【Servant】(8) Another Monad - Qiita](https://qiita.com/sand/items/9c8fa50815b00daead24)
[【Servant】(9) Handlerモナド - Qiita](https://qiita.com/sand/items/ae649f14f1b598d636c3)
[【Servant】(10) SQLite - Qiita](https://qiita.com/sand/items/091b0ea981152ad7d744)
[【Servant】(11) Servant-Client - Qiita](https://qiita.com/sand/items/a0e7758da7274a119849)
[【Servant】(12) Basic 認証 - Qiita](https://qiita.com/sand/items/dba678cef365fbd23ae3)





今回はServantチュートリアルとして、公式サイトにある簡単なexampleを動かしてみたいと思います。

# 1. 少々、前準備

## 1-1. Servantの特徴

[Introduction - 公式サイト](https://haskell-servant.readthedocs.io/en/v0.8/introduction.html)

ServantはType（型）レベルでWev APIを記述する**DSL**です。次のような特徴を持っています。

###(1) concision(簡潔)
同じことを繰り返す必要がない。例えばリソースのserialize や deserializeをその都度マニュアルで行う必要はない。type単位で宣言すればよいだけ。また同じquery parametersを扱うハンドラが複数あったとして、それぞれのハンドラがそのロジックを持つ必要はない。一か所に書けばよい。

###(2) flexibility（フレキシビリティ）
それぞれの要望において、必要なものを必要な時に追加できる。

###(3) separation of concerns（関心の分離）
リクエスト・ハンドラとHTTP ロジックが分離されている。


###(4) type safety
APIの仕様を満たしていることをコンパイラが保証してくれる。



## 1-2. GHCの型拡張に親しむ

Servantの一番の特徴は、APIをtypeとして定義することです。逆にこの辺が、私のような初心者には違和感を覚えるところです。この違和感を拭うためには、少しGHCの型拡張に慣れる必要があります。後の説明でも簡単に触れますが、以下のようなサイトを、できる範囲で参照しておくことが望まれます。

[About kind system of Haskell (Part 1)](https://haskell.jp/blog/posts/2017/10-about-kind-system-part1.html)
[Haskellの種(kind)について (Part 2)](https://haskell.jp/blog/posts/2017/13-about-kind-system-part2.html)
[Haskellにおける型レベルプログラミングの基本(翻訳)](https://qiita.com/HirotoShioi/items/39fc492401e4dcbc8cba)
[Part I: Dependent Types in Haskell](https://www.schoolofhaskell.com/user/konn/prove-your-haskell-for-great-safety/dependent-types-in-haskell)



## 1-3. Servantのドキュメントを読む

Servant自身のドキュメントは以下にあります。

[servant – A Type-Level Web DSL - 公式サイト](https://www.servant.dev/)

[servant-server: A family of combinators for defining webservices APIs and serving them - hackage](https://hackage.haskell.org/package/servant-server)





# 2.最初のプロジェクト

今回は、公式サイトの最も簡単なexampleを動作させることを目指します。
[Docs » Tutorial » Serving an API - A first example](https://docs.servant.dev/en/stable/tutorial/Server.html)



## 2-1. プロジェクトを作る

まずはstackプロジェクトを作成します。

```
stack new first-project
cd first-project
```

動作を確認します。

```
stack build && stack exec first-project-exe
```

以下のような出力が得られます。

```
someFunc
```


stackプロジェクトの、最初のソースを確認しておきます。


メインは以下の通りです。

```haskell:app/Main.hs
module Main where

import Lib

main :: IO ()
main = someFunc
```

メインから以下の関数が呼ばれます。

```haskell:Lib.hs
module Lib
    ( someFunc
    ) where

someFunc :: IO ()
someFunc = putStrLn "someFunc"
```


## 2-2. はじめてのServant 

それではソースを変更して、Servantアプリに変更します。

メインでは呼び出す関数名をrunServantに変更します。

```haskell:app/Main.hs
module Main where

import Lib

main :: IO ()
main = runServant
```


Libs.hsを以下のように変更します。オリジナルのソースでは言語拡張宣言やimport文が鬼のようについていますが、必要最小限のものに削っているのでスッキリしています。


```haskell:Lib.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DeriveGeneric #-}

module Lib
    ( runServant
    ) where

import Servant(serve, Proxy(..), Server, JSON, Get, (:>))
import Data.Aeson(ToJSON)
import Data.Time.Calendar
import GHC.Generics(Generic)
import Network.Wai(Application)
import Network.Wai.Handler.Warp(run)

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

instance ToJSON User


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)
  ]

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

server1 :: Server UserAPI1
server1 = return users1

userAPI :: Proxy UserAPI1
userAPI = Proxy

-- serve allows you to implement an API and produce a wai Application.
-- serve :: HasServer api '[] => Proxy api -> Server api -> Application
app1 :: Application
app1 = serve userAPI server1



runServant :: IO ()
runServant = run 8081 app1
```


必要なライブラリを追加します。


```yaml:package.yaml
dependencies:
- base >= 4.7 && < 5
- servant-server
- aeson
- time
- wai
- warp
```

コンパイルして、実行してみます。

```
stack build && stack exec first-project-exe
```



curlコマンドでアクセスしてみます。


```
$ curl http://localhost:8081/users
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   188    0   188    0     0    858      0 --:--:-- --:--:-- --:--:--   870
[{"email":"isaac@newton.co.uk","registration_date":"1683-03-01","age":372,"name":"Isaac Newton"},
{"email":"ae@mc2.org","registration_date":"1905-12-01","age":136,"name":"Albert Einstein"}]
```

望み通りの結果が得られました。満足です。


## 2-3. 説明 

### 2-3-1. WAI & Warp

前回のWarpのrunコマンドの復習です。

```haskell:
type Port = Int
run :: Port -> Application -> IO ()
```

### 2-3-2. Application

runコマンドで走らせるApplicationは以下のように定義されます。

```
-- serve allows you to implement an API and produce a wai Application.
-- serve :: HasServer api '[] => Proxy api -> Server api -> Application
app1 :: Application
app1 = serve userAPI server1
```


serveでApplicationを生成します。serveの型は以下の通りです。

[Servant.Server - hackage](https://hackage.haskell.org/package/servant-server-0.17/docs/Servant-Server.html)

```haskell:
serve :: HasServer api '[] => Proxy api -> Server api -> Application
```

つまりserveは API（userAPI）とハンドラ(server1)を結び付けてApplicationを返しています。API(userAPI）とハンドラ(server1)は以下のように別々に定義されています。


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

userAPI :: Proxy UserAPI1
userAPI = Proxy
```

ProxyはUserAPIという型を保持しています。




### 2-3-3. 型とハンドラ

Servantの骨子は、型として記述されたAPIと、ハンドラです。そして重要なのはそのAPIとハンドラが分離されていることです。

APIの定義は以下のように型レベルで行われます。型としてHTTP のGETメソッドが定義されています。エンドポイント "users"へのHTTP GETリクエストがあった場合、User型のリストをJSONで返すことを定義しています。

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

リストの前についたアポストロフィ　**'**[JSON]　が違和感がありますが、これは **DataKinds** によって導入されたものです。通常のカギカッコ[...]は、リストを表現するための値構成子ですが、**DataKinds** によって**'**[....]は型構成子に昇格させられています。Heterogenous Listsのようなものです。

また**:>**もGHCの型拡張の**TypeOperators**で、型構成子として使われています。

UserAPI1に対応するハンドラは以下のように定義されています。皆さん大好きなモナドですね。

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

### 2-3-4. JSON シリアライズ

Webサーバ(Servant)とクライアント間では、User型のデータを、JSON形式で、やりとりすることになりますが、もちろんAesonを利用します。GHCの言語拡張DeriveGenericを用いて以下のように宣言するだけです。

```haskell:
---
{-# LANGUAGE DeriveGeneric #-}
---
import GHC.Generics(Generic)
---

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

instance ToJSON User
```


今回は以上です。