ElmのMaybeはデータの有無を型で表現できるゆえ非常に便利なものであるが、文脈が失われるため無闇に使い過ぎるとワケがわからなくなる。ケースによっては、カスタム型を使うことによって型でデータの有無を Maybe に代えて表現するほうがよりメンテナブルにある可能性がある。
前提
ユーザーログインの機能を持つTODOアプリを開発している。
上記の仕様から、おそらくモデルに currentUser: User
みたいなフィールドと TODO を表す todos: List Todo
があるだろうと言うことは容易に想像できる。
だが、実際には「ログインしていない」と「ログインしている」のふたつのコンテキストを表現しなければいけないため、両方とも Maybe になる。ログインしていなければ両方共 Nothing になる。
type alias Model = { currentUser: Maybe User , todos: Maybe List Todo }
正直ここの Maybe は苦しい。なぜならこの Maybe は文脈を持っていないからだ。
例えば、ログインに失敗して Nothing なのか、純粋にまだ TODO を一個も持っていないから Nothing なのかは、ここでは分からない。 ユーザーがログインに失敗したから Nothing なのか、まだログインしていないから Nothing なのかも、判断が難しい。場当たり的な処置として Result 型を使う手もある。だが、結局文脈をもたない Maybe はコードを書く側が頑張ってその文脈をコード・リーディングしながら紐解いていくことになる。これはまず大変だし、バグを生む可能性がある。
ではログインにまつわる文脈を持たせましょう、ということで LogInStatus
型のデータを与えると以下のようになる。
type alias Model = { currentUser: Maybe User , todos: Maybe List Todo , logInStatus: LogInStatus } type LogInStatus = NotLoggedIn | LoggingIn | LogInSucceeded | LogInFailed
これでモデルにログイン文脈のデータが与えられたが、それでもまだ苦しい。
それぞれの Maybe の結果は LogInStatus
と独立しているため、開発者のうちひとりが LogInSucceeded
の状態であるにも関わらず currentUser
を Nothing のままにしているなどのコードを書いてしまう可能性がある。ログインが成功しているのにユーザーがいない、というのはまず仕様としてもおかしい。
カスタム型で仕様を表現する
Elmは型付言語なので、仕様を表現するような型制約を設けるのがよい
たとえば、以下のようにデータが前提としてログインの状態に紐づくようにすることによって Maybe を使わずに currentUser
や todos
の存在有無を表現できる
type alias UserData = { currentUser: User , todos: List Todo } type Model = NotLoggedIn | LoggingIn | LogInSucceeded UserData | LogInFailed
このコードであれば、まずログインが成功していない限りユーザーも、TODOも存在していないということが型レベルで表現されるため、もとのコードにあったような Maybe の結果とログイン状態が食い違ってしまうようなコードを書いてしまうのを防ぐことができる。

- 作者:鳥居 陽介
- 出版社/メーカー: シーアンドアール研究所
- 発売日: 2019/02/27
- メディア: 単行本(ソフトカバー)