メソッド呼び出しによって返されたOptionを(好みにより)Tryに変換するにはどうすればよいですか(Eitherまたはscalaz \/
または検証でもOKかもしれません)必要に応じて失敗値を指定することを含みますか?
たとえば、私は次のコードを持っています。これはぎこちなく感じますが、少なくとも(ほとんどの)仕事をします:
import scala.util._
case class ARef(value: String)
case class BRef(value: String)
case class A(ref: ARef, bRef: BRef)
class MismatchException(msg: String) extends RuntimeException(msg)
trait MyTry {
// Given:
val validBRefs: List[BRef]
// Want to go from an Option[A] (obtained, eg., via a function call passing a provided ARef)
// to a Try[BRef], where the b-ref needs to be checked against the above list of BRefs or fail:
def getValidBRefForReferencedA(aRef: ARef): Try[BRef] = {
val abRef = for {
a <- get[A](aRef) // Some function that returns an Option[A]
abRef = a.bRef
_ <- validBRefs.find(_ == abRef)
} yield (abRef)
abRef match {
case Some(bRef) => Success(bRef)
case None => Failure(new MismatchException("No B found matching A's B-ref"))
}
}
}
最終的な一致をマップまたはflatMapまたは同様の構成にモーフィングし、理解のために先行に組み込む方法があるはずだと感じています。
また、ARefからOption [A]を返す呼び出しが失敗した場合(Noneが返された場合)と、BRefチェックが失敗した場合(失敗の理由を1つだけ知っていることだけが気になります)で、別の失敗メッセージを指定できるようにしたいと思います。そのため、scalaz検証は理想的な適合とは思えません)。
これはモナド変換子を使用するのに適した場所ですか?もしそうなら、scalazは適切なものを提供しますか、または誰かがそれがどのように見えるかの例を与えることができますか?
For-compを最初からTry
で始める場合は、最後に一致を排除できます。これを行うには、Option
を介してTry
をfold
に強制します。これは次のようになります。
def getValidBRefForReferencedA(aRef: ARef): Try[BRef] = {
for {
a <- get[A](aRef).fold[Try[A]](Failure[A](new OtherException("Invalid aRef supplied")))(Success(_))
abRef = a.bRef
_ <- validBRefs.find(_ == abRef).fold[Try[BRef]](Failure(new MismatchException("No B found matching A's B-ref")))(Success(_))
} yield abRef
}
このアプローチでは、2つの異なるチェックに対して異なる例外を取得できます。それは完璧ではありませんが、多分それはあなたのために働くでしょう。
暗黙の変換を使用できます
implicit class OptionOps[A](opt: Option[A]) {
def toTry(msg: String): Try[A] = {
opt
.map(Success(_))
.getOrElse(Failure(new NoSuchElementException(msg)))
}
}
Scala標準ライブラリはこのタイプのアプローチを使用します。 http://docs.scala-lang.org/tutorials/FAQ/finding-implicits.html#companion-objects-of-a-type を参照してください。
Either
を使用する場合は、Option.toRight
を使用できます。
def getValidBRefForReferencedA(aRef: ARef): Either[Throwable,BRef] = {
for {
a <- get[A](aRef).toRight[Throwable](new Exception("Invalid ARef")).right
bRef <- validBRefs.find(_ == a.bRef).toRight(new MismatchException("No B found matching A's B-ref")).right
} yield bRef
}
Try
を使用すると、必要に応じて適切な例外をスローし、全体をTry.apply
でラップすることで、非常に手続き的な方法でコードを簡単に記述できます(例外をキャッチしてFailure
インスタンス)。
def getValidBRefForReferencedA(aRef: ARef): Try[BRef] = Try {
val a = get[A](aRef).getOrElse(throw new Exception("Invalid ARef"))
validBRefs.find(_ == a.bRef).getOrElse(throw new MismatchException("No B found matching A's B-ref"))
}
Try(option.get)
派手なマッピングは必要ありません。オプションが空の場合、次のようなエラーが発生します。
Java.util.NoSuchElementException: None.get
[さまざまな障害を識別するために編集]
単純化しようとしました
def getValidBRefForReferencedA(aRef: ARef): Try[BRef] = {
val abRef = for {
a <- get[A](aRef)
bRef = a.bRef
result = Either.cond(validBRefs.contains(bRef), bRef, "Invalid B Reference")
} yield result
abRef.map {
case Right(bRef) => Success(bRef)
case Left(error) => Failure(new InvalidReferenceException(error))
}.getOrElse(Failure(new MismatchException("No B found matching A's B-ref")))
}