2012年4月6日金曜日

Scala Tips / Either (17) - 二項演算, OR, Plus

Rightを成功とする、成功/失敗文脈におけるEitherに対する二項演算です。(Scala Tips / Either (9) - 二項演算)

ここまで「Either:AND」について「Either:AND」×「値:任意の関数で計算」「Either:AND」×「値:Monoid」「Either:AND」×「値:Plus」をみてきました。

また「Either:OR」について「Either:OR」×「値:任意の関数で計算」「Either:OR」×「値:Monoid」を見てきました。

今回は、値に対する二項演算としてPlusの<+>を考えます。Plusは「Scala Tips / Either (14) - 二項演算, AND, Plus」で説明したとおりコンテナの連結を行う型クラスと思われます。

値に対する二項演算は以下の組合せとします。

lhs/rhsともRight
Plusの<+>で計算
lhs/rhsともLeft
lhs側を使う

(分類の基準)

Java風

if式を使って、4つの場合を記述します。「Rightの二項演算」の所で型クラスPlusの演算子<+>を用いて型クラスPlusの型クラスインスタンスを持つオブジェクト、つまり連結機能を持つコンテナオブジェクトの連結を行っています。

def f[M[_]: Plus, A](e1: Either[Throwable, M[A]], e2: Either[Throwable, M[A]]): Either[Throwable, M[A]] = {
  if (e1.isRight && e2.isRight) {
    Right(e1.right.get <+> e2.right.get) // Rightの二項計算
  } else if (e1.isRight && e2.isLeft) {
    e1
  } else if (e1.isLeft && e2.isRight) {
    e2
  } else { // e1.is Left && e2.isLeft
    e1 // Leftの二項演算
  }
}

Scala風

match式を使って、4つの場合を記述します。「Rightの二項演算」の所で型クラスPlusの演算子<+>を用いてコンテナオブジェクトの連結を行っています。

def f[M[_]: Plus, A](e1: Either[Throwable, M[A]], e2: Either[Throwable, M[A]]): Either[Throwable, M[A]] = {
  e1 match {
    case Right(e1r) => e2 match {
      case Right(e2r) => Right(e1r <+> e2r) // Rightの二項計算
      case Left(_) => e1
    }
    case Left(_) => e2 match {
      case Right(_) => e2
      case Left(_) => e1 // Leftの二項演算
    }
  }
}

match式のネストが気に入らない場合は以下のようにすればネストしない方式で記述することもできます。

def f[M[_]: Plus, A](e1: Either[Throwable, M[A]], e2: Either[Throwable, M[A]]): Either[Throwable, M[A]] = {
  (e1, e2) match {
    case (Right(e1r), Right(e2r)) => Right(e1r <+> e2r) // Rightの二項計算
    case (Right(_), Left(_)) => e1
    case (Left(_), Right(_)) => e2
    case (Left(_), Left(_)) => e1 // Leftの二項計算
  }
}

後者(Tuple方式)は、Tupleを導入しているのとパターンマッチングの回数が増えるので性能的には不利ですが、プログラムの見通しはよくなります。フレームワークで使う場合には性能重視で前者(ネスト方式)、アプリケーションで使う場合には可読性重視で後者(Tuple方式)という選択も考えられます。

Scala

Eitherに対するORを行うScalaらしい関数合成、Monadic演算による方式を見つけることができませんでした。Scala風で説明した方法を用いることになります。(「Scala Tips / Either (15) - 二項演算, OR」と同じです。)

Scalaz

Scalazでも、Eitherに対するORを行うScalaらしい関数合成、Monadic演算による方式を見つけることができませんでした。Scala風で説明した方法を用いることになります。

(「Scala Tips / Either (15) - 二項演算, OR」と同じです。)

ノート

型クラスPlusについては、型クラスMonoidとの違い、コンテキスト・バウンドでの指定方法という話題があります。詳しくは「Scala Tips / Either (14) - 二項演算, AND, Plus」のノートを参照してください。

演算仕様

EitherのOR

EitherのORは以下の演算になります。

EitherのOR
lhsrhs結果Rightの値Leftの値
RightRightRight二項演算-
RightLeftRightlhs-
LeftRightRightrhs-
LeftLeftLeft-二項演算

lhsとrhsの両方がLeft(失敗)でない場合は、Right(成功)となります。

値に対する二項演算

値に対する二項演算は、lhs/rhsともRightだった場合と、Leftだった場合があります。

値に対する二項演算は、以下のものが考えられます。

lhs
lhs側を使う
rhs
rhs側を使う
  • f(lhs, rhs) :: 任意の関数で計算
  • lhs |+| rhs :: Monoidで計算
  • lhs <+> rhs :: Plusで計算

諸元

  • Scala 2.9.1
  • Scalaz 6.0.3

0 件のコメント:

コメントを投稿