PureScriptでHTTPリクエストを叩き結果のJSONをレコードで受け取る

最近PureScriptを使い始めました。練習としてHTTPリクエストを投げて結果をJSONで受け取り、それを任意のレコード（newtypeやdataでもいいけど）で受け取る処理を実装してみました。
なんということはないコードですが、意外に世間にサンプルが少ない気がしますので共有しておきます。

# 主要な処理

この処理は、大雑把に言えば以下の２つの処理に分けられます。
1. HTTPリクエストを投げる
2. ボディのJSON文字列をパースしてレコードを作る

今回は[https://httpbin.org/ip](https://httpbin.org/ip)にリクエストを投げて結果のJSONを ``` { origin :: String } ``` というレコードで受け取る処理を題材にしてこの処理を実装します。


# JSON文字列をパースしてレコードを作る
[PureScriptを使ってJSONの型安全な読み書きを自動化する](https://qiita.com/hiruberuto/items/afae75564ecac9ccde6a)というQiitaの記事で紹介されている[purescript-simple-json](https://github.com/justinwoo/purescript-simple-json)を使ってみたところ、これ以上ないくらいにシンプルで非常に便利でした。

```haskell
import Data.Either (Either (..))
import Foreign     (MultipleErrors)
import Simple.JSON (readJSON)

type IpRes = { origin :: String }

jsonToIpRes :: String -> Either MultipleErrors IpRes
jsonToIpRes = readJSON
```

なお、``` MultipleErrors ```という型は ``` NonEmptyList ForeignError ```のエイリアスです。

ここではリクエスト結果をレコードで受け取っていますが、newtypeやdataで定義した型であってもSimple.JSONは期待通りパースしてくれます。

# HTTPリクエストを投げる

HTTPリクエストを投げるパッケージはいくつかあるっぽいのですが、ここでは[Affjax](https://github.com/slamdata/purescript-affjax)というパッケージを使います。


GETを投げるだけなら非常に簡単で、get関数を使います。get関数の戻り値はAffモナドで包まれます。
JSONのパースには、先ほど作ったrequest関数を使います。

```haskell
import Data.List.NonEmpty    (singleton)
import Effect.Aff            (Aff)
import Affjax                as AX
import Affjax.ResponseFormat as ResponseFormat

request :: Aff (Either MultipleErrors IpRes)
request = do
  res <- AX.get ResponseFormat.string "https://httpbin.org/ip"
  case res.body of
    Left (ResponseFormat.ResponseFormatError err _) -> pure $ Left $ singleton err
    Right json -> pure $ jsonToIpRes json
```

なお ``` ResponseFormatError ```という型はレスポンスが期待の書式でないことを示すエラーであり、エラーとその原因となった値の対を保持します。定義は次の通りです。

```haskell
data ResponseFormatError
  = ResponseFormatError ForeignError Foreign
```

# ソース全体
PureScriptで作ったプログラムのエントリポイントはmain関数であり、型は``` Effect a ```です。Affjaxを使ってHTTPリクエスト処理を実装すると型はAffになるので、Effectモナドの中でAffモナドを使うために``` launchAff_ ```という関数が提供されています。

``` launchAff ```という関数もあるのですが、これはAffモナドを非同期に実行する場合に使うようです、多分。

```haskell
module Main where

import           Prelude

import           Data.Either           (Either (..))
import           Data.List.NonEmpty    (singleton, foldl)
import           Foreign               (MultipleErrors)

import           Simple.JSON           (readJSON)

import           Effect                (Effect)
import           Effect.Aff            (Aff, launchAff_)
import           Effect.Class.Console  (log)

import           Affjax                as AX
import           Affjax.ResponseFormat as ResponseFormat

type IpRes = { origin :: String }

jsonToIpRes :: String -> Either MultipleErrors IpRes
jsonToIpRes = readJSON

request :: Aff (Either MultipleErrors IpRes)
request = do
  res <- AX.get ResponseFormat.string "https://httpbin.org/ip"
  case res.body of
    Left (ResponseFormat.ResponseFormatError err _) -> pure $ Left $ singleton err
    Right json -> pure $ jsonToIpRes json

main :: Effect Unit
main = launchAff_ $ do
  res <- request
  case res of
    Left errors -> log $ foldl (\s err -> if (s == "")  then show err else s <> "\n" <> show err) "" errors
    Right  gr   -> log gr.origin
```

# 今回の処理を実装した感想
たったこれだけの処理の実装にまあ時間のかかること。PureScriptを使い始めということに加えサンプルが少なく原典（＝パッケージのリポジトリやソースコード）を参照することが多くなり、時間を多く取られました。

またAffやFiberなど、知識外の型の意味を調べるのにもまた時間を取られます。

しかし、**ビルドに通ってしまえばほぼ意図通りに動作**します。
これはまさにHaskellと同じ**ツンデレ言語**ではないですか。

素晴らしい、PureScript。