2014-07-21 11 views
9

Non sono sicuro di come descrivere questo problema, quindi mostrerò solo le firme del tipo.Esiste un modo per appiattire le monadi annidate di tipi diversi?

ho un'istanza di quanto segue:

val x:Future[F[Future[F[B]]]] = ??? 

E voglio un'istanza di:

val y:Future[F[B]] = ??? 

F è una monade, così mi hanno i seguenti metodi:

def pure[A](a:A):F[A] = ??? 
def flatMap[A, B](fa:F[A], f:A => F[B]):F[B] = ??? 
def map[A, B](fa:F[A], f:A => B):F[B] = flatMap(fa, (a:A) => pure(f(a))) 

Penso che quanto segue dovrebbe funzionare, ma non sembra giusto:

x.flatMap { fWithFuture => 
    val p = Promise[F[B]] 
    flatMap(fWithFuture, (futureF: Future[F[B]]) => { 
    p.completeWith(futureF) 
    pure(()) 
    }) 
    p.future 
} 

C'è un concetto che mi manca?

Un po 'di informazioni di base. Sto cercando di definire una funzione come questa:

def flatMap[A, B](fa:Future[F[A]], f: A => Future[F[B]]):Future[F[B]] = ??? 

Forse questa è concettualmente una cosa strana. Eventuali suggerimenti su astrazioni utili sono i benvenuti.

+1

FWIW, ho risolto questo problema nel codice prima di utilizzare l'approccio forza bruta che avete qui. Anch'io non mi sentivo in grado di farlo, ma non ho capito niente di meglio. – joescii

+2

Benvenuti nel mondo goffo e goffo dei trasformatori monad! –

+0

@RexKerr stai dicendo che questo è il modo per farlo? – EECOLOR

risposta

2

Questo può rispondere alla parte "E voglio un'istanza di:".

$ scala 
Welcome to Scala version 2.10.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_05). 
Type in expressions to have them evaluated. 
Type :help for more information. 

scala> import scala.concurrent.Future 
import scala.concurrent.Future 

scala> import scala.concurrent.ExecutionContext.Implicits.global 
import scala.concurrent.ExecutionContext.Implicits.global 

scala> Future(List(Future(1),Future(2),Future(3))) // Future[F[Future[B]]] 
res0: scala.concurrent.Future[List[scala.concurrent.Future[Int]]] = [email protected] 

scala> res0.map(Future.sequence(_)) // transformed to Future[Future[F[B]] 
res1: scala.concurrent.Future[scala.concurrent.Future[List[Int]]] = [email protected] 

scala> res1.flatMap(identity) // reduced to Future[F[B]] 
res2: scala.concurrent.Future[List[Int]] = [email protected] 

Speriamo che la definizione flatMap seguito dovrebbe dare un'idea di trasformare i tipi :) ho sostituito F con un tipo di lista per la facilità di comprensione.

+0

'sequence' richiede un' TraversableOnce' (nel tuo caso un 'List'), quindi non funzionerà con altri tipi –

+0

In essenza' sequence' fa la stessa cosa. Usa solo 'CanBuildFrom' per nascondere la mutazione. – EECOLOR

6

Come già detto in precedenza da Rex Kerr, è possibile utilizzare spesso un trasformatore monad per gestire una situazione in cui ci si trova a livelli alternati come questo. Per esempio, se F qui è Option, è possibile utilizzare il trasformatore OptionT monade Scalaz 7.1 s' per scrivere il vostro flatMap:

import scalaz._, Scalaz._ 

type F[A] = Option[A] 

def flatMap[A, B](fa: Future[F[A]], f: A => Future[F[B]]): Future[F[B]] = 
    OptionT(fa).flatMap(f andThen OptionT.apply).run 

OptionT[Future, A] qui è una sorta di wrapper per Future[Option[A]]. Se il numeroè il numero List, è sufficiente sostituire OptionT con ListT e run con underlying (e così via).

La cosa bella è che quando si lavora con OptionT[Future, A], per esempio, si può generalmente evitare di finire con Future[Option[Future[Option[A]]]], in primo luogo, vedere la mia risposta here per una discussione più dettagliata.

Uno svantaggio è che non tutte le monadi hanno trasformatori. Ad esempio, è possibile inserire Future nella parte inferiore della pila (come ho fatto sopra), ma non è davvero un modo utile per definire FutureT.

+0

"ma non c'è davvero un modo utile per definire' FutureT' "Perché è così? Questo è in realtà ciò di cui ho bisogno. – EECOLOR

+0

È [non possibile definire senza bloccare] (https://github.com/typelevel/scalaz-contrib/issues/10). Quando si lavora con 'Future [Option [A]]' il trasformatore monad corrispondente è 'OptionT [Future, A]', non 'FutureT [Option, A]' -è sicuro di aver bisogno di 'FutureT'? –

+0

Non sono sicuro di aver bisogno di un 'FutureT'. L'unica cosa che so per certo è che so di avere un "Futuro" e qualche monade arbitraria che è dentro ... Tornerò al codice per vedere se riesco a strutturarlo diversamente in un modo che mi permette di saltare il 'FutureT'. – EECOLOR

Problemi correlati