web-dev-qa-db-ja.com

オプションを(最良の)トライに変換するにはどうすればよいですか?

メソッド呼び出しによって返された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は適切なものを提供しますか、または誰かがそれがどのように見えるかの例を与えることができますか?

20
Shadowlands

For-compを最初からTryで始める場合は、最後に一致を排除できます。これを行うには、Optionを介してTryfoldに強制します。これは次のようになります。

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つの異なるチェックに対して異なる例外を取得できます。それは完璧ではありませんが、多分それはあなたのために働くでしょう。

7
cmbaxter

暗黙の変換を使用できます

  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 を参照してください。

10
JasonG

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"))
}
3

短くてシンプル

Try(option.get)

派手なマッピングは必要ありません。オプションが空の場合、次のようなエラーが発生します。

Java.util.NoSuchElementException: None.get
2
Atais

[さまざまな障害を識別するために編集]

単純化しようとしました

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")))
}
1
pagoda_5b