[Elm] Decoder a からいろいろ理解ってしまおう

[前提]

- Elm 0.19.1 (元は0.18時代に書かれましたが、更新済み)
- elm/json 1.1.3

# 1. ElmにおけるJSONのdecode

Elmでは、公式organizationがアプリケーション開発において必須となるであろう標準的な機能をパッケージとして提供しています。JSONのdecode/encodeももちろん、[elm/json]の中で[`Json.Decode`][jd]および[`Json.Encode`][je]として提供されています。

[elm/json]: https://package.elm-lang.org/packages/elm/json/1.1.3/
[jd]: https://package.elm-lang.org/packages/elm/json/1.1.3/Json-Decode
[je]: https://package.elm-lang.org/packages/elm/json/1.1.3/Json-Encode

ここで、**[`Json.Decode.Decoder a`][jdd]という型**が登場します。この`Decoder a`を組み合わせることによってElmアプリケーションの中でJSONのdecodeを実現するのですが。。。

[jdd]: https://package.elm-lang.org/packages/elm/json/1.1.3/Json-Decode#Decoder

## 1.1 Decoderの壁

大抵の言語は、初めて学ぶにあたって初期のつまづきポイントがいくつかあるものですが、例に漏れずElmにもあり、以下のようなラインナップになってございます。

1. Haskellライクなシンタックスの読解
    - 本家Haskellと比べてかなり簡素化されているため、だいたいどうにかなる印象
    - シンタックスが近い関数型言語に取り組んだ経験があれば容易
2. The Elm Architectureの理解
    - 純粋関数としてのview、messageとupdateによる状態遷移、`Cmd`や`Task`で抽象化された非同期処理など
    - React + Reduxにすでに触れた経験があると難易度低下
3. **`Decoder a`によるJSONのdecode**

異論は認める。次点はなんだろう、再帰型によるbad-recursionとかかな。。。非同期処理関連ももう少し長く引きずる部分かもしれません。

何にせよ、この`Decoder a`というのが他のフロントエンド開発言語のJSON parserとアプローチが異なるため、難しいように（最初は）思えます。検索してみるとやはり、`Json.Decode`に関しては多くの議論や解説記事があることがわかります。


