2013-07-08 13 views
12

W jaki sposób mogę (najlepiej) przekonwertować opcję zwróconą przez wywołanie metody na próbę (według preferencji, chociaż może to być poprawne albo scalanie \/ lub nawet sprawdzanie poprawności), w tym określanie wartości błędu, jeśli jest to właściwe?Jak mogę (najlepiej) przekonwertować opcję na próbę?

Na przykład, Mam następujący kod, który czuje kludgy, ale ma przynajmniej zrobić (większość) zadanie:

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

Czuje się jak nie powinno być sposobem na mecz finałowy być przekształcił się w mapę lub płaską mapę lub podobną konstrukcję i włączony do poprzedniej dla zrozumienia.

Ponadto, wolałbym móc określić inny komunikat o błędzie, jeśli nie powiodło się wywołanie zwracania Opcji [A] z ARef (zwrócono Brak) w porównaniu do niepowodzenia sprawdzania BRef (zależy mi tylko na poznaniu jednego powodu na awarię, więc walidacja nie wydaje się idealnym rozwiązaniem).

Czy to odpowiednie miejsce do korzystania z transformatora Monada? Jeśli tak, to czy skalak dostarcza odpowiedni, czy może ktoś da przykład tego, jak by wyglądał?

+0

Czy masz na myśli coś takiego jak 'try {abRef.getOrElse (throw new MismatchException ("No B okaże się pasować do B-ref")) lub' '} {abRef.map Success (_)} .getOrElse (awaria (nowy wyjątek MismatchException ("Nie znaleziono B pasującego do B-ref A")))? – senia

+0

@senia więcej tego ostatniego: 'abRef.map {Success (_)} .getOrElse (Niepowodzenie (nowy wyjątek MismatchException (" Nie znaleziono B pasującego do A-B "))), ale wydaje się, że powinno być możliwe - i bardziej idiomatyczne - w jakiś sposób zbudować to w zrozumieniu. – Shadowlands

+0

Na marginesie, dlaczego nie użyć 'filter' na' Option' - tj. Zastąpić 'for' -rozumienie za pomocą' get [A] (aRef) .map (_. BRef) .filter (validBRefs.contains) ' ? –

Odpowiedz

5

Jeśli zaczynasz z Try od uzyskania go z telefonem za-Comp następnie można wyeliminować mecz na końcu. Możesz to zrobić, wymuszając Option na Try poprzez . Oto, co to może wyglądać tak:

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 
} 

Dzięki takiemu podejściu, można uzyskać różne wyjątki dla dwóch różnych kontroli. Nie jest doskonały, ale może zadziała dla ciebie.

+0

Ach, to było to, czego oczekiwałem, powinno być możliwe - przegapiłem metodę składania (chociaż wydaje się, że tylko przyszła ze Scala 2.10.x). Pozdrawiam @cmbaxter! – Shadowlands

-1

ja wypracowaliśmy rozwiązanie alternatywne, mimo że nadal nie pozwala mi określić inny komunikat o awarii dla przypadku, gdy opcja [A] jest niczym w porównaniu BREF nie jest ważny:

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

Sądzę, że spodziewam się, że powinien być możliwy sposób szybkiego przekonwertowania zwróconej opcji [A] na próbę (w odpowiednio idiomatyczny sposób Scala - np. W celu zrozumienia), a następnie kontynuowanie przetwarzania (uzyskanie i sprawdzanie b-ref) podczas ustawiania wszelkich odpowiednich Awarii po drodze.

2

Jeśli chcesz użyć Either, można użyć 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 
} 

Korzystanie z Try, można po prostu napisać kod w sposób bardzo proceduralnego, wrzucając odpowiednie wyjątki, gdzie potrzebne i owijając całość z Try.apply (który będzie wychwytywał wyjątek i przedstawiał je jako instancje 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")) 
} 
1

[Edytowane zidentyfikować różne awarie]

Próbowałem uprościć

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"))) 
} 
Powiązane problemy