Rightを成功とする、成功/失敗文脈におけるEitherに対する二項演算です。(Scala Tips / Either (9) - 二項演算)
前回までは、「Either:AND」について「Either:AND」×「値:任意の関数で計算」、「Either:AND」×「値:Monoid」、「Either:AND」×「値:Plus」についてみてきました。
今回は、「Either:OR」×「値:任意の関数で計算」を考えます。
EitherのORは以下の演算になります。
lhs | rhs | 結果 | Rightの値 | Leftの値 |
---|---|---|---|---|
Right | Right | Right | 二項演算 | - |
Right | Left | Right | lhs | - |
Left | Right | Right | rhs | - |
Left | Left | Left | - | 二項演算 |
lhsとrhsの両方がLeft(失敗)でない場合は、Right(成功)となります。
値に対する二項演算は、lhs/rhsともRightだった場合と、Leftだった場合があります。
値に対する二項演算は、以下のものが考えられます。
- lhs
- lhs側を使う
- rhs
- rhs側を使う
- f(lhs, rhs) :: 任意の関数で計算
- lhs |+| rhs :: Monoidで計算
- lhs <+> rhs :: Plusで計算
値に対する二項演算は以下の組合せとします。
- lhs/rhsともRight
- 任意の関数で計算
- lhs/rhsともLeft
- lhs側を使う
(分類の基準)
Java風
if式を使って、4つの場合を記述します。
def f(e1: Either[Throwable, Int], e2: Either[Throwable, Int], f: (Int, Int) => Int): Either[Throwable, Int] = { if (e1.isRight && e2.isRight) { Right(f(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つの場合を記述します。
def f(e1: Either[Throwable, Int], e2: Either[Throwable, Int], f: (Int, Int) => Int): Either[Throwable, Int] = { e1 match { case Right(e1r) => e2 match { case Right(e2r) => Right(f(e1r, e2r)) // Rightの二項計算 case Left(_) => e1 } case Left(_) => e2 match { case Right(_) => e2 case Left(_) => e1 // Leftの二項演算 } } }
match式のネストが気に入らない場合は以下のようにすればネストしない方式で記述することもできます。
def f(e1: Either[Throwable, Int], e2: Either[Throwable, Int], f: (Int, Int) => Int): Either[Throwable, Int] = { (e1, e2) match { case (Right(e1r), Right(e2r)) => Right(f(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風で説明した方法を用いることになります。
Scalaz
Scalazでも、Eitherに対するORを行うScalaらしい関数合成、Monadic演算による方式を見つけることができませんでした。Scala風で説明した方法を用いることになります。
ノート
Scala Tips / Either (10) - 二項演算, AND、Scala Tips / Either (13) - 二項演算, AND, Monoid、Scala Tips / Either (14) - 二項演算, AND, Plusの3つはflatMapを使った関数合成やApplicative Functorの利用がぴったりとはまりました。
一方、今回はEitherに対するORを行おうとしたわけですが、ぴったりはまるよい方法を見つけることができませんでした。
scalaz.Traverseあたりを使うと何とかなりそうな気もしていたのですが、TraverseもflatMapと同様にANDのセマンティクスのようです。Optionに変換する方法はLeftの情報がなくなってしまうのでダメです。
Eitherに対するOR演算は、今後の研究課題にしたいと思います。
諸元
- Scala 2.9.1
- Scalaz 6.0.3
このコメントは投稿者によって削除されました。
返信削除https://twitter.com/#!/halcat0x15a/status/175973874968694784
返信削除https://twitter.com/#!/halcat0x15a/status/175974439589122048
この方法ではうまくいかないようです。
返信削除