- [Is a decoder the right level of abstraction for parsing JSON? - Google グループ][abs]
[abs]: https://groups.google.com/forum/#!msg/elm-discuss/XW-SRfbzQ94/aKufhX0LBgAJ
- [JSON decoding in Elm is still difficult – Noah – Medium][dif]
[dif]: https://medium.com/@eeue56/json-decoding-in-elm-is-still-difficult-cad2d1fb39ae)
- [How JSON decoding works in Elm—Part 1 | 8th Light][how1]
[how1]: https://8thlight.com/blog/kofi-gumbs/2017/06/28/elm-json-decoding-types.html
    - [Part 2](https://8thlight.com/blog/kofi-gumbs/2017/07/17/elm-json-decoding-data-structures.html)
    - [Part 3](https://8thlight.com/blog/kofi-gumbs/2017/08/29/elm-json-decoding-custom.html)
- [You could have designed the Json.Decode library!][des]
[des]: https://dev.to/matthewsj/you-could-have-designed-the-jsondecode-library-2d8
- [Decoding large objects and Json.Decode.Extra - I want to understand! : elm][want]
[want]: https://www.reddit.com/r/elm/comments/49fkz5/decoding_large_objects_and_jsondecodeextra_i_want/
- [Easiest json decode? : elm][easy]
[easy]: https://www.reddit.com/r/elm/comments/54fk9k/easiest_json_decode/
- [Why Elm has JSON Decoders - YouTube](https://www.youtube.com/watch?v=nY0Uo3e_GnY) (動画)

しかしながら、この`Decoder a`からなるAPIにはなかなかいいところがあるのです。
0.18時代の[JSON · An Introduction to Elmの末尾][agp]にはこう書いてありました：

[agp]: https://github.com/evancz/guide.elm-lang.org/blob/07fe010b747d90c4b8eeb1af5d12181a42017eae/interop/json.md#a-general-pattern

> JSON decoders are an example of a more general pattern in Elm. You see it whenever you want to wrap up complicated logic into small building blocks that snap together easily.

- 複雑なロジックを、
- 簡単に組み合わせが効くような、
- 小さな部品に還元する。

これを可能にするのが`Decoder a`なのです。

# 2. `Decoder a`でJSONを読む

具体例から当たっていきましょう。

## 2.1. 増田クローン

たとえばElmで、[はてな匿名ダイアリー](https://anond.hatelabo.jp/)（通称増田）のようなwebアプリを作ることを考えてみます。
なぜ増田を例にするかというと、普通のブログの例だと「記事」と「コメント」のような主従関係でデータ構造を考えますが、増田ではある「投稿」が記事であると同時にコメントにもなりうるというフラットな関係性を持っていて面白いためです。

- サーバはJSONを返すREST API
- 登場するデータは、`User`、`Post`
- `User`が`Post`を投稿する

サーバはこんなAPIを持つでしょう。まずは簡単に`User`-`Post`で一対多関係とします。
GETだけ書いていきますが、POSTやPUTはよしなに想像してください。

- <details><summary>`GET /users`</summary><div>
    - Response Body

    ```json
    {
        "users": [
            {"id": "userAbCd0001", "display_name":"John"},
            {"id": "userAbCd0002", "display_name":"Mike"}
        ]
    }
    ```

    </div></details>
- <details><summary>`GET /users/:id`</summary><div>
    - Response Body

    ```json
    {"id": "userAbCd0001", "display_name":"John"}
    ```
    </div></details>
- <details><summary>`GET /posts`</summary><div>
    - Query Stringで絞り込みができる。（`?user_id=userAbCd0001`など）
    - Response Body

    ```json
    {
        "posts": [
            {
                "id": "postAbCd0001",
                "created_at": "2017-10-01T15:00:00Z",
                "user_id": "userAbCd0001",
                "subject": "Today I learned...",
                "body": "...utterly nothing."
            },
            {
                "id": "postAbCd0001",
                "created_at": "2017-11-01T09:00:00Z",
                "user_id": "userAbCd0002",
                "subject": "Once upon a time",
                "body": "There was a cat.\n\nAnd he was great."
            }
        ]
    }
    ```
    </div></details>
- <details><summary>`GET /posts/:id`</summary><div>
    - Response Body

    ```json
    {
        "id": "postAbCd0001",
        "created_at": "2017-10-01T15:00:00Z",
        "user_id": "userAbCd0001",
        "subject": "Today I learned...",
        "body": "...utterly nothing."
    }
    ```
    </div></details>

## 2.2. `Decoder a`を定義する

Elm側では、シンプルに以下のように対応するRecordを定義します。

```elm
type alias User =
    { id : String
    , display_name : String
    }

type alias Post =
    { id : String
    , created_at : String
    , user_id : String
    , subject : String
    , body : String
    }
```

ElmではRecordを定義すると、**keyの定義順と引数順序が対応したコンストラクタが自動生成される**ことに注意してください。
つまり`User`と`Post`は、

```elm
User
--> <function> : String -> String -> User
Post
--> <function> : String -> String -> String -> String -> String -> Post
```

という関数になります。

そして、本題である`Decoder a`を実装します。

```elm
import Json.Decode exposing (Decoder)

userDecoder : Decoder User
userDecoder =
    Json.Decode.map2 User
        (Json.Decode.field "id" Json.Decode.string)
        (Json.Decode.field "display_name" Json.Decode.string)

postDecoder : Decoder Post
postDecoder =
    Json.Decode.map5 Post
        (Json.Decode.field "id" Json.Decode.string)
        (Json.Decode.field "created_at" Json.Decode.string)
        (Json.Decode.field "user_id" Json.Decode.string)
        (Json.Decode.field "subject" Json.Decode.string)
        (Json.Decode.field "body" Json.Decode.string)
```

**さあわからなくなる瞬間が来ました。**

### 2.2.1. そもそも`Decoder a`とはなにか

**`Decoder a`は関数です**。

関数であることを一見隠して`Maybe a`や`List a`などと似たような表現がなされているので誤解するかもしれませんが、関数です。
[docには][jdd]、

> A value that knows how to decode JSON values.

と書かれています。しかし"value"という単語に惑わされないでください。いや、正確に言うと、"value"という単語が`1`とか`"foo"`みたいな「いわゆる値」を指すという思い込みから解放されてください（もしあるのであれば）。関数だって"value"と呼ばれうるというだけです。この辺は計算機科学等のバックグラウンドを持つ人には自然なのかもしれません。

それを前提とすれば`Decoder a`は、

> A **function** that knows how to decode JSON values.

と言いかえられ、つまり「（生の）JSONの値を`a`という型のElmの値に変換する方法を知っている関数」になります。少しわかりやすくなりませんか。なりませんか。

### 2.2.2. 「（生の）JSONの値」？

ここで、「（生の）JSONの値」とは何なのでしょうか。`Json.Decode`のソースを見ると、Kernel moduleを使った実装になっているのでわかりづらいところですが、**要はJavaScriptの何らかの値です**。「`typeof`で見れば`'object'`や`'string'`などになる、それ」です。

つまり`Decoder String`なら、「JavaScriptの値をElmの`String`に変換する方法を知っている関数」なのです。デフォルトで、

```elm
string : Decoder String
```

という`Decoder`が用意されていますが、これなんかは最も基本的な「JavaScriptの値をElmの`String`に変換する関数」です。[実装][strImpl]を見てみましょう。これはJavaScriptです。

[strImpl]: https://github.com/elm/json/blob/1.1.3/src/Elm/Kernel/Json.js#L73-L79

```js:src/Elm/Kernel/Json.js#L73-L79
var _Json_decodeString = _Json_decodePrim(function(value) {
	return (typeof value === 'string')
		? __Result_Ok(value)
		: (value instanceof String)
			? __Result_Ok(value + '')
			: _Json_expecting('a STRING', value);
});
```

そのまんまといえばそのまんまです。

- 引数にあたる`value`のJavaScript上の型がプリミティブの`string`ならそのまま`Ok value`としてElmの世界に導入する
- プリミティブではないが、`String` objectなら、`value + ''`を挟むことでプリミティブの`string`に変換してからやはりElmに導入する
    - (こんなことできたんだ)
- どちらでもなければ「String的なもの」とはみなせないわけなので、エラーとする(Elm内では`Err message`となる）

[JavaScriptの型は色々と面倒][jstype]だそうです。Elmのコンパイラや標準ライブラリは、そのあたりをこうした境界防御ではっきりさせることで、Elmの世界の内側では余計な曖昧さが生じないようにしてくれています。

[jstype]: https://qiita.com/south37/items/c8d20a069fcbfe4fce85

### 2.2.3. 「型`a`なるElmの値に変換する関数」

だとすると話はだいぶ分かるようになってくるはずです。先程の例に戻ると、

```elm
userDecoder : Decoder User
userDecoder =
    Json.Decode.map2 User
        (Json.Decode.field "id" Json.Decode.string)
        (Json.Decode.field "display_name" Json.Decode.string)
```

このうち、`Json.Decode.string`はすでに見たとおり、JavaScript界で文字列的なものを、Elmの`String`としていわば正規化してくれる関数です。

では`Json.Decode.field`は何でしょう。[定義][jdf]を見ると、

[jdf]: http://package.elm-lang.org/packages/elm/json/1.1.3/Json-Decode#field

```elm
field : String -> Decoder a -> Decoder a
```

となっています。第一引数の文字列は明らかに、JSONの中のフィールド名を指定しているようですね。
一方、第二引数は別の`Decoder a`（**という関数**）を受け取っています。そして、この関数自体の返り値も同じ`Decoder a`型になる、つまりJSONからElmの`a`型の値を取り出す関数になるわけです。

なんとなくもう察しがついたと思いますが、

- `Json.Decode.string`
    - 「JavaScriptの文字列的な値をElmの`String`にする関数」
- `Json.Decode.field "field" Json.Decode.string`
    - **「JavaScriptのオブジェクトからフィールドを指定して値を取り出し、それが文字列的な値ならElmの`String`にする関数」**

となりました。どちらも、「JavaScriptの値」を入力、「Elmの`String`への変換結果」を出力とすることを意識してください。ここではJS/Elmの界面で事が繰り広げられてしまっているので、「JavaScriptの値」が少々特殊な扱いをされていて混乱を呼びそうですが、役割としてはほとんど同じなのです。対応しているJavaScriptの値の型が違うだけです。

`Json.Decode.field`単体でみれば、これは「**すでに存在する別の`Decoder a`に、「JavaScriptオブジェクトからフィールド指定して対象とする値を取り出し」という追加機能を付与して、新たな`Decoder a`を生成するユーティリティ関数**」ということになるでしょう。これはまさに、

> - 複雑なロジックを、
> - 簡単に組み合わせが効くような、
> - 小さな部品に還元する。

この一例になっています。つまり、

- `Json.Decode.string`などの個々の`Decoder a`が「小さな部品」、
- それらを`Json.Decode.field`などの関数によって「組み合わせ」て、
- **データを読み取って指定したElmの型の値として取り出す**という「複雑なロジック」を構成するわけです。

また、前節で、`Json.Decode.string`がJavaScriptから文字列的な値を正しくElmに持ち込めた場合は`Ok value`としていたことを思い出してください。このように、`Decoder a`という関数がJavaScriptの値に適用された場合、Elm側から見える結果は、

```elm
Result String a = Ok a | Err String
```

となっています。失敗することもあるわけなので、`Result x a`を使っています。

仕上げとして、Elm側から見た場合に、「「なんだか詳細はわからないがJavaScriptの値」であることを示す型」というのを仮に`Value`と定義すれば、`Decoder a`は以下のように再定義できるでしょう。[^1]

[^1]: 実際、[`Value`は定義されています](https://package.elm-lang.org/packages/elm/json/1.1.3/Json-Decode#Value)。

```elm
type alias Decoder a : Value -> Result String a
```

まさにこれが`Decoder a`の本質です。
もちろん、実際にはKernel moduleで実装されているので、このままではありませんが。

先に紹介したこのブログ記事でも、似たような概念(`JsonValue`)を導入して`Json.Decode`を解説しています。
[You could have designed the Json.Decode library!][des]

## 2.3. `mapN`

だいぶわかってきましたが、まだわからないことがあります。`Json.Decode.map2`です。

```elm
Json.Decode.map2 User
    (Json.Decode.field "id" Json.Decode.string)
    (Json.Decode.field "display_name" Json.Decode.string)
```

まだシンタックスに慣れていない人もいるかもしれないので、さらに単純にしましょう。`Json.Decode.field ...`部分は、「JSONからフィールド指定で値を取り出す`Decoder`」だとすでに確定したので、以下のようにprivate関数に置き換えてしまいましょう。

```elm
userDecoder : Decoder User
userDecoder =
    Json.Decode.map2 User idDecoder displayNameDecoder
```

<details><summary>(定義)</summary><div>

```elm
idDecoder : Decoder String
idDecoder =
    Json.Decode.field "id" Json.Decode.string

displayNameDecoder : Decoder String
displayNameDecoder =
    Json.Decode.field "display_name" Json.Decode.string
```
</div></details>

`map2`は3引数関数のようです。[定義][map2]はこうなっています。

[map2]: https://package.elm-lang.org/packages/elm/json/1.1.3/Json-Decode#map2

```elm
map2
    :  (a -> b -> value)
    -> Decoder a
    -> Decoder b
    -> Decoder value
```

なんかきな臭いですね。。。
**矢印いっぱいあると最初はわけわかんないですよね。**もう少しわかりやすくならないでしょうか。
（もう慣れちゃった人も、ML系言語に初めて触れたときのことを思い出してください）

### 2.3.1 `map`

先に述べておくと、`map2`もまた`field`と同様、`Decoder`を組み合わせ、何らか機能を付与して新たな`Decoder`を作る関数の一つで、最終的にはやはり`Decoder`を返します。

`map2`が`field`よりも少しわかりにくい（ように見える、ような気がする）のは、「組み合わせ」作業のときに、**パーツとして関数を使うから**ではないでしょうか。`field`もパーツとして「フィールド名」を受け取っていましたが、あちらは単なる`String`なので少し分かりやすかったのかもしれません。`Decoder`が本質的には関数だとわかった今、`map2`はこのように表現できます：

> **関数(`a -> b -> value`)をパーツとして
関数(`Decoder a`)と
関数(`Decoder b`)を組み合わせて新たな
関数(`Decoder value`)を返す
関数**

何も筆者は煙に巻こうとしているわけではありません。あえて言えば、Elmが関数型プログラミング言語であることを思い出してください。関数がたくさん出てきてもうろたえることはないのです。**そういうもんです。**しかもElmのような言語では、このように関数自体を関数の対象としてしまうことで、「小さな部品をぱちぱち組み合わせて複雑なロジックを構成」する書き方ができるようになっていきます。

さて、`map2`はちょっと複雑そうなので、一歩引いてみましょう。類似品に[`map`][map]というのがあります。

[map]: https://package.elm-lang.org/packages/elm/json/1.1.3/Json-Decode#map

```elm
map : (a -> value) -> Decoder a -> Decoder value
```

さらに、このような状況を考えてみましょう。`User`に`display_name`がなかったとします。

```elm
type alias User =
    { id : String }
```

ユーザたちはランダムなIDによってのみ管理され、そこに名前などという概念は存在しないディストピアです。このディストピアでは当然、`User`コンストラクタ関数も、必要なのは`id`だけですから、

```elm
User
--> <function> : String -> User

User "userAbCd0001"
--> { id = "userAbCd0001" } : User
```

このようになります。`Decoder`を書いてみましょう。

```elm
userDecoder : Decoder User
userDecoder =
    map User idDecoder
```

これが何を示すか。残念ながらKernel moduleの`map`実装はちょっと複雑なので、`map`が以下のように定義されていると仮定してしまってください。

```elm
map : (a -> value) -> Decoder a -> Decoder value
map func decoder value =
    case decoder value of
        Ok decoded ->
            Ok (func decoded)

        Err reason ->
            Err reason
```

**「引数の数、おかしくない？」**
→ 鋭い。でもこれは成立しているのです。

```elm
type alias Decoder a : Value -> Result String a
```

実質こうだったことを思い出してください。しかも`type alias`は展開する事ができます。つまり、

```elm
map : (a -> value) -> Decoder a -> Decoder value
-- ↓ 展開
map : (a -> value) -> Decoder a -> (Value -> Result String value)
```

（第2引数に当たる部分はここでは展開する必要が無いので、恣意的に展開していません。）
更に、右端の関数を囲っている`()`は実はなくても等価です。

```elm
map : (a -> value) -> Decoder a -> Value -> Result String value
map func decoder value =
    case decoder value of
        Ok decoded ->
            Ok (func decoded)

        Err reason ->
            Err reason
```

引数の数が合いました。さて、ElmやHaskellなどでは、関数に引数を左から順に適用していったとき、**足りない引数があったら、その時点で足りていない引数だけを取るような新たな関数が出来上がります**。
(特別な構文なしでこのような操作ができることを「関数が**カリー化されている**」といいます。ElmやHaskellなどでは、すべての関数がカリー化されています)

```elm:関数がカリー化されているさま
-- 2つの数で足し算する関数
add : number -> number -> number
add n m =
    n + m

-- addに1つだけ引数を与えることでできる、「1を足す」関数を導ける
add 1
--> <function> : number -> number
```

つまり、上述の`map`定義は3引数を取っていますが、そこに`func`と`decoder`に当たる2引数だけが与えられたら、どうなるでしょうか。

```elm
map User idDecoder
--> <function> : Value -> Result String User
--> <function> : Decoder User (と等価といえる)
```

ということで、`map`はきちんと`Decoder a`を返す関数として定義できていることになるのです。

前置きが長くなりましたが実装の中身を見ていくと、

- `decoder value`はそのまんま
    - `value : Value`を`decoder : Value -> Result String a`に渡せば`Result String a`が返ります
- `Ok decoded`なら、`decoded : a`を`func : a -> value`に適用し、`Ok`にラップして返します
    - すると関数全体としての返り値は`Result String value`になりました

つまり我々は`map`によって、「JavaScriptオブジェクトから"id"フィールドの値を取り出してElmの`String`の値に変換する関数(`idDecoder`)に、`String`から`User`を生成する関数(`User`)を組み合わせ、JavaScriptオブジェクトから`User`を生成する関数」を得ました。

このことをもう少し一般化すると、「**`Decoder a`に、生成された`a`を変換する関数`a -> value`を組み合わせることで、`Decoder value`を作り出す関数**」、それが`map`です。いわば**`Decoder`を変換**しているわけです。このことを表現して、docにも、

> Transform a decoder.

と説明されています。

### 2.3.2. Re: `mapN`

`map2`も`map`の延長線で捉えられます。**`Decoder a`と`Decoder b`を、生成される`a`と`b`をもとに`value`を作り出す関数で組み合わせて、`Decoder value`を作る関数**になりますね。元となる値を引っ張ってくる`Decoder`が2種類、`value`を生成する関数の引数も2つあるというのが違いです。中身的にはどちらにせよ`Result`ベースの分岐が使われているので、どちらかの`Decoder`が失敗に終われば、`Decoder value`が全体として失敗することになります。

こうなれば、ディストピアをやめて`User`に`display_name`を復活させても大丈夫でしょう。

```elm
type alias User =
    { id : String
    , display_name : String
    }

userDecoder : Decoder User
userDecoder =
    Json.Decode.map2 User idDecoder displayNameDecoder
```

`userDecoder`は、**JavaScriptの値（オブジェクト）で、"id"フィールドと"display_name"フィールドを持っていて、両方Elmの`String`に変換可能な値を持っているような値**を受け取れば、`User`型のElmの値を生成できる関数となったわけです。

ここまでくれば、`map3`から`map8`までのバリエーションももはや説明不要ではないでしょうか。

```elm
postDecoder : Decoder Post
postDecoder =
    Json.Decode.map5 Post
        (Json.Decode.field "id" Json.Decode.string)
        (Json.Decode.field "created_at" Json.Decode.string)
        (Json.Decode.field "user_id" Json.Decode.string)
        (Json.Decode.field "subject" Json.Decode.string)
        (Json.Decode.field "body" Json.Decode.string)
```

`postDecoder`もまた、JavaScriptの値から`Post`型の値を生成できるでしょう。必要なフィールドが全て揃っているJSONであれば。

## 2.4. 真価を発揮する

はじめに定義した増田クローンのAPIレスポンスを解釈する2つの`Decoder`を理解できたでしょうか。しかし、ここまでではまだ道半ばです。いくつか仕様を拡張してみましょう。

まず、今はユーザと投稿を取得する機能が`GET /user/:id`と`GET /posts`という2つのAPIに分かれていますが、ラウンドトリップを減らすためにも、少なくとも最新10件程度の投稿は`GET /user/:id`についてくるようにしてみたいと思います。

- `GET /user/:id`
    - Response Body

    ```json
    {
        "id": "userAbCd0001",
        "display_name": "John",
        "recent_posts": [
            {
                "id": "postAbCd0001",
                "created_at": "2017-10-01T15:00:00Z",
                "user_id": "userAbCd0001",
                "subject": "Today I learned...",
                "body": "...utterly nothing."
            }
        ]
    }
    ```

このようなケースでは、**`Decoder`による機能分割は非常に強力**です。

```elm
type alias User =
    { id : String
    , display_name : String
    , recent_posts : List Post
    }

userDecoder : Decoder User
userDecoder =
    Json.Decode.map3 User  -- < !!
        (Json.Decode.field "id" Json.Decode.string)
        (Json.Decode.field "display_name" Json.Decode.string)
        (Json.Decode.field "recent_posts" (Json.Decode.list postDecoder)) -- < !!
```

たったこれだけで、`List Post`という型で確実にチェックされた新しいフィールドを導入できてしまいました。`postDecoder`も`Decoder`であることで、**このような組み合わせがパズルをはめるかのように自在にできる**わけです。

ポイントは2つ：

- フィールドが1つ増えたので、`map3`になりました。
- `Json.Decode.list`が登場しました。が、もはやあまり説明もいらないのではないでしょうか。

```elm
list : Decoder a -> Decoder (List a)
```

`a`への変換方法を教えてもらえれば、それをもとに`List a`に変換する関数を生成できる関数、それが`list`です。対応しているJavaScriptの型は`Array` objectになります。`Array`の中に`a`に変換できない要素が混じっていれば失敗します。

こうなると、どんどん似たような拡張は入れていけそうです。

- フィールドが増えるなら、`map4`, `map5` ... と増やしていけば良い。
- プリミティブな`Decoder`は一通りある：`string`, `int`, `float`, `bool`
- 使い方が比較的単純そうなユーティリティもいくつかある：
    - `list : Decoder a -> Decoder (List a)`
    - `maybe : Decoder a -> Decoder (Maybe a)`、

なんてこった。**バラ色だ！**今ならどんなデータ構造でもdecodeできる気がする。

### 2.4.1. へんないきもの

前節で、「使い方が比較的単純そうなユーティリティ」という表現をしました。これは「逆」の存在を示唆しています。使い方がよくわからないへんないきものがいくつか並んでいます。

#### [`andThen : (a -> Decoder b) -> Decoder a -> Decoder b`][andThen]

[andThen]: https://package.elm-lang.org/packages/elm/json/1.1.3/Json-Decode#andThen

**この記事の中では、多分こいつがわかってしまえばあとはウィニングランみたいなもんです。**

`map`と似ていますが、第1引数の関数が`(a -> b)`ではなく`(a -> Decoder b)`になっています。これはちょっと難しいシロモノだと思いますが、こう考えてみてください。

`mapN`は、N個の`Decoder`を「**並列に**」組み合わせます。

```elm
import Json.Decode exposing (map5, field, string)

map5 Post               -- Post : String -> String -> String -> String -> String -> Post
    (field "id" string)         --┘         |         |         |         |
    (field "created_at" string) ------------┘         |         |         |
    (field "user_id" string)    ----------------------┘         |         |
    (field "subject" string)    --------------------------------┘         |
    (field "body" string)       ------------------------------------------┘
```

この無駄に努力して作ったダイアグラムのとおり、`mapN`で組み合わされた`Decoder`たちは、**それぞれ独立に評価され**、結果が集約関数(ここでは`Post`)の引数に左から適用されていきます。"id"フィールドの`Decoder`の評価結果は、（成功したか失敗したか、という情報以外は）後続する"created_at"の評価に一切影響しない、という意味で「並列」です。

`andThen`は、2つ以上の`Decoder`を「**直列に**」組み合わせる（生成する）ものと思ってください。つまり、前提となる`Decoder a`の評価結果、成功か失敗かだけでなく、**実際にdecodeされて得られた`a`型の値の中身が、後続する`Decoder b`の生成に影響する**のです。極端な話、`Decoder a`が成功しても、得られた`a`の中身が想定外だったら、常に失敗するような`Decoder b`を生成する、といったことができます。[Doc][andThen]にある例はまさにこれです。

```elm:andThenのサンプルコード
info : Decoder Info
info =
  field "version" int
    |> andThen infoHelp

infoHelp : Int -> Decoder Info
infoHelp version =
  case version of
    4 ->
      infoDecoder4

    3 ->
      infoDecoder3

    _ ->
      fail <|
        "Trying to decode info, but version "
        ++ toString version ++ " is not supported."

-- infoDecoder4 : Decoder Info
-- infoDecoder3 : Decoder Info
```

`info`の前段`field "version" int`は、すでに出てきたものと同じ、JSONの"version"フィールドを整数として読み取る`Decoder`です。これを`|> andThen infoHelp`で、`Decoder Info`を生成する関数である`infoHelp`と組み合わせていますが、`infoHelp`は**前段の`Decoder`が生み出した`Int`値の中身に依存しています**。

フィールド間に依存関係がある場合など、`|> andThen`で直列に条件判定を連結していけるというわけです。すぐには必要にならないかもしれませんが、複雑なデータ構造を相手にするときにお世話になるでしょう。

```elm:こんなことが必要になるAPIは相手にしたくないやつ
fooDecoder
    |> andThen barDecoderGen
    |> andThen banDecoderGen
    |> andThen bazDecoderGen
```

（**上から順に調べていって、どこかで失敗した時点で次に進まない、という意味では`mapN`が「直列」ともいえます**。そのほうがしっくりくる人も多いかもしれません。ただ筆者の持っている印象としては`andThen`を直列、`mapN`を並列と考えると捉えやすいと思います）

#### [`fail : String -> Decoder a`][fail]

[fail]: https://package.elm-lang.org/packages/elm/json/1.1.3/Json-Decode#fail

「**入力を無視して必ず失敗し、指定したエラーメッセージを返す`Decoder`を生成する関数**」になります。`andThen`の例の中で部品としてすでに出てきていました。`andThen`と組み合わせれば、何らかの前提条件を満たさない場合は失敗する、という`Decoder`を生み出すのに使えますね。あるいは開発途中で、APIレスポンスがdecodeできないケースを再現するのに使ったりできそうです。

#### [`succeed : a -> Decoder a`][succ]

[succ]: https://package.elm-lang.org/packages/elm/json/1.1.3/Json-Decode#succeed

`fail`の逆で、「**入力を無視して「その値」を常に返す`Decoder`を生成する関数**」です。これだけだと一見なんのためにこんなものがあるのかわからないシロモノですが、やっぱり`andThen`と組み合わせれば、「前提条件によってはデフォルト値（固定値）を返す`Decoder`」を作れるでしょう。またはAPIがまだ完成していない段階で、ダミーデータを生成して常に成功するような`Decoder`を用意して開発するのも場合によってはありかもしれません。

また、`oneOf : List (Decoder a) -> Decoder a`というのもあるので、フォールバック（デフォルト）値を持つような`Decoder`であれば、`andThen`を使うまでもなく以下のようにも書けます。

```elm
fallbackDecoder : Decoder String
fallbackDecoder =
    Json.Decode.oneOf
        [ Json.Decode.string
        , Json.Decode.succeed ""
        ]
```

実は`succeed`には他にも使い方があります。後述します。

## 2.5. 魔物：再帰的データ構造

増田クローンをさらに増田に近づけてみましょう。`Post`は、別の`Post`への返信という形で投稿することができるものとします。

```json
{
    "id": "postAbCd0001",
    "created_at": "2017-10-01T15:00:00Z",
    "user_id": "userAbCd0001",
    "subject": "Today I learned...",
    "body": "...utterly nothing.",
    "in_reply_to": "postaBcD0001"
}
```

しかしこれだと、"in_reply_to"に含まれるpost_idについて、`GET /posts/:id`を繰り返し呼ばなければpostの実体を得られません。スレッドが長く続いているようなケースではラウンドトリップが増えまくりますね。

一方で、サーバ側ではdatabaseからのデータロード戦略をきちんと設計すれば、パフォーマンスをある程度保ったまま関係するpostを効率的に読み出せるわけですから、ここはサーバ側にやらせてみましょう。つまり、"in_reply_to"にそのままpostが入ってくるようにしてしまいましょう。

```json
{
    "id": "postAbCd0001",
    "created_at": "2017-10-01T15:00:00Z",
    "user_id": "userAbCd0001",
    "subject": "Today I learned...",
    "body": "...utterly nothing.",
    "in_reply_to": {
        "id": "postaBcD0001",
        "created_at": "2017-10-01T14:00:00Z",
        "user_id": "userAbCd0002",
        "subject": "nya-n",
        "body": "nya-n",
        "in_reply_to": {
            "id": "postaaaa0001",
            "created_at": "2017-10-01T13:00:00Z",
            "user_id": "userAbCd0001",
            "subject": "nya-n",
            "body": "nya-n"
        }
    }
}
```

もちろん、いくらでも"in_reply_to"を祖先に向かって辿っていけるようにしてしまうとパフォーマンス不安があります（古いpostはキャッシュに乗っていなかったりしそうです）。そもそもそんなに深く辿れる必要もあまりないでしょう。ということでここではmax_depth=2を暗黙に仮定し、in_reply_toはない可能性もある、とします。

ElmのRecordを再定義しましょう。

```elm
type alias Post =
    { id : String
    , created_at : String
    , user_id : String
    , subject : String
    , body : String
    , in_reply_to : Maybe Post -- << !!!
    }
```

さあ、こいつは成立しているでしょうか？[Ellieで試してみます。][badRec](0.18時代のコードのままですが、結果だけは表示されます)

[badRec]: https://ellie-app.com/kTmNYjRSta1/0

> ALIAS PROBLEM
Line 5, Column 1
This type alias is recursive, forming an infinite type!

> When I expand a recursive type alias, it just keeps getting bigger and bigger. So dealiasing results in an infinitely large type! Try this instead:

> 
```elm
type Post
    = Post
        { id : String
        , created_at : String
        , user_id : String
        , subject : String
        , body : String
        , in_reply_to : Maybe Post
        }
```
> This is kind of a subtle distinction. I suggested the naive fix, but you can often do something a bit nicer. So I would recommend reading more at: https://github.com/elm-lang/elm-compiler/blob/0.18.0/hints/recursive-alias.md

**なにか複雑なことを言われています。**再帰的データ構造は色々と警戒が必要な魔物です。
エラーメッセージ的には`type alias`の展開において無限後退が発生しているといっているのですが、せっかくなのでこれは（解決法とdecodeの仕方も含めて）別記事にしようと思います。**この記事すでに長すぎるんですよね**。

=> [Elmで再帰的データ構造を扱う][rec]

よろしければご覧ください。

## 2.6. Advanced: `mapN`からの卒業

再帰部分さえどうにかできれば、どうにか増田クローンのようなデータを扱うことはできそうですが、フィールドが増えるたびに`mapN`の`N`を増やすのってダルくないですか？大丈夫、すぐにダルくなると思います。そもそも[`map8`][map8]までしかありません。

[map8]: https://package.elm-lang.org/packages/elm/json/1.1.3/Json-Decode#map8

Docにもそのことは言及されていて、[`Json.Decode.Pipeline`][jdp]という解決策が提案されています。

[jdp]: https://package.elm-lang.org/packages/NoRedInk/elm-decode-pipeline/3.0.1

```elm
import Json.Decode exposing (Decoder, succeed, string)
import Json.Decode.Pipeline exposing (required)

postDecoder : Decoder Post
postDecoder =
    succeed Post
        |> required "id" string
        |> required "created_at" string
        |> required "user_id" string
        |> required "subject" string
        |> required "body" string
```

見た目すごくきれいな感じですね。`(|>)`を使うようになったことで`()`も不要になりました。[`required`][req]の正体は以下です。

[req]: https://package.elm-lang.org/packages/NoRedInk/elm-decode-pipeline/3.0.1/Json-Decode-Pipeline#required

```elm
required : String -> Decoder a -> Decoder (a -> b) -> Decoder b
```

フィールド名を取るあたりは`field`と似ていますが、、、**いよいよ出てきてしまいました。`Decoder (a -> b)`です。**

「JavaScriptの値からElmの関数を生成する`Decoder`。。。？」

謎が深まります。これを理解するためには、一旦「JavaScriptの値から」を忘れましょう。上のコードに`succeed`が登場していることに注目してください。`succeed`は「指定した値を（どんな値でも）返すような`Decoder`を自由に生成」できますから、関数を生成するような`Decoder`も作れてしまいます。

```elm
succeed Post
--> Decoder (String -> String -> String -> String -> String -> Post)
```

**なんか引数多い**ですね。でも大丈夫です。すでに述べたカリー化のために、**「5引数関数」は、「4引数関数を返す1引数関数」と考えて差し支えない**からです。

その前提で考えると：

```elm
succeed Post
    |> required "id" string
--> Decoder (String -> String -> String -> String -> Post)
```

つまりここでも、JavaScriptオブジェクトのフィールドから取り出したElmの値を、`Post`関数に左から適用したにすぎないのです。`mapN`と中身としては同じで、書き方が変わっただけです。しかもこの**Pipelineスタイルでは、全く同じ書き方で`Decoder`を連結できます**。

```elm
succeed Post
    |> required "id" string
    |> required "created_at" string
--> Decoder (String -> String -> String -> Post)

succeed Post
    |> required "id" string
    |> required "created_at" string
    |> required "user_id" string
--> Decoder (String -> String -> Post)

succeed Post
    |> required "id" string
    |> required "created_at" string
    |> required "user_id" string
    |> required "subject" string
--> Decoder (String -> Post)

succeed Post
    |> required "id" string
    |> required "created_at" string
    |> required "user_id" string
    |> required "subject" string
    |> required "body" string
--> Decoder Post
```

あれぇ？`Decoder Post`ができてしまった。では`Post`に"keywords"が増えたらどうでしょう。

```elm
type alias Post =
    { id : String
    , created_at : String
    , user_id : String
    , subject : String
    , body : String
    , keywords : List String -- << !!!
    }
    
succeed Post
    |> required "id" string
    |> required "created_at" string
    |> required "user_id" string
    |> required "subject" string
    |> required "body" string
--> Decoder (List String -> Post) -- << あとはkeywordsをdecodeする部品があれば良い

succeed Post
    |> required "id" string
    |> required "created_at" string
    |> required "user_id" string
    |> required "subject" string
    |> required "body" string
    |> required "keywords" (list string) -- << !!!
--> Decoder Post
```

できちゃった。

このスタイルは`Decoder`の、

> - 簡単に組み合わせが効くような、

この特性を強烈にブーストします。`Post`という関数に、左の引数から順に対応する`Decoder`をポチポチくっつけていけば`Decoder Post`の完成です。しかも`mapN`と違って、`Post`の引数の数を気にしなくてもいいんです。`succeed`して順繰りに`Decoder`をくっつけていくだけでいい。**なんてこった。便利すぎる。**

## 2.7. まとめ

これにて`Json.Decode`の基本的な流れは把握できました。増田クローンのようなデータ構造も問題なく扱えそうです（[再帰型の件はこちらで][rec]）。

[rec]: https://qiita.com/ymtszw/items/a10229de887b38c7a65b

- `Decoder a`は本質としてはElmの`a`型の値を生み出す関数である
- このような抽象化は、
    - 複雑なロジックを、
    - 簡単に組み合わせが効くような、
    - 小さな部品に還元することを可能にする
- Pipelineスタイルなら、更にこれを自在に書ける

めでたしめでたし。長らくお付き合い頂きありがとうございました。

# 3. おまけ

めでたしですね。**ここから先は読まなくても大丈夫です。**

**その筋の人は**、`Decoder`がFunctorであり、Applicativeであり、Monadであることに気づいたかもしれません。私がこれらの概念について整理するときには大抵こちらの記事↓を参考にして浅瀬チャプチャプしますが、

- [箱で考えるFunctor、ApplicativeそしてMonad - Qiita][hako]
[hako]: https://qiita.com/suin/items/0255f0637921dcdfe83b

`Json.Decode.map`は明らかに[`fmap`][fmap]ですね。 

[fmap]: http://hackage.haskell.org/package/base-4.10.1.0/docs/Prelude.html#v:fmap

`Json.Decode.andThen`は[`=<<`][fbind]の関数版です。`|> andThen`とすれば擬似的に[`>>=`][bind]です。

[fbind]: http://hackage.haskell.org/package/base-4.10.1.0/docs/Prelude.html#v:-61--60--60-
[bind]: http://hackage.haskell.org/package/base-4.10.1.0/docs/Prelude.html#v:-61--60--60-

Pipelineスタイルは、`Decoder (a -> b)`の`a`部分に`Decoder a`の`a`を適用していくわけですから、ワークフローとしてはまさにApplicativeです。

類似のスタイルで[Json.Decode.Extraの`andMap`を`|> andMap (...)`のように使う](https://github.com/elm-community/json-extra/blob/4.0.0/docs/andMap.md)のもあります。こちらは[`required`][req]と違い、汎用的な`Decoder`をapplyしていくことができるようになっていて、

```elm
succeed Post
    |> andMap (field "id" string)
    |> andMap (field "created_at" string)
    |> andMap (field "user_id" string)
    |> andMap (field "subject" string)
    |> andMap (field "body" string)
```

と書けます。[`andMap`の定義](https://github.com/elm-community/json-extra/blob/4.0.0/src/Json/Decode/Extra.elm#L111-L113)はこれだけです。

```elm
andMap : Decoder a -> Decoder (a -> b) -> Decoder b
andMap =
    map2 (|>)
```

こちらも`|> andMap`とすれば擬似的に[`<*>`][<*>]です。(0.18ではカスタム演算子を定義できたので、`|:`として提供されていました)

[<*>]: http://hackage.haskell.org/package/base-4.10.1.0/docs/Control-Applicative.html#v:-60--42--62-

筆者は`Decoder`を振り回しているにつれて、今までよくわかっていなかったApplicativeがどういうシロモノなのか、なんとなくわかった気がしています。**なので`Decoder`が好きです**。教育的です。

また、皆さん本アドカレの先週の@jinjorさんの記事は読んだでしょうか。

- [Elm で構文解析しよう](https://qiita.com/jinjor/items/d0d4b83b530251df913e)

**`Parser`と`Decoder`は、構造的にほっとんど同じです。**

`|> andMap`(旧`|:`)はまんま[`Parser.(|=)`][|=]です。

[|=]: http://package.elm-lang.org/packages/elm-tools/parser/2.0.1/Parser#|=

Elmは本当に教育的ですね。雰囲気でApplicativeもわかってしまうのです。