部分適用の使いどころ

部分適用というのは、簡単に言ってしまえば、関数の型を変換する方法の1つです。

たとえば、Func を Func に変換するとか。

部分適用という言葉を使わずとも、これはよく行われていることで、ラムダ式を使って実現されます。

わかりやすいように、型を明示してサンプルコード書いてみます。

        [TestMethod]
        public void TestLambda()
        {
            Func<int, int, int> add = (x, y) => x + y;
            int a = 100;

            // ラムダ式で Func<int, int, int> から Func<int, int> に型を変換
            Func<int, int> selector = y => add(a, y);

            IEnumerable<int> result = Enumerable.Range(0, 10).Select(selector);
            Assert.IsTrue(result.SequenceEqual(Enumerable.Range(100, 10)));
        }

自分は、これを、関数をラムダ式でwrapしてadaptするみたいに表現します。


上記のコードを部分適用を使って書くとこんな感じになります(前のエントリで定義した拡張メソッドPartialを使います)。

        [TestMethod]
        public void TestPartialApplication()
        {
            Func<int, int, int> add = (x, y) => x + y;
            int a = 100;

            // 部分適用で Func<int, int, int> から Func<int, int> へ型を変換
            Func<int, int> selector = add.Partial(a);

            IEnumerable<int> result = Enumerable.Range(0, 10).Select(selector);
            Assert.IsTrue(result.SequenceEqual(Enumerable.Range(100, 10)));
        }

見た目的には、「y => add(a, y)」が「add.Partial(a)」に変わっただけだったりします。

大して変わらないといえば、そのとおりです。

強いてポイントを挙げるならば、ラムダ式にとって変数「a」は自由変数ですがが、部分適用においては、変数aが引数として扱われるということですね。部分変数のほうが変数の共有によるバグを避けられるといえるかもしれない。それから、部分適用のほうがスコープの入れ子がない分だけ単純です。

もちろん、部分適用より、ラムダ式による型変換のほうが柔軟です。なんでもできる。部分適用は、Func を Func に変換するようなことはできないですが、ラムダ式を使えばなんでもできます。

まとめ

部分適用は関数の型変換における単純ケースの1つ。なので、そういう型変換が適切な場合に使える、と考えるのがわかりやすいです。

ちなみ、C#で部分適用を使ったほうがいいかはどうかは微妙です。やっぱり、言語の構文に組み込まれていてシンプルに使える場合に効果を発揮しやすいんじゃないかなと思います(F#のようにね!)。