関数

【Haskell】カリー化と部分適用

【Haskell】カリー化と部分適用

Haskellにおいて高階関数を理解するために重要となる「カリー化(Currying)」と「部分適用 (Partial Application)」について解説します。

カリー化と部分適用

高階関数とは、引数に関数を受けとったり、返り値として関数を返却できる関数のことを言います。高階関数は、Haskellのような関数型プログラミング言語では必要不可欠なものとなっています。

高階関数について、理解するために覚えておくべき内容として「カリー化(Currying)」と「部分適用 (Partial Application)」という考え方があります。

この記事では、カリー化と部分適用について説明します。

Haskellの関数の基本については「関数の基本」にまとめているので参考にしてください。

カリー化 (currying)

Haskellは、純粋関数型のプログラミング言語であるため、実装の中心は関数となります。Haskellの関数の基本は「関数の基本」でまとめているので参考にしてください。

この記事で、伝えたいHaskellの関数における重要なポイントの1つは以下になります。

Haskellのすべての関数は、引数を1つだけ取る

しかし、皆さんが関数を作成する際には、複数の引数をとる関数を作成してきたと思います。これはどういうことでしょうか。実際にはHaskellの関数は「カリー化 (Currying)」された関数だったのです。カリー化について理解できると上記の引数を1つだけとるの意味を理解することができます。

簡単な例を使って理解していきましょう。例として以下のような2つの整数を加算するような簡単な関数を考えてみましょう。

add :: Int -> Int -> Int
add x y = x + y

次の2つの呼び出し方は、結果としては同じになります。

ghci> add 1 2
3
ghci> (add 1) 2
3

addの型シグネチャは、「Int -> Int -> Int」ですが、これは「Int -> (Int -> Int)」と書くことができます。後者の記載では「Int」を1つ引数にとって、「(Int -> Int)」つまりは Intを受け取ってIntを返す関数を返却すると読むことができます。

つまり、「add 1 2」というのは、実際には2つのステップを踏みます。

  1. add 1が評価されて1を加算するような関数が返却されます。この関数は引数を1つ受け取る関数です。
  2. ステップ1で返却された関数の引数に2を適用して、結果の3を返却する。

これは引数が3つ、4つ、…と増えていっても同じです。1つ引数が適用されて関数が返却され、次の引数が適用されてまた関数が返却されるというのが繰り返されます。このような関数をカリー化された関数といいます。

「Haskellのすべての関数は、引数を1つだけ取る」の意味はご理解いただけたでしょうか。では、これは何がうれしいのでしょうか。それが以降で説明する部分適用になります。

部分適用 (Partial Application)

部分適用 (Partial Application)は、関数にその関数がとる引数の一部だけを提供し、残りの引数を受け取るような新しい関数を生成することを言います。

先ほども見てきたadd関数を使って、部分適用の例を見てみましょう。

add :: Int -> Int -> Int
add x y = x + y

-- 2を足し算する関数
add2 = add 2

-- 3を足し算する関数
add3 = add 3
【実行結果例】
ghci> add2 3
5
ghci> add3 3
6

上記の例では、add関数の引数xのみ値を渡して新しい関数を作成しています。先ほどカリー化でも見てきたように1つの引数だけ与えた場合には、返却値は関数となっているので、その関数に新しく名前(add2add3)を付けて使うことができます。

部分適用の利点は、既存の関数の再利用性とモジュール性を高めることができる点にあります。

Note

関数の引数は最も汎用的なものから順に並べるというのがベストプラクティスです。これは部分適用によりモジュールの再利用性を高めやすくなるためです。

例えば、HTTP通信のリクエスト組み立てるような関数を考えた時にホスト名は最も汎用的といえます。このような引数を左側に持ってきておくと、ホスト名を指定した関数を作っておいて、後続のパラメータのみ変動させて使うようなこともできるようになります。

カリー化と部分適用の違い

上記で「カリー化」と「部分適用」について説明してきましたが、これらはよく混同されがちです。しかし、これらは明確に異なる概念です。

カリー化は、複数の引数をとる関数を引数を1つだけ取る関数の連鎖に変換するプロセスのこと自体を指します。一方で、部分適用は、複数の引数をとる関数の一部に引数を渡して新しい関数を生成することです。

これらは、明確に異なった概念ですので区別して理解してください。

まとめ

Haskellにおいて高階関数を理解するために重要となる「カリー化(Currying)」と「部分適用 (Partial Application)」について解説しました。

カリー化は、複数の引数をとる関数を、引数を1つだけ取る関数の連鎖に変換するプロセスのことです。部分適用は、複数の引数をとる関数の一部に引数を渡して新しい関数を生成することでカリー化と関連しています。

この記事では、カリー化と部分適用について、簡単な例を使いながら紹介しました。これらの概念は、理解していなくてもプログラムすることはできるのですが、純粋関数型プログラミング言語であるHaskellの理解を深めるのに非常に重要なので、ぜひ理解していただきたいと思います。