2013-02-22 11 views
7

Mi piace molto lo scala.util.Try in Scala 2.10 e il suo funzionamento con la comprensione rende la gestione di più passaggi che potrebbero andare storto facilmente.Errore rapido quando si usa la comprensione per scala.util.Try

Ad esempio, potremmo usare il seguente codice per assicurarci di stampare solo due numeri se e solo se tutto è sotto controllo e il valore viene correttamente ottenuto.

def tryA: Try[Int] = {....} 
def tryB: Try[Int] = {....} 

for { 
    a <- tryA 
    b <- tryB 
} { 
    println (s"We got:${a+b}") 
} 

Ma una delle mia preoccupazione è che questo codice è in realtà ignora tutte le eccezioni, il che significa che ha il seguente blocco try-cactch:

try { 
    // ..... 
} catch { 
    case _: Exception => // Swallow any exception 
} 

Per quanto ne so, non c'è un argomento che questo tipo di codici è un cattivo odore, perché nessuno noterà che si verifica un'eccezione.

Quello che vorrei realizzare è che ancora utilizzando for per assicurarsi che il println eseguire solo se tutto è OK, ma se non v'è alcuna eccezione in tutte le fasi, si farà saltare in aria e buttare fuori l'eccezione direttamente.

Attualmente ecco come faccio questo, ma sembra meno elegante perché introduce un nuovo oggetto Try[Unit], quindi mi chiedo come potrei rendere questo codice migliore?

Ad esempio, è possibile eliminare la variabile result e l'istruzione result.get, ma viene comunque generata un'eccezione?

def tryA: Try[Int] = {....} 
def tryB: Try[Int] = {....} 

val result = for { 
    a <- tryA 
    b <- tryB 
} yield { 
    println (s"We got:${a+b}") 
} 
result.get 

Aggiornamento

Per rendere più chiaro cosa, è il risultato da Scala REPL del primo codice in questa domanda.

scala> def tryA: Try[Int] = Success(1) 
tryA: scala.util.Try[Int] 

scala> def tryB: Try[Int] = Failure(new Exception("error")) 
tryB: scala.util.Try[Int] 

scala> for { 
    | a <- tryA 
    | b <- tryB 
    | } { 
    | println (s"We got:${a+b}") 
    | } 

scala> 

Possiamo vedere che non succede nulla qui, anche tryB è un Failure con un'eccezione. Quello che vorrei ottenere è un'eccezione generata e senza l'introduzione del nuovo oggetto Try[Unit] con yield, è possibile?

+0

Il tipo 'println' è' Unit', quindi 'result' genererà un'eccezione o restituirà il valore' Unit', '()'. –

+0

No, non genererà un'eccezione a meno che non utilizzi il metodo 'get'. –

+0

Il codice di esempio include un 'result.get' incondizionato. (Intendevo scrivere 'result.get' nel mio primo commento ...) –

risposta

4

È possibile utilizzare recover:

import scala.util.Try 

def tryEven = Try { val i = (math.random * 1000).toInt; if (i % 2 != 0) throw new Exception("odd") else i } 

def tryEvenOrNeg1 = Try { val i = (math.random * 1000).toInt; if (i % 2 != 0) throw new Exception("odd") else i } recover { case exx: Exception => -1 } 

scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b") 
res1: scala.util.Try[Unit] = Failure(java.lang.Exception: odd) 

scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b") 
res2: scala.util.Try[Unit] = Failure(java.lang.Exception: odd) 

scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b") 
res3: scala.util.Try[Unit] = Failure(java.lang.Exception: odd) 

scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b") 
Got 542, -1 

scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b") 
res5: scala.util.Try[Unit] = Failure(java.lang.Exception: odd) 

scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b") 
res6: scala.util.Try[Unit] = Failure(java.lang.Exception: odd) 

scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b") 
Got 692, 750 

ho rimosso la resNN che rifletteva Success(()).

+0

Quello che sto cercando di fare è di non ottenere un valore predefinito o di salvare un'eccezione. Invece, voglio che l'eccezione venga lanciata direttamente se 'tryEven' o' tryEvenOrNeg1' è un fallimento. In altre parole, mi piacerebbe ottenere l'effetto del risultato.get' nel mio post, ma senza la variabile 'result'. –

+0

'(per (un <- tryEven; b <- tryEven) restituisce println (s" Got $ a $ b ")). Get' –

0

OK, ho dimenticato che abbiamo sempre una conversione implicita in Scala. ;-)

Così potremmo implementare questo comportamento noi stessi, creeremo più oggetti rispetto alla versione yield, ma penso che l'intenzione di questo codice sia molto più chiara.

implicit class BlowUpTry[T](current: Try[T]) { 

    def throwIfFailed: Try[T] = current match { 
    case Success(value)  => current 
    case Failure(exception) => throw exception 
    } 

} 

def tryA: Try[Int] = Success(1) 
def tryB: Try[Int] = Failure(new Exception("error")) 

for { 
    a <- tryA.throwIfFailed 
    b <- tryB.throwIfFailed 
} { 
    println(s"We got ${a + b}") 
} 
+5

Non ha senso usare" Prova "se si intende lanciare un'eccezione. –

+1

Più "throwIfFailed' è equivalente al metodo" get "di" get ". –

Problemi correlati