【HaskellでWeb開発入門】「ScottyのActionM」って何？どんな型なの？

## この記事の目的
* Haskell の Web フレームワーク Scotty に登場する ActionM の正体を理解する
* ActionM の使い方を、具体例付きで実感する
* Haskell で「Webアプリってどう書くの？」という疑問に答える


## ScottyのActionMって何？
Haskell の Web開発用フレームワーク Scotty。
そこで登場する型（型コンストラクタ）がActionMです。

一言でいうと、
「Webリクエストに対する処理を書くための“箱（モナド）”」
です。

Webアプリでは、
* ユーザーからリクエストが来て
* 処理して
* レスポンスを返す

という流れがあります。

この「一連の流れ」を書くための場所、それが ActionM です。

※モナドについて
ここでは、厳密な話はしません。正確ではないですが、外部に影響を与えないようにするための箱、のようなイメージを持ってみてください。


## どこで出てくる？

よくある例

```haskell
get "/hello" $ do
  json $ object ["message" .= ("Hello!" :: String)]
```

ここで do ブロックの中身はすべて ActionM () 型です。
つまり、
* 「GET /hello」というリクエストに対して
* 「JSONを返す」というアクションを
* ActionM の中で実行している


## ActionM の中で使える主な関数

| 関数       | 用途                 | 例                                 |
| -------- | ------------------ | --------------------------------- |
| `param`  | URLのパラメータ取得        | `param "id"` で `/memos/:id` の値を取得 |
| `body`   | POSTされたリクエストボディを取得 | JSON文字列などを受け取る                    |
| `json`   | JSONを返す            | `json memo`                       |
| `status` | HTTPステータスコードを設定    | `status status404`                |
| `liftIO` | IO処理を使う            | DBアクセスなどで使用                       |




## 具体例つきで学ぶ

### 例1: 単純なレスポンスを返す
```haskell
get "/hello" $ do
  json $ object ["message" .= ("Hello, world!" :: String)]
```
`/hello` にアクセスした人に、JSON形式でメッセージを返すだけの処理です。

### 例2: パラメータ付きのレスポンス
```haskell
get "/hello/:name" $ do
  name <- param "name"
  json $ object ["message" .= ("Hello, " ++ name ++ "!")]
```
`/hello/Alice` にアクセスすると `{"message": "Hello, Alice!"}` が返ります。

### 例3: POSTされたデータを受け取って保存
```haskell
post "/echo" $ do
  b <- body
  text b
```
リクエストボディをそのまま返すだけのエコーサーバー。


## ActionM の裏側　ActionT
Scottyの内部では、ActionM は以下のように定義されています。
```haskell
type ActionM = ActionT IO
```

つまり、ActionTによって定義されています。
ActionTの定義を見てみましょう。

```haskell
-- 簡略版の定義
newtype ActionT m a = ActionT { runActionT :: Request -> m (Response, a) }
```
* `ActionT` は モナド変換子（Monad Transformer） です。
* 型パラメータ `m` には、通常 `IO` などのモナドが入ります。
* `a` は「処理の結果として返す値の型」です。
* `Request -> m (Response, a)` という関数をラップしています。

つまり、「HTTPリクエストを受け取り、レスポンスを生成するような処理（副作用あり）を表現するモナド」を意味しています。

例えば、`ActionT IO String`ならば、`IO` を使って `HTTP` リクエストを処理し、最終的に `String` を返す `ActionT` モナド、という意味です。

ActionMの定義に戻りましょう。
```haskell
type ActionM = ActionT IO
```
* ActionM は ActionT に IO を固定した型のエイリアス（別名） です。
* つまり、「IOモナドで動く ActionT」ということです。

### IO モナドについて　補足

Haskellは純粋関数型言語なので、「副作用（＝外部世界への影響）」は直接書けません。ですがWebアプリでは、
* ユーザーの入力を受け取る
* データベースを操作する
* ファイルを読み書きする

といった副作用は必須です。
そのため、こうした副作用を「安全な箱」で包むのが IO モナドです。


## 実用：メモの作成処理（POST）

```haskell
post "/memos" $ do
  b <- body
  case eitherDecode b :: Either String NewMemo of
    Left err -> do
      status status400
      json $ object ["error" .= ("Invalid JSON: " ++ err)]
    Right newMemo -> do
      liftIO $ saveToDB newMemo
      status status201
      json newMemo

```
* body でリクエストボディを取り出し
* eitherDecode で JSON を解析
* エラーなら 400 を返し
* 成功したら DB に保存し、201 を返す

という処理を ActionM の中で書いています。

※例として、メモは`NewMemo`型で管理しています。

## 補足　getの型は？

```haskell
get "/hello" $ do
  json $ object ["msg" .= ("Hello!" :: String)]
```
このgetの型も確認しておきます。

getの型
```haskell
get :: RoutePattern -> ActionM () -> ScottyM ()
```
「指定したパスに対して、HTTP GET リクエストが来たときの処理（`ActionM`）を登録する処理（`ScottyM`）」です。

### RoutePattern型とは？
リクエストの パスにマッチするための「パターン」の型


### ActionM ()の()とは？

`()`は、「何も返さない」ことを表す特別な型です。
Webリクエストに対して何らかの処理を行うけど、処理の結果（値）は外に返さない。副作用だけを行う。


### ScottyM ()
これはルーティングを定義する Scottyアプリ全体の設定を構築するためのモナド。
`get` は「このルーティングを1つ追加する」という副作用的な処理なので、結果は `ScottyM ()`型になります。




## 関連記事
https://qiita.com/meta77/items/3e62724bb4a4f4ac9541
https://qiita.com/meta77/items/91e09025338626fa220b
https://qiita.com/meta77/items/6d6bbbc3abc4b7bd8338