関数についてもっと

型変数

以下のような型シグネチャを持つ関数を考えてみましょう:

indexOf : String -> List String -> Int

ここで仮定した関数は、文字列と文字列のリストを取り、指定された文字列がリスト内で見つかった場合はインデックスを、見つからない場合は-1を返します。 もちろん、文字列のリストではなく整数リストに対しては、この関数を使用することはできません。

しかし、特定の型の代わりに型変数またはスタンドインを使用することによって、この関数をジェネリックにすることができます。

indexOf : a -> List a -> Int

Stringaで置き換えることにより、 今やindexOfのシグネチャは「任意の型aの値と同じ型aのリストをとり、整数を返す」というものになります。型が一致する限り、コンパイラは満足します。indexOfは今や、引数「StringStringのリスト」でも「IntIntのリスト」でも呼び出すことができ、期待するように動作します。

この方法で関数をよりジェネリックにできます。複数の型変数を持つこともできます:

switch : ( a, b ) -> ( b, a )
switch ( x, y ) =
  ( y, x )

この関数は(a,b)型のタプルをとり、(b,a)型のタプルを返します。次はすべて有効な呼び出しです。

switch (1, 2)
switch ("A", 2)
switch (1, ["B"])

型変数には任意の小文字の識別子を使用でき、 abのように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]

しかし、 IntStringは特殊すぎます。代りに型変数を使ったシグネチャを良く見るでしょう。

map : (a -> b) -> List a -> List b

この関数は aのリストをbのリストにマップします。abが実際にどのような型であるかは、最初の引数に与えられた関数が同じ型を使用している限り気にしません。

たとえば、次のシグネチャを持つ関数があるとき、

convertStringToInt : String -> Int
convertIntToString : Int -> String
convertBoolToInt : Bool -> Int

ジェネリックなmapは次のように呼び出すことができます:

map convertStringToInt ["Hello", "1"]
map convertIntToString [1, 2]
map convertBoolToInt [True, False]

results matching ""

    No results matching ""