Note: カスタム型(Custom type)は Elm では以前“ユニオン型(union type)”と呼ばれていました。他のコミュニティでの名前としてはtagged unionADTなどがあります。

カスタム型(Custom type)

これまでBoolIntStringのような Elm に元から用意された型を使ってきました。ですが、自分の型をどうやって定義したらよいでしょうか?

例えば、チャットルームを作っているとします。全員に名前が必要ですが、永続的なアカウントを作らない人もいるかもしれません。そういう人にはチャットルームに入る度に名前を付けてもらうことにしましょう。

UserStatus型を定義し全ての可能なバリアントを列挙することで、この状況を表現することができます:

type UserStatus = Regular | Visitor

UserStatus型は 2 つの バリアント を持っています。ユーザはRegularVisitorになれます。つまりユーザをこのようなレコードで表すことができます:

type UserStatus
  = Regular
  | Visitor

type alias User =
  { status : UserStatus
  , name : String
  }

thomas = { status = Regular, name = "Thomas" }
kate95 = { status = Visitor, name = "kate95" }

こうすれば、誰がアカウントを持っているRegularか、通りすがっただけのVisitorなのかを把握できます。このやり方は難し過ぎるということはありませんが、もっとシンプルにできます。

上述のようにカスタム型と型の別名を作成するのではなく、すべてを単一のカスタム型で表すことができます。RegularVisitorのバリアントにはそれぞれ関連するデータを持たせることができます。今回の場合、関連するデータはStringの値です:

type User
  = Regular String
  | Visitor String

thomas = Regular "Thomas"
kate95 = Visitor "kate95"

名前のデータはバリアントに直接付与されたので、レコード型はもう必要ありません。

この方法の別の利点は各バリアントには別の関連データを持たせられることです。Regularなユーザがサインアップのときに年齢を登録することを考えてみてください。これをレコードでどうにかするいい方法はありませんが、カスタム型を自分で定義するなら何の問題もなく行えます。Regularバリアントに他の関連データを追加してみましょう:

type User
  = Regular String Int
  | Visitor String

thomas = Regular "Thomas" 44
kate95 = Visitor "kate95"

ある型において、各バリアントはそれぞれまったく異なる構造をとることができます。例えば、Regularユーザに地域ローカルなチャットルームを提供するために現在地を追加することがあるかもしれません。更なる関連データを追加しましょう!また、匿名ユーザの機能が欲しくなるかもしれません。3 つ目のAnonymousバリアントを追加しましょう。最終的にこうなります:

type User
  = Regular String Int Location
  | Visitor String
  | Anonymous

何も問題はありません!ここで別の例を見てみましょう。

メッセージ

"The Elm Architecture"の節ではMsg型の定義の例をいくつか見てきました。この手の型は Elm では非常に一般的です。チャットルームアプリではこのようにMsg型を定義するかもしれません:

type Msg
  = PressedEnter
  | ChangedDraft String
  | ReceivedMessage { user : User, message : String }
  | ClickedExit

この型には 4 つのバリアントがあり、関連データを持つバリアントと持たないバリアントがあります。他のバリアントは関連データを持っています。ReceivedMessageが実際に関連データとしてレコードを持っていることに気づきましたか?これはまったく問題ありません。どんな型も関連データにすることができます!どんな型も関連データにすることができるため、どのユーザーからどんなメッセージを受けたかなど、ただの String や User の組み合わせでは何を意味するのか曖昧になってしまうような情報も非常に厳密に記述することができます。

型の設計

ある状況を非常に厳密に表現し始めるのにカスタム型は極めて強力です。例えばもし何かデータをロードされるのを待っているとしたら、その状況をこのようにカスタム型で表現したいかもしれません:

type Profile
  = Failure
  | Loading
  | Success { name : String, description : String }

Profile のデータの状態をLoadingから始めて、フェッチに失敗したらFailure、成功したらSuccessというように何が起こったかに応じて状態を遷移させることができます。このような設計はview関数を書くのを本当にシンプルにします。データの状態がカスタム型で表現されているのでview関数はデータをロードしているときも常に妥当な見た目を表示することができます。

ここまででカスタム型の作り方を知りました。次の節ではそれの使い方を学びましょう!

Note: カスタム型は Elm で最も重要な機能です。 特に一度でもより厳密にシナリオを設計しようとする習慣を持てば、カスタム型に非常に深みを感じるでしょう。わたしはこの深みを付録の集合としての型型のビット表現で共有しようと試みています。助けになれば幸いです!

results matching ""

    No results matching ""