Elmのカスタム型がわかんなくなったときに読む記事

Elmを学び始めた方がよくつまづくポイントに「カスタム型」があります。
さくらちゃんも別の言語で最初にカスタム型のような概念に出会ったとき、頭がぐちゃぐちゃになりました。むじゅかしぃ😢

ということで、カスタム型につまづいた方の理解の助けになれるよう、解説していきます🖊

# まずはEnumからはじめよう

別の言語でEnumとか列挙型とかいった概念を知っている方であれば、きっと以下の例までは理解しやすいと思います。

```elm
type Color
    = Red
    | Blue
    | Green
```

ここでは色を表すための独自のあたらしい型`Color`を定義しています。
`Red`(赤)、`Blue`(青)、`Green`(緑)の3色を表現できます。

もちろん、文字列型を使って`"Red"` `"Blue"` `"Green"` と表現することもできますが、それでは誤って `"Glue"` みたいなデータを入れてしまう可能性があります。
厳密に「これらの値しかありえない」と分かっているなら、上記のようにとりうる具体的な値を決めた独自の型 **カスタム型** を定義することでそういったミスを防げるわけです。

さて、この型定義は実は`Color`という **型** を定義しているだけではありません。だって、型だけ定義してその型をもつ値をつくる方法がなかったら意味ないじゃないですか。
だから同時に`Red` `Blue` `Green` という **値** も定義するのがこの構文の正体なんです。
言い方を変えれば、`Red` `Blue` `Green`という `Color`型の **定数** を定義していることになります。

定数は小文字からはじまるはずなのに、これだけは大文字はじまりの定数です。ひどいことをしますね！💢🐐
イメージとしては、上記の型定義によって以下のような **定数** が定義されているということです。

```elm
Red : Color
Red = (なんちゃらかんちゃら)

Blue : Color
Blue = (なんちゃらかんちゃら)

Green : Color
Green = (なんちゃらかんちゃら)
```

そして、Elmにおいて **定数** とは「引数なしの関数」と同じだということを少し頭の隅に置いておいてください🤔

# 型引数をマスターしよう

ここからがつまづきポイントです。

さて、よくある会員登録が必要なWebサービスを考えてみましょう。
利用者の利便性を考えるなら、はじめは会員登録せずにつかえるようにしたほうが喜ばれます。
そうして「いいな」と思ってもらった段階で会員登録させてより多くの機能を使ってもらうのです。

そんなアプリケーションで会員を表す型をつくってみましょう。

```elm
type UserType
    = GuestUser
    | RegularUser
```

先ほどと同じようにとりうる値を列挙しただけです。

さて、ユーザーにはユーザーIDなどの情報も存在します。
愚直にそれを実現するなら以下のような型エイリアスを用意するのではないでしょうか。

```elm
type alias User =
    { userType : UserType
    , userId : UserId
    }

type UserType
    = GuestUser
    | RegularUser

type alias UserId = String
```

