以下のような型シグネチャを持つ関数を考えてみましょう:
indexOf : String -> List String -> Intここで仮定した関数は、文字列と文字列のリストを取り、指定された文字列がリスト内で見つかった場合はインデックスを、見つからない場合は-1を返します。 もちろん、文字列のリストではなく整数リストに対しては、この関数を使用することはできません。
しかし、特定の型の代わりに__型変数__または__スタンドイン__を使用することによって、この関数を__ジェネリック__にすることができます。
indexOf : a -> List a -> IntStringをaで置き換えることにより、 今やindexOfのシグネチャは「任意の型aの値と同じ型aのリストをとり、整数を返す」というものになります。型が一致する限り、コンパイラは満足します。indexOfは今や、引数「StringとStringのリスト」でも「IntとIntのリスト」でも呼び出すことができ、期待するように動作します。
この方法で関数をよりジェネリックにできます。複数の__型変数__を持つこともできます:
switch : ( a, b ) -> ( b, a )
switch ( x, y ) =
( y, x )この関数は(a,b)型のタプルをとり、(b,a)型のタプルを返します。次はすべて有効な呼び出しです。
switch (1, 2)
switch ("A", 2)
switch (1, ["B"])型変数には任意の小文字の識別子を使用でき、 aとbのように1文字にするのは慣習にすぎません。たとえば、次のシグネチャは完全に有効です。
indexOf : thing -> List thing -> Int
次のようなシグネチャを考えてみましょう:
map : (Int -> String) -> List Int -> List Stringこの関数は:
- 引数として関数「
(Int -> String)」と整数のリストをとり、 - そして文字列のリストを返します。
興味深いのは(Int -> String)の部分です。これは、引数の関数が (Int -> String)シグネチャに従わなければならないことを示しています。
例えば、coreにある toStringはそのような関数です。したがって、この map関数を以下のように呼び出すことができます:
map toString [1, 2, 3]しかし、 IntとStringは特殊すぎます。代りに型変数を使ったシグネチャを良く見るでしょう。
map : (a -> b) -> List a -> List bこの関数は aのリストをbのリストにマップします。aとbが実際にどのような型であるかは、最初の引数に与えられた関数が同じ型を使用している限り気にしません。
たとえば、次のシグネチャを持つ関数があるとき、
convertStringToInt : String -> Int
convertIntToString : Int -> String
convertBoolToInt : Bool -> Intジェネリックなmapは次のように呼び出すことができます:
map convertStringToInt ["Hello", "1"]
map convertIntToString [1, 2]
map convertBoolToInt [True, False]