2012年2月27日月曜日

Scala Tips / Either (11) - Applicative

Either (10) - 二項演算, AND」でApplicative Functor(以下Applicative)という型クラスを使いました。見慣れない演算子「|@|」が使われていますが、これがScalazが提供するApplicativeの演算子です。

def f(e1: Either[Throwable, Int], e2: Either[Throwable, Int], f: (Int, Int) => Int): Either[Throwable, Int] = {
  (e1 |@| e2)(f)
}

e1とe2が共にRightの場合、関数fの第1引数にe1(Right)の値、第2引数e2(Right)の値を適用して評価し、その結果をRightに詰めて返すという動作をします。

逆にいうと、e1とe2のどちらかがLeftの場合、結果もLeftになります。つまり、Eitherを成功/失敗文脈として、その上でEitherの持つ値に対して関数fを適用しています。

「|@|」が引数の区切り記号のようなイメージで使われていて、動作のイメージとしては「f(e1, e2)」となります。ただし、失敗の文脈の処理は文脈の中で自動的に行ってくれるわけです。

このように文脈上の処理(Eihterの場合は失敗文脈の引き継ぎ)を自動的に行なってくれる点が、「f(e1, e2)」という形で関数を生で使うのではなく、「(e1|@|e2)(f)」という形でApplicativeを使うメリットです。

Functor, Applicative, Monad

ScalazではFunctorとMonadの間に、Functorより強力で、Monadよりシンプルな型クラスApplicativeを導入しています。型クラスFunctorのサブ型クラスがApplicative、Applicativeのサブ型クラスがMonadという関係になっています。

以下では、Functor, Applicative, Monadの機能比較と使い分けについて簡単に説明します。説明のために、必要に応じてFunctor、Applicative、Monadの事をコンテナと呼びます。それぞれ「Functorのコンテナ」、「Applicativeのコンテナ」、「Monadのコンテナ」というぐらいの意味をイメージしながら読み進めてください。

Functor

Functorは、引数が1つの関数をコンテナに適用することができますが、複数のコンテナに対して複数の引数を持つ関数を適用することはできません。一方、Functorの合成は簡単に行うことができます。

Monad

Monadは、複数のコンテナに対して複数の引数を持つ関数を適用することができます。一方、Monadの合成はできないことはありませんが、モナド変換子やクライスリ圏上での射を導入する必要があり、かなり難易度が上がります。

Applicative

Functorは合成は簡単にできるものの複数のコンテナに対して複数の引数を持つ関数を適用することができません。一方、Monadは複数のコンテナに対して複数の引数を持つ関数を適用することはできすが、合成を行うのがなかなか面倒です。

そこで、登場するのがApplicativeです。Applicativeは、複数のコンテナに対して複数の引数を持つ関数を簡単に適用することができるのが特徴です。

前述の以下の使い方は、e1(Either)とe2(Either)という2つのコンテナに対して、引数2の関数fを適用していますが、まさにApplicativeの典型的な使用方法です。

(e1 |@| e2)(f)

またApplicativeの合成は比較的簡単に行うことができます。これはMonadに対する優位点です。

ただし、Applicativeでは、文脈の切替えを行うことができません。文脈の切替えが必要な場合はMonadを使う必要があります。

比較

Functor, Applicative, Monadの性質を比較すると以下のものになります。

型クラス複数引数関数×複数コンテナ合成文脈の切替
Functor
Applicative
Monad

使い分けですが、以下の方針がよいでしょう。

まず、文脈の切替が必要な場合はMonadを選択するしかありません。また、1引数関数を1コンテナに適用する場合は、簡潔に記述できるFunctorがよいでしょう。

問題は「複数引数関数×複数コンテナ」の場合です。

まず、記述方法の簡潔度ですが、ケースバイケースといえます。Monadの場合flatMapメソッドや>>=メソッドを使った記述はちょっと煩雑ですが、(Monad演算の文法糖衣である)for式が適用できる場合には簡潔に記述することができます。ただし、Applicativeの「|@|」の記法がぴったりはまるケースはより簡潔に記述することができます。このあたりは好みの問題もあるので、好きな方を選べばよいでしょう。

残るのはコンテナの合成ですが、コンテナの合成が必要、あるいは含みを持たせたい場合には、Applicativeにしておくのが有利です。つまり、文脈の切替がないケースで、コンテナの合成がある/ありそうな場合にApplicativeを選択することになります。

諸元

  • Scala 2.9.1
  • Scalaz 6.0.3

0 件のコメント:

コメントを投稿