ジェネリクスは、コードの再利用を可能にする言語の機能です。
たとえば、配列の長さを計算するのに、その配列がどんな型を持つかは関係ないはずです。
だからこそ、length関数は型シグネチャの中でジェネリックな型パラメーターaを使っています。
length : List a -> Int
ある型が別の型に対してジェネリックであるとき、その別の型には小文字の名前を使い、それを型パラメーターと呼びます。
よくある例は、List、Maybe、Resultというカスタム型です。
-- Maybe is generic over the type a
type Maybe a
= Nothing
| Just a
someString : Maybe String
someString = Just "hello"
someInt : Maybe Int
someInt = Just 42
型パラメーターの名前について大切なのは、複数の文字を使えるということです。 唯一の制約は、小文字の名前でなければならないことです。 そのため、役に立つときには、説明的な名前を付けることがよくあります。
型は複数の別の型に対してジェネリックになれるので、複数の型パラメーターを持つこともできます。 その場合、それぞれ独立した型に違う小文字の名前を付けます。 独立しているからといって、同じ型になれないわけではありません。ただ、違う型になれるようになるだけです。
-- Result is generic over the types error and value
type Result error value
= Ok value
| Err error
レコードの型エイリアスも型パラメーターを持てますが、あまり一般的ではありません。
-- Event is generic over the type a
type alias Event a =
{ at : Time
, data : a
}
型がジェネリックになれるのですから、関数も同様です。
ジェネリックな型を扱う関数を書くこともできます。たとえばMaybe.withDefaultです。
withDefault : a -> Maybe a -> a
withDefault default maybe =
case maybe of
Just value -> value
Nothing -> default
他の関数を仮引数として受け取る関数は高階関数と呼ばれ、これらもジェネリックになれます。
map : (a -> b) -> Maybe a -> Maybe b
map function maybe =
case maybe of
Just value ->
Just (function value)
Nothing ->
Nothing
この演習では、宝物をしまっておくための、ジェネリックな(しかも魔法のような!)TreasureChestを書いていきます。
TreasureChestを定義する単一のバリアントを持つ、カスタム型TreasureChestを定義します。
このバリアントには、関連付けられたStringの値を持たせます。この値は、宝箱のパスワードを保存するために使います。
このバリアントには、関連付けられたジェネリックな値も持たせます。この値は宝物を保存するために使います。 宝物はどんなものでもかまわないように、この値はジェネリックになっています。
getTreasure関数を定義するこの関数は2つの入力を受け取ります。
String(パスワードを試すためのもの)TreasureChest
この関数は、渡されたパスワードの試行を、TreasureChestの中のStringと照らし合わせます。
この関数はMaybeを返します。
パスワードが一致したら、TreasureChestから取り出したジェネリックな値(宝物です!)を持つJustを返します。
パスワードが一致しなければ、Nothingを返します。
multiplyTreasure関数を定義するこの(高階)関数は2つの入力を受け取ります。
TreasureChest
この関数は、宝箱の中の宝物を渡して、掛け算用の関数を呼び出します。
この関数は、元と同じパスワードを持ち、掛け算された宝物(戻り値)を持つ、新しいTreasureChestを返します。