2016-04-18 20 views
5

Spesso uso la funzione qui sotto per convertire Option[Try[_]] in Try[Option[_]] ma sembra sbagliato. Può essere una tale funzionalità espressa in modo più idiomatico?Come convertire l'opzione [Prova [_]] per provare [Opzione [_]]?

def swap[T](optTry: Option[Try[T]]): Try[Option[T]] = { 
    optTry match { 
    case Some(Success(t)) => Success(Some(t)) 
    case Some(Failure(e)) => Failure(e) 
    case None => Success(None) 
    } 
} 

Dire che ho due valori:

val v1: Int = ??? 
val v2: Option[Int] = ??? 

Voglio fare un'operazione op (che può fallire) su questi valori e passare che a funzionare f sotto.

def op(x: Int): Try[String] 
def f(x: String, y: Option[String]): Unit 

Io di solito uso di comprensione per migliorare la leggibilità:

for { 
    opedV1 <- op(v1) 
    opedV2 <- swap(v2.map(op)) 
} f(opedV1, opedV2) 

PS. Mi piacerebbe evitare alcune cose pesanti come lo scalaz.

+2

La definizione di 'swap' sembra ragionevole. Non sono sicuro di cosa si possa desiderare di più. –

+3

Un pensiero: questa operazione è solitamente (idiomaticamente?) Chiamata 'sequenza' piuttosto che' scambio '. –

risposta

2

Suoni come Try { option.map(_.get) } faranno quello che vuoi.

+8

Dal mio punto di vista, lanciare e catturare l'eccezione come questa ha un sovraccarico sufficiente (ad esempio in fase di esecuzione, ma soprattutto per i lettori) che l'implementazione degli OP è preferibile, anche se è un po 'meno concisa. –

+1

Qual è il sovraccarico dell'eccezione di lancio (oltre a istanziarlo e riempire lo stack, che sarebbe già stato fatto a questo punto)? Non sono consapevole che sia significativamente più grande di una funzione. In ogni caso, immagino che, se si tratta di spese generali di lancio delle eccezioni, non dovrebbe usare "Prova" per cominciare. O scala per questo (ci sono cose in scala che pongono molto più overhead e sono molto più difficili da evitare rispetto alle eccezioni). Basta attaccare con "C" e gotos per essere sicuri di ridurre al minimo il sovraccarico :) – Dima

+1

Ecco perché ho sottolineato il sovraccarico per i lettori umani. Spesso grep per le chiamate '.get' su' Try' o 'Option' come misura rapida dello stato di salute di un progetto che è nuovo per me, e non faccio eccezioni perché gli autori sono intelligenti per salvare un un paio di linee ed evitare un pattern match. :) –

0

Questa variante consente di evitare rethrowing:

import scala.util.{Failure, Success, Try} 

def swap[T](optTry: Option[Try[T]]): Try[Option[T]] = 
    optTry.map(_.map(Some.apply)).getOrElse(Success(None)) 

swap(Some(Success(1))) 
// res0: scala.util.Try[Option[Int]] = Success(Some(1)) 

swap(Some(Failure(new IllegalStateException("test")))) 
// res1: scala.util.Try[Option[Nothing]] = Failure(java.lang.IllegalStateException: test) 

swap(None) 
// res2: scala.util.Try[Option[Nothing]] = Success(None) 
2

La libreria cats consente di sequenza un Option ad un Try molto facilmente:

scala> import cats.implicits._ 
import cats.implicits._ 

scala> import scala.util.{Failure, Success, Try} 
import scala.util.{Failure, Success, Try} 

scala> Option(Success(1)).sequence[Try, Int] 
res0: scala.util.Try[Option[Int]] = Success(Some(1)) 

scala> Option(Failure[Int](new IllegalArgumentException("nonpositive integer"))).sequence[Try, Int] 
res1: scala.util.Try[Option[Int]] = Failure(java.lang.IllegalArgumentException: nonpositive integer) 

scala> None.sequence[Try, Int] 
res2: scala.util.Try[Option[Int]] = Success(None)