2016-03-18 20 views
5

Esiste il concetto di uno Future che non può fallire in Scala?Futuro che non può fallire in Scala

sto trasformando un Future[Result], che può fail-perciò gestire sia un Failure e Success -nel un Future[Option[String]], portando un messaggio di errore facoltativa derivante dalle fallimento o il successo stati. Fin qui tutto bene.

La cosa è ora, vorrei formalmente (vale a dire, con l'aiuto del sistema di tipi) ricordare che questo futuro avrà sempre un Success e che non avrò bisogno di gestire il caso di fallimento in futuro.

C'è un modo intelligente per farlo?

+0

Perché ne hai bisogno? modifica: stai solo ignorando alcuni: Risultato per restituire un messaggio di errore opzionale: String? –

+0

Perché non 'Future [Try [T]]'? – cchantep

+0

@cchantep Non vedo il punto con questo: dovrei gestire sia il fallimento del futuro, sia il fatto che il futuro, anche se di per sé un successo, possa contenere un valore di 'Failure'. Al contrario di quello che voglio. –

risposta

3

Non è questo il tipo di codifica?

scala> type Tagged[U] = { type Tag = U } 
defined type alias Tagged 

scala> type @@[T, U] = T with Tagged[U] 
defined type alias $at$at 

scala> trait OK ; trait Uncertain 
defined trait OK 
defined trait Uncertain 

scala> type Sure[A] = Future[A] @@ OK 
defined type alias Sure 

scala> type Unsure[A] = Future[A] @@ Uncertain 
defined type alias Unsure 

scala> val f = Future.successful(42).asInstanceOf[Sure[Int]] 
f: Sure[Int] = Future(Success(42)) 

poi

scala> object X { def p(f: Sure[_]) = "sure" ; def p(f: Unsure[_])(implicit d: DummyImplicit) = "unsure" } 
defined object X 

scala> X.p(f) 
res1: String = sure 

Non resta sicuro sotto la mappa, ovviamente.

+0

Davvero molto bello. (a) Posso aggiungere nuovi metodi a un tipo 'Sure [A]'? (b) Posso ottenere in qualche modo che il compilatore mi dica "non chiamare onFailure su' Sure [A] ', dummy"? –

+0

È possibile eseguire normalmente i metodi di estensione tramite classi di tipi. Si potrebbe fare una classe di tipo per un 'onFailing 'personalizzato che non si applicherebbe a' Sure' introducendo un implicito ambiguo. Il tuo normale onFailing potrebbe semplicemente inoltrare a f.onFailure. –

+0

Grazie. Suppongo che intendessi "Puoi eseguire i metodi di estensione tramite ** classi implicite **". –

6

Non è possibile farlo "con l'aiuto del sistema di tipi" perché non è possibile che il sistema di tipi garantisca un errore Future, anche se si promette che non lo farà.

Considerate questo:

Future { doStuff(); } 
    .recover { case _ => "Failed!" } // Now it always succeeds 
    .map { _ => Seq.empty[String].head } // Now it does not. 

Anche se si andavano a rendere impossibile ogni ulteriore trasformazione, una volta che il Future è stato dichiarato di riuscire sempre, che ancora non aiuta, perché il gestore di eccezioni (o quello che fare per convertire il futuro originale in "sempre successo") potrebbe gettare.

Aggiornamento: come ha sottolineato in un commento qui sotto, il frammento di codice di cui sopra non è corretto: il risultato di .map non è la stessa Future come il risultato di .recover. Il punto si regge comunque. Ecco l'illustrazione corretta:

Future { doStuff } 
    .recover { case _ => Seq.empty[String].head } 
+0

Non '.recover {case _ =>" Fallito! "}' una cattiva idea dato che stai prendendo anche 'java.lang.Error'? –

+0

@KevinMeredith Sì ... era solo un esempio a scopo illustrativo. Non metterlo in produzione :) – Dima

+1

Il risultato del recupero e il risultato della mappa sono due diversi futures, quindi "it" cambia referente. Non c'è niente di intrinsecamente sbagliato nell'affermare al compilatore, "So qualcosa che non si tratta del risultato del recupero." Forse un correttore di effetti collaterali viene eseguito offline. Inoltre devi solo ragionare sulle eccezioni 'Nonfatal'. Se genera InterruptedException, il futuro semplicemente non viene completato. Quindi il "successo" si basa sul completamento. –