代数的データ型(ADT)は、決まった数の名前付きケースを表します。 ADTの値は、名前の付いたケースのうち、ちょうど1つに対応します。
ADTはdataキーワードで定義し、各ケースはパイプ(|)で区切ります。
どのケースにもデータが結び付いていない場合、そのADTは、他の言語で一般に_列挙型_(enum)と呼ばれているものに似ています。
data Season
= Spring
| Summer
| Autumn
| Winter
ADTの各ケースには、データを結び付けることもできます。また、ケースごとに違う型のデータを持たせることもできます。 データを結び付けるケースには、コンストラクターが必要です。
data Number
= NInt Int --'NInt' is the constructor for an Int Number.
| NFloat Float --'NFloat' is the constructor for an Float Number.
| Invalid --'Invalid' does not have data associated to it.
特定のケースの値を作るには、そのケースの名前を参照します(例:NInt 22)。
ケース名は単なるコンストラクター関数なので、結び付けるデータは通常の関数の引数として渡せます。
ADTには_構造的等価性_があり、これは、同じケースで同じ(省略可能な)データを持つ2つの値が等しいとみなされることを意味します。
if/else式でADTを扱うこともできますが、おすすめの方法は、_case_文を使ったパターンマッチングです。
add1 :: Number -> String
add1 number =
case number of
NInt i -> show (i + 1)
NFloat f -> show (f + 1.0)
Invalid -> error "Invalid input"
この演習では、バレンタインデーにパートナーと何をするかを計画します。パートナーはたくさんのアイデアを出していて、一番よい活動を見つけるために、そのアイデアを評価してほしいと頼んできています。
パートナーが提案してきたアイデアは次のとおりです:
バレンタインデーの過ごし方を選ぶための、6つのタスクがあります。
パートナーが提案するそれぞれのアイデアに対して、はい・いいえ・たぶんの3つのうちのどれかで答えます。
これらの選択肢を表すために、次の3つのケースを持つ代数的データ型Approvalを定義します:Yes、No、Maybe。
パートナーは2つのレストラン候補を選びました。1つは韓国料理、もう1つはトルコ料理です。
これらのレストランを表すために、次の2つのケースを持つ代数的データ型Cuisineを定義します:Korean、Turkish。
選べる映画は山ほどあるので、絞り込むために、パートナーは好みのジャンルも挙げています。
次のジャンルのケースを表すために、代数的データ型Genreを定義します:Crime、Horror、Romance、Thriller。
すでに触れたとおり、パートナーはボードゲームで遊ぶ、のんびり過ごす、映画を見る、レストランに行く、散歩するという5つの活動を思いつきました。
これらの活動の種類を表すために、代数的データ型Activityを定義します:
BoardGame:関連するデータはありません。Chill:関連するデータはありません。Movie:関連するデータとしてGenreを持ちます。Restaurant:関連するデータとしてCuisineを持ちます。Walk:歩く距離をキロメートルで表すIntを関連するデータとして持ちます。いよいよ、パートナーのアイデアを評価する準備ができました。 パートナーのアイデアに対するあなたの気持ちは次のとおりです:
Activityの値を受け取り、上記の気持ちに基づいてApprovalを返すrateActivityという名前の関数を実装します。
例えば:
rateActivity (Restaurant Turkish)
-- -> Maybe