Как я могу (лучше всего) конвертировать вариант в Try?

Как я могу (лучше всего) преобразовать Опцию, возвращенную вызовом метода, в Try (по предпочтению, хотя Либо или сказус \/, или даже Проверка может быть ОК), включая указание значения отказа, если это необходимо?

Например, у меня есть следующий код, который чувствует себя kludgy, но, по крайней мере, выполняет (большую часть) работу:

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 B-ref"))
    }
  }
}

Похоже, что должен быть способ окончательного совпадения превращаться в карту или flatMap или аналогичную конструкцию и включаться в предысторию для понимания.

Кроме того, я предпочел бы иметь возможность указывать другое сообщение об ошибке, если вызов возвратить опцию [A] из ARef не удалось (возвращается None) по сравнению с ошибкой BRef (я забочусь только об одной причине для отказ, поэтому проверка достоверности скаляз не похожа на идеальную подгонку).

Это подходящее место для использования монадного трансформатора? Если да, то делает ли scalaz подходящий вариант, или кто-нибудь может дать пример того, как он будет выглядеть?

Ответ 1

Если вы начинаете с Try от get go with your for-comp, тогда вы можете устранить совпадение в конце. Вы можете сделать это, нажимая 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 B-ref")))(Success(_))
  } yield abRef
}

При таком подходе вы можете получить разные исключения для двух разных проверок. Это не идеально, но, возможно, это сработает для вас.

Ответ 2

Если вы хотите использовать 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 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 B-ref"))
}

Ответ 4

[Отредактировано для выявления различных сбоев]

Пробовал упростить

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 B-ref")))
}

Ответ 5

Я разработал альтернативное решение, хотя он по-прежнему не позволяет мне указывать другое сообщение об ошибке для случая, когда Option [A] имеет значение None и BREF недействителен:

def getValidBRefForReferencedA(aRef: ARef): Try[BRef] = 
  Try {
    (for {
      a <- get[A](aRef)
      bRef = a.bRef
      _ <- bs.find(_ == bRef)
    } yield (bRef)) getOrElse (throw new MismatchException("No B found matching A B-ref"))
  }

Я предполагаю, что я ожидаю, что у вас будет возможность быстро преобразовать возвращенный вариант [A] в Try (в подходящем идиоматическом Scala способе, например внутри внутри-понимания), а затем продолжить (получение и проверка b-ref) при установке любых соответствующих сбоев на пути.