2012-12-11 9 views
7

Sto pensando di iniziare ad usare lo stile Monadic nel mio codice Scala per, tra gli altri, lo stato di threading. Ecco un esempio semplificato di combinare 3 funzioni monadici (e la cura solo circa gli effetti collaterali)Scalaz - combinando List e State Monad in per comprensione

import scalaz._ 
import Scalaz._ 

object MonadTest { 
    def adder(i: Int) = State[String, Int] ({str: String => (str + i.toString + " ", i) }) 
    val oneTwoThreeMonad = for { 
    m1 <- adder(1) 
    m2 <- adder(2) 
    m3 <- adder(3) 
    } yield m3 
    oneTwoThreeMonad("start: ")._1 //String = "start: 1 2 3 " 
} 

Tutto questo è abbastanza auto-esplicativo e funziona come previsto. Ma perché questo approccio mi sia davvero utile vorrei poterlo combinare con la comprensione per List. Ecco un po 'di (non funzionante) il codice per mostrare quello che voglio dire:

val list = List(1, 2, 3) 

val oneTwoThreeBis = for { 
    i <- list 
    mx <- adder(i) 
} yield mx 

Fondamentalmente vorrei essere in grado di coniugare monadi sulla base di argomentazioni da un List - eseguire la funzione monadica su ciascuno degli elementi del list e accumula gli effetti collaterali mentre procedo. Capisco la sintassi di esempio non funziona e vedo perché non lo fa - Sto solo cercando un equivalente pulito ed elegante.

Sono quasi sicuro che è possibile ottenere ciò utilizzando i trasformatori scalaz monad, più specificamente con StateT ma non sono sicuro di come si farebbe per farlo.

PS. Sto usando Scalaz 7.0-M3, quindi la sintassi potrebbe essere leggermente diversa dalla più comune 6.x.

risposta

9

io non sono sicuro di capire esattamente quello che stai cercando, ma suona come si desidera qualcosa di più simile a traverse qui (dove traverse è una versione più generale di mapM di Haskell):

import scalaz._, Scalaz._ 

def adder(i: Int) = State[String, Int](str => (str + i.toString + " ", i)) 

List(1, 2, 3).traverseS(adder)("start: ")._1 

Questo stamperà il seguente, come previsto:

res0: String = "start: 1 2 3 " 

si noti che sto usando traverseS (dove i S sta per State) per evitare di dover WRI il parametro di tipo piuttosto disordinato, ma traverse è più generalmente utile ogni volta che vuoi mappare una funzione monadica su qualcosa di attraversabile.

Sono felice di dare un esempio StateT se questo non è quello che volevi, ma questo sta per finire con te avendo qualcosa di tipo List[(String, Int)].

+0

Sembra esattamente quello che sto cercando. Ma non cambia il fatto che dovrò imparare a usare i trasformatori di stato in pratica prima o poi;) – nietaki

+1

Mi sono appena accorto di aver già letto una delle tue risposte contenente una descrizione dettagliata di 'traverse' e' traverseS' e ti ho persino elogiato per questo. Non riesco a immaginare come non sia stato con me ... – nietaki

+1

Se qualcuno ha bisogno di fare qualcosa di simile a Cats piuttosto che a Scalaz, nota che non c'è 'traverseS' ma ho un esempio funzionante di esso usando' traverseU' qui : https://github.com/benhutchison/gesture/blob/master/core/jvm/src/test/scala/gesture/GestureProcessorSpec.scala#L34 –

Problemi correlati