2015-10-15 15 views
6

Perché le monadi non compongono quando una Monade è una Applicativa e una Applicativa è una Functional. Vedete questa catena ereditaria in molti articoli sul web (che ho attraversato). Ma quando Funcitors e Applicatives compongono perché Monads rompe questo?Perché le monade non compongono in scala

Qualcuno può fornire un semplice esempio in scala che dimostra questo problema? So che questo è molto richiesto ma difficile da comprendere senza un semplice esempio.

+0

Possibile duplicato di [Applicatives compose, monads do not] (http://stackoverflow.com/questions/7040844/applicatives-compose-monads-dont) – ziggystar

+0

Questa è la risposta haskell non per scala – Jay

+4

Controlla la [risposta di Conal] (http://stackoverflow.com/a/7070339/108915). È un linguaggio agnostico. Le monadi sono un concetto matematico e non differiscono tra le lingue. – ziggystar

risposta

9

In primo luogo, iniziamo con un semplice problema. Diciamo che abbiamo bisogno di ottenere una somma di due numeri interi entrambi avvolti in un Future e Option. Supponiamo che usiamo la libreria cats.

Se usiamo approccio monade (aka flatMap), abbiamo bisogno di:

  • sia Future e Option dovrebbe avere Monad casi definiti su di loro
  • abbiamo anche bisogno di trasformatore monadica OptionT che funzionerà solo per Option (precisamente F[Option[T]])

Quindi, ecco il codice (dimentichiamoci per la comprensione e il sollevamento per rendere più semplice):

val fa = OptionT[Future, Int](Future(Some(1))) 
val fb = OptionT[Future, Int](Future(Some(2))) 
fa.flatMap(a => fb.map(b => a + b)) //note that a and b are already Int's not Future's 

se si guarda al OptionT.flatMap fonti:

def flatMap[B](f: A => OptionT[F, B])(implicit F: Monad[F]): OptionT[F, B] = 
    flatMapF(a => f(a).value) 

def flatMapF[B](f: A => F[Option[B]])(implicit F: Monad[F]): OptionT[F, B] = 
OptionT(F.flatMap(value)(_.fold(F.pure[Option[B]](None))(f))) 

Si noterà che il codice è abbastanza specifico per la logica s' Option interna e della struttura (fold, None). Stesso problema per EitherT, StateT ecc

cosa importante è che non v'è alcun FutureT definito nei gatti, in modo da poter comporre Future[Option[T]], ma non può farlo con Option[Future[T]] (in seguito vi mostrerò che questo problema è ancora più più generico).

D'altra parte, se si sceglie la composizione utilizzando Applicative, dovrete soddisfare solo requisito:

  • sia Future e Option dovrebbe avere Applicative casi definiti su di loro

È non sono necessari trasformatori speciali per Option, in pratica la libreria cats fornisce la classe Nested che funziona per qualsiasi Applicative (dimentichiamo lo zucchero del builder applicativo per semplificare comprensione):

val fa = Nested[Future, Option, Int](Future(Some(1))) 
val fb = Nested[Future, Option, Int](Future(Some(1))) 
fa.map(x => (y: Int) => y + x).ap(fb) 

Diamo loro swap:

val fa = Nested[Option, Future, Int](Some(Future(1))) 
val fb = Nested[Option, Future, Int](Some(Future(1))) 
fa.map(x => (y: Int) => y + x).ap(fb) 

Works!

Quindi sì Monade è applicativo, Option[Future[T]] è ancora una monade (su Future[T] ma non su T in sé), ma consente di operare solo con Future[T] non T. Per "unire" Option con i livelli Future - devi definire il trasformatore monadico FutureT, per unire Future con Option - devi definire OptionT. E, OptionT è definito in cats/scalaz, ma non FutureT.

In generale (da here):

Purtroppo, il nostro vero obiettivo, la composizione di monadi, è un po 'più difficile. .. In realtà, possiamo effettivamente dimostrare che, in un certo senso, non c'è modo di costruire una funzione di join con il tipo sopra usando solo le operazioni delle due monadi (vedi l'appendice per un profilo della dimostrazione). Ne consegue che l'unico modo che si potrebbe sperare per formare una composizione è se ci sono alcune costruzioni aggiuntive che collegano i due componenti

E questa composizione non è nemmeno commutativa come ho mostrato per Option e Future.

Come esercizio, si può provare a definire la 's FutureT flatMap:

def flatMapF[B](f: A => F[Future[B]])(implicit F: Monad[F]): FutureT[F, B] = 
    FutureT(F.flatMap(value){ x: Future[A] => 
     val r: Future[F[Future[B]] = x.map(f) 
     //you have to return F[Future[B]] here using only f and F.pure, 
     //where F can be List, Option whatever 
    }) 

fondamentalmente il problema con tale implementazione è che si deve 'estrarre' valore dal r che è impossibile qui, supponendo che può 'estrai il valore da Future (non è stato definito alcun comando da parte di esso), che è vero se stiamo parlando di API "non bloccanti". Questo in pratica significa che non è possibile "scambiare" Future e F, come Future[F[Future[B]] => F[Future[Future[B], che è a proposito di trasformazione naturale (morfismo tra i funtori).In modo che spiega il primo commento su this general answer:

è possibile comporre monadi se è possibile fornire una trasformazione di swap naturale: NM a -> MN un

Applicative s tuttavia non hanno questi problemi - puoi facilmente comporli, ma tieni presente che il risultato della composizione di due Applicatives potrebbe non essere una monade (ma sarà sempre un applicativo). Nested[Future, Option, T] non è una monade su T, indipendentemente dal fatto che sia Option sia Future siano monadi su T. Mettere in parole semplici Nested as a class non ha flatMap.

Sarebbe anche utile leggere:

Mettendo tutto insieme (F e G sono monadi)

  • F[G[T]] è una monade sul G[T], ma non su T
  • G_TRANSFORMER[F, T] necessaria al fine di ottenere una monade sul T da F[G[T]].
  • non esiste in quanto tale MEGA_TRANSFORMER[G, F, T] trasformatore non può essere costruito sopra monade - richiede ulteriori operazioni definite su G (sembra che comonad su G dovrebbe essere sufficiente)
  • ogni monade (compresi G e F) è applicativo, ma non ogni applicativo è una monade
  • teoricamente F[G[T]] è un applicativo su entrambi G[T] e T. Tuttavia scala richiede di creare NESTED[F, G, T] per ottenere l'applicativo composto su T (che è implementato nella libreria di gatti).
  • NESTED[F, G, T] è applicativa, ma non una monade

Questo significa che è possibile comporre Future x Option (aka Option[Future[T]]) ad un unico monade, ma non è possibile comporre Option x Future (aka Future[Option[T]]) senza sapere che sono qualcosa altro oltre a essere monadi. E puoi comporre qualsiasi due applicativi in ​​un unico applicativo, puoi anche comporre due monadi in un solo singolo applicativo (ma non in una singola monade).

0

Tony Morris ha tenuto un discorso sui trasformatori monad che spiega molto bene questa precisa questione.

http://tonymorris.github.io/blog/posts/monad-transformers/

usa haskell, ma gli esempi sono facilmente traducibile scala.

+0

Voglio evitare questa sintassi haskell perché è ancora più difficile da leggere soprattutto per i principianti su questo argomento! Quindi preferirei un semplice esempio di scala che sia comprensibile. Thx – Jay

+1

Forse dovresti provare a scrivere una funzione di composizione per Functors, quindi provare a scriverne uno per Monads: ti darà una buona idea. – melps

+2

Questa è una buona risorsa e una sintesi delle parti pertinenti sarebbe una buona risposta, ma i propri collegamenti a risorse esterne dovrebbero essere commenti, non risposte. –