アドカレ記事みんな頑張りすぎ問題に共感しているので、できるだけ短くしたい。
道具立て
arowM/elm-form-decoderは、HTML formからのユーザ入力に、
- アプリケーションの要請に基づくValidationを行い、
- かつ、適切な型を与える、
優れた抽象化です。
また、dillonkearns/elm-graphqlは、SelectionSet
という型を中心とするAPIで、
- サーバ側のexposeするGraphQLスキーマに沿ったElm側moduleを自動生成した上で、
- GraphQLの強みである操作(operation)の組み合わせを容易にする、
よく練られたパッケージです(現在ElmでGraphQLを扱うなら、もっとも有力視されていると言っていいかと)。
どちらもJson.Decoder
のような抽象化にある程度慣れていれば、そこから直観的にステップアップできるでしょう。
両者の日本語解説・紹介記事をいくつかリンク:
- フォームバリデーションからフォームデコーディングの時代へ
- arowM/elm-form-decoderのAPIを【かんぜんりかい】しよう!
- ElmとGraphQLの出会い - さようならJson Decoder !
FormをdecodeしたらSelectionSetにぶちこめばいっちょ上がりなんだよなあ
elm-form-decoderではForm
のような、適当に用意したレコードなどにまずユーザ入力を文字列1で受けておき、それを適切な型のついたElmの値に"Decode"するワークフローになっています。そのための手続きを格納しているのがForm.Decoder
です。
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型で受けるか、という情報も保持する、
といった仕組みになっています。
SelectionSet decodesTo typeLock
(typeLock
は若干わかりにくいですが、operationの種類を示すRootQuery
, RootMutation
, RootSubscription
のどれかや、codecでの変換元データ型が入ります)
フォームから値を受け取ってなにかするのはたいていmutationで、mutationには引数(arguments)が必要であり、レスポンスの型共々スキーマに定められています。
elm-graphqlはスキーマを元に以下のような型や関数を生成します。
-- 生成コード
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
ユーザ入力
↓
Form -┐
↓ |
MyMutationRequiredArguments | ∧∧∧∧∧∧∧∧
↓ |-- < 型で守られてる!>
SelectionSet AtodeTsukauKata RootMutation | ∨∨∨∨∨∨∨∨
|
GraphQL schema -┘
もし、サーバ側のAPIもGraphQL Schemaからある程度自動生成する実装方法で作られていれば、
ユーザ入力
↓
Form -┐
↓ |
MyMutationRequiredArguments | ∧∧∧∧∧∧∧∧
↓ |-- < 型で守られてる!>
SelectionSet AtodeTsukauKata RootMutation | ∨∨∨∨∨∨∨∨
↓ |
GraphQL API (GraphQL schema) -┘
何ならサーバの実装言語が静的型付きならDBまでずっと型で守られます。うれC
真面目な話、Form.Decoder
もSelectionSet
もよく考えられていて、特にcomposabilityが高くかつ迷いづらい(型パズルやってりゃだいたいやりたいことが完成する)ので、Elmでサービス開発する喜びが加速すると思います。もっと詳しく、という方はコメントやTwitterで質問お答えします。(アドカレ時期ですので、他にも記事出てくるかも)
では良いElmライフを!