2012年4月5日木曜日

Scala Tips / Either (16) - 二項演算, OR, Monoid

3月2日以来ですが、OFADが一段落したのでScala Tipsを再開します。

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

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

前回(3月2日)「Scala Tips / Either (15) - 二項演算, OR」は「Either:OR」×「値:任意の関数で計算」を考えました。

今回は、値に対する二項演算としてMonoidの|+|を考えます。

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

lhs/rhsともRight
Monoidの|+|
lhs/rhsともLeft
lhs側を使う

(分類の基準)

Java風

if式を使って、4つの場合を記述します。コンテキスト・バウンドでMonoid型の型パラメータTを定義して、Eitherの右側で使っています。

def f[T: Monoid](e1: Either[Throwable, T], e2: Either[Throwable, T]): Either[Throwable, T] = {
  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の二項演算
  }
}

2つのEitherの両方が右側だった時の動作は:

  • Right(e1.right.get |+| e2.right.get)

となります。「|+|」がMonoidの演算です。一般的に加法的な演算が行われます。

Scala風

match式を使って、4つの場合を記述します。

def f[T: Monoid](e1: Either[Throwable, T], e2: Either[Throwable, T]): Either[Throwable, T] = {
  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[T: Monoid](e1: Either[Throwable, T], e2: Either[Throwable, T]): Either[Throwable, T] = {
  (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」と同じです。)

ノート

Scala Tips / Either (13) - 二項演算, AND, Monoid」では、Java風、Scala風、Scalaの実装はJavaやScala標準にはMonoidがないためIntを使用していました。

Scala Tips / Either (15) - 二項演算, OR」で、検討した時からEitherに対するORをスマートに行う方法は進展がなく、Java風、Scala風のプログラムのみ取り上げます。そこで今回はJava風、Scala風のコーディングにScalazを併用してみました。

演算仕様

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 件のコメント:

コメントを投稿