elm-form-decoderでユーザ入力に型付けたらelm-graphqlでドーン

[アドカレ記事みんな頑張りすぎ問題](https://qiita.com/advent-calendar/2019/vim-short-tips)に共感しているので、できるだけ短くしたい。

## 道具立て

[arowM/elm-form-decoder](https://github.com/arowM/elm-form-decoder)は、HTML formからのユーザ入力に、

- アプリケーションの要請に基づくValidationを行い、
- かつ、適切な型を与える、

優れた抽象化です。

また、[dillonkearns/elm-graphql](https://github.com/dillonkearns/elm-graphql)は、`SelectionSet`という型を中心とするAPIで、

- サーバ側のexposeするGraphQLスキーマに沿ったElm側moduleを自動生成した上で、
- GraphQLの強みである操作(operation)の組み合わせを容易にする、

よく練られたパッケージです(現在ElmでGraphQLを扱うなら、もっとも有力視されていると言っていいかと)。

どちらも[`Json.Decoder`のような抽象化にある程度慣れていれば](https://qiita.com/ymtszw/items/1cabbdbda4273b4c1978)、そこから直観的にステップアップできるでしょう。

両者の日本語解説・紹介記事をいくつかリンク:

- [フォームバリデーションからフォームデコーディングの時代へ](https://qiita.com/arowM/items/6c32db1f9e4b92445f3b)
- [arowM/elm-form-decoderのAPIを【かんぜんりかい】しよう！](https://qiita.com/miyamo_madoka/items/d02f003ec1c212360111)
- [ElmとGraphQLの出会い - さようならJson Decoder !](https://qiita.com/kyasu1/items/c441fa3c8b6c180a4069)

## FormをdecodeしたらSelectionSetにぶちこめばいっちょ上がりなんだよなあ

elm-form-decoderでは`Form`のような、適当に用意したレコードなどにまずユーザ入力を文字列[^1]で受けておき、それを適切な型のついたElmの値に"Decode"するワークフローになっています。そのための手続きを格納しているのが`Form.Decoder`です。

[^1]: `type="checkbox"`のみ`Bool`でもユーザ入力を保持可能

```elm
Form.Decoder input error output
```

そういうわけですから、入力の型、だめだったときのエラーの型、うまく行ったときの出力の型が必要になるわけですね。

```
ユーザ入力
 ↓
Form
 |
 | (Form.Decoder Form error output)
 ↓
output
```

一方elm-graphqlでは、サーバのスキーマを元にoperation(queryやmutation)が関数としてコード生成されます。それらの関数は`SelectionSet`型を持ちます。`SelectionSet`型は、

- 組み合わせ・合成(compose)できるようになっている
- Composeしたものを最終的に送信APIに投入すれば、サーバへの送信とエラー検知はよしなにやってくれる[^2]
- あるoperationのために必要な引数の情報を格納する
- さらに、operationが成功したときのレスポンスを最終的にどのようなElm型で受けるか、という情報も保持する、

[^2]: もちろん、自分のアプリ用に更に使いやすくラップしたAPIを適宜用意するのもアリ。

といった仕組みになっています。


```elm
SelectionSet decodesTo typeLock
```

(`typeLock`は若干わかりにくいですが、operationの種類を示す`RootQuery`, `RootMutation`, `RootSubscription`のどれかや、codecでの変換元データ型が入ります)

フォームから値を受け取ってなにかするのはたいていmutationで、mutationには引数(arguments)が必要であり、レスポンスの型共々スキーマに定められています。

elm-graphqlはスキーマを元に以下のような型や関数を生成します。

```elm
-- 生成コード

type alias MyMutationRequiredArguments =
    { value : String
    }


myMutation :
    MyMutationRequiredArguments
    -> SelectionSet decodesTo AutoGenerated.Object.ResponseObject
    -> SelectionSet decodesTo RootMutation
-- 実装は複雑なので略
```

つまり、**もっとも単純な使い方としては、Formのユーザ入力を`MyMutationRequiredArguments`にdecodeして、`myMutation`関数で`SelectionSet`につなぎこめばいい**わけです。

このとき、スキーマに書いてあるレスポンスの型(例では`ResponseObject`)を、その後使用するElmの型に変換する`SelectionSet`も与えます。

```
ユーザ入力
 ↓
Form
 |
 | (Form.Decoder Form error MyMutationRequiredArguments)
 ↓
MyMutationRequiredArguments
 |
 | (myMutation)
 | (SelectionSet AtodeTsukauKata ResponseObject)
 ↓
SelectionSet AtodeTsukauKata RootMutation
```

こいつをelm-graphqlの送信APIにドーンぶちこめば`Result error AtodeTsukauKata`になって返ってくっから！じゃ、あとはよろしく！あばよ！

## 型で守られたぬるま湯で純粋培養されて生きていたい

elm-graphqlの生成する型は(バグがない限りは)スキーマと一致しているので、[^3]

[^3]: ちなみに今はintrospectionクエリをAPIに実際に送って生成するようになっていますが、静的なスキーマファイルから生成するオプションも提案されてたので入りそうな気配はある

```
ユーザ入力
 ↓
Form                                     -┐
 ↓                                        |
MyMutationRequiredArguments               |     ∧∧∧∧∧∧∧∧
 ↓                                        |-- < 型で守られてる！>
SelectionSet AtodeTsukauKata RootMutation |     ∨∨∨∨∨∨∨∨
                                          |
GraphQL schema                           -┘
```

もし、サーバ側のAPIもGraphQL Schemaからある程度自動生成する実装方法で作られていれば、

```
ユーザ入力
 ↓
Form                                     -┐
 ↓                                        |
MyMutationRequiredArguments               |     ∧∧∧∧∧∧∧∧
 ↓                                        |-- < 型で守られてる！>
SelectionSet AtodeTsukauKata RootMutation |     ∨∨∨∨∨∨∨∨
 ↓                                        |
GraphQL API (GraphQL schema)             -┘
```

何ならサーバの実装言語が静的型付きならDBまでずっと型で守られます。うれＣ

真面目な話、`Form.Decoder`も`SelectionSet`もよく考えられていて、特に**composabilityが高くかつ迷いづらい**(型パズルやってりゃだいたいやりたいことが完成する)ので、Elmでサービス開発する喜びが加速すると思います。もっと詳しく、という方はコメントやTwitterで質問お答えします。(アドカレ時期ですので、他にも記事出てくるかも)

では良いElmライフを！