型エイリアスについてはElm guideの[型エイリアス](https://guide.elm-lang.jp/types/type_aliases.html)の項目をご覧ください。

でもここで問題があります。ログインしていないユーザーはユーザーIDを持たないんです。
もちろん、苦肉の策としてログインしていない場合は`userId`フィールドの値を空文字列にするような運用も可能です。
でも以下のような「間違った」状態が存在しうることには変わりません。

```elm
okasinaUser : User
okasinaUser =
    { userType = GuestUser
    , userId = "ゲストなのになぜかIDがあるよ"
    }
```

ユーザーIDが存在しないはずの`GuestUser`（ログインしていない場合）なのに、データ構造上はIDを持つことができてしまうのです。
`Color`型の例でカスタム型と文字列型を比較して「カスタム型にすると、ありえない値を作れないようにできる」みたいなことを言っておきながらこの体たらくです。カスタム型をつかったのに、結局ありえない値をつくれちゃってるじゃん！💢🐐

ちょっと道端の草でも食べて落ち着いてください🌻 そういう需要を満たすために、実はカスタム型を使って以下のように定義することができます。

```elm
type User
    = GuestUser
    | RegularUser UserId

type alias UserId = String
```

`RegularUser`（ログイン済みの場合）の方に、なんか`UserId`型（実態は`String`型）の値がくっついてますね？
こうやって書くと、`User`型を定義するとともに、以下のような **関数** が自動的に定義されます。

```elm
GuestUser : User
GuestUser = (なんちゃらかんちゃら)

RegularUser : UserId -> User
RegularUser userId = (なんちゃらかんちゃら)
```

`GuestUser` **定数** （無引数の **関数** でもありました）は前述の`Color`型で見たのと同じ形式です。

一方で`RegularUser`の方は引数を1つ持つ **関数** になっています。
「上記のような型定義をすると、こういう関数が自動的に定義されるよ」という単なるお約束だと思ってください。
そしてここで自動的に定義された`RegularUser`という **関数** は、「引数で渡した`UserId`型の値を **内包した** `User`型の値を返す」という関数です。

さて**内包した** とはどういうことでしょうか？ 例えば以下のように`User`型の **値** を定義してみます。

```elm
someUser : User
someUser = RegularUser "Sakura-chan"
```

この`someUser`という **値** から、あとで`"Sakura-chan"`という値を取り出せるというのが、「内包した」の意味するところです。

# 内包した値を取り出してみよう

実際に取り出すには、`case`式を使います。`User`型の値を受け取って、そのユーザーのIDを画面表示用文字列に変換する関数を作ってみましょう。

```elm
displayUserId : User-> String
displayUserId user =
    case userId of
        GuestUser ->
            "ゲスト"

        RegularUser userId ->
            userId
```

こう定義すれば、以下のように使うことができます。

```elm
someUser1 : User
someUser1 = RegularUser "Sakura-chan"

someUser2 : GuestUser
someUser2 = GuestUser


displayUserId someUser1
--> "Sakura-chan"

displayUserId someUser2
--> "ゲスト"
```

これなら、「ログインしていないユーザーなのにIDを持っている」というありえない状態をつくれないように制限できますね🌷

# もっと情報を内包させてみよう

さて、ログイン中のユーザーはユーザーIDの他にも氏名などを登録しているかもしれません。
そういう場合にも、以下のようにすれば対応できます。

```elm
type User
    = GuestUser
    | RegularUser UserId Name

type alias UserId = String
type alias Name = String
```

こうやって書くと、`User`型を定義するとともに、以下のような **関数** が自動的に定義されます。

```elm
GuestUser : User
GuestUser = (なんちゃらかんちゃら)

RegularUser : UserId -> Name -> User
RegularUser userId name = (なんちゃらかんちゃら)
```

今度は、`User`型の値を受け取って、そのユーザーの氏名を画面表示用文字列に変換する関数を作ってみます。

```elm
displayUserName : UserType -> String
displayUserName user =
    case userId of
        GuestUser ->
            "ゲスト様"

        RegularUser userId name ->
            name
```

こう定義すれば、以下のように使うことができます。

```elm
someUser1 : UserType
someUser1 = RegularUser "Sakura-chan" "ヤギのさくらちゃん"

someUser2 : GuestUser
someUser2 = GuestUser


displayUserId someUser1
--> "ヤギのさくらちゃん"

displayUserId someUser2
--> "ゲスト様"
```

# もっともっと情報を内包させるには

同じように引数を増やしていけば、電話番号やツノの本数などの情報も内包させることができます。
でも、実用的には以下のような定義のしかたがオススメです。

```elm
type User
    = GuestUser
    | RegularUser Profile

type alias Profile =
    { userId : String
    , name : String
    , phone : String
    , horns : Int
    }
```

そう、[レコード](https://guide.elm-lang.jp/core_language.html#%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89)を内包させちゃえばいいのです。

# まとめ

カスタム型のポイントは、裏で内緒で **関数** が自動的に定義されていることに気づくことです。

もしこの説明でわからないところがあれば、Twitterで[さくらちゃん](https://twitter.com/arowM_)にメンションしてください。
Qiitaのコメント欄に質問などを書いていただいても見ないのでご注意ください。

では、よいElmライフを🌹


[さくらちゃんのツイッターをフォローする](https://twitter.com/arowM_)
[さくらちゃんが書いた他の記事を見る](https://qiita.com/arowM)
[さくらちゃんが翻訳したElmの本を手に入れる](https://amzn.to/3ssFWYu)
[さくらちゃんの写真集を手に入れる](https://amzn.to/38DyjWr)

![goodbye.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16345/3a47391b-4b77-72dc-5bfa-5764390ccf0b.jpeg)