2013-08-23 11 views
7

Ho una monade che è molto simile a una collezione monade. Attualmente sto cercando di implementare un trasformatore monad per questo, ma sto fallendo.Come implementare il trasformatore monade `List` in Scala?

Ho esaminato l'implementazione ListT in Scalaz 6 e 7, ma non riesco a capire come funziona. Usa qualche altro tipo Step, il cui scopo non è chiaro per me.

Quindi qualcuno può spiegarmi come implementare un elenco di trasformatore monad, spiegando l'approccio Scalaz o utilizzando un'implementazione diversa?

+1

La versione 7.x non usa 'Passo', sembra molto semplice. https://github.com/scalaz/scalaz/blob/v7.0.3/core/src/main/scala/scalaz/ListT.scala – huynhjl

+0

@huynhjl Giusto, ero sicuro di aver visto un'implementazione 7 con 'Step', sebbene . – ziggystar

risposta

17

Non sono del tutto sicuro, cosa significa Step in scalaz, ma l'implementazione di ListT è piuttosto semplice. A seconda del numero di operazioni che vuoi mettere su di esso, può essere un po 'di lavoro, ma le operazioni di base di Monad possono essere implementate come segue.

Prima dobbiamo typeclasses per monade e funtore (potremmo anche aggiungere applicativa, ma che non è necessario per questo esempio):

trait Functor[F[_]] { 
    def map[A,B](fa: F[A])(f: A => B): F[B] 
} 

trait Monad[F[_]] extends Functor[F] { 
    def flatMap[A,B](fa: F[A])(f: A => F[B]): F[B] 
    def pure[A](x: A): F[A] 
} 

object Monad { 

    implicit object ListMonad extends Monad[List] { 
    def map[A,B](fa: List[A])(f: A => B) = fa map f 
    def flatMap[A,B](fa: List[A])(f: A => List[B]) = fa flatMap f 
    def pure[A](x: A) = x :: Nil 
    } 

    implicit object OptionMonad extends Monad[Option] { 
    def map[A,B](fa: Option[A])(f: A => B) = fa map f 
    def flatMap[A,B](fa: Option[A])(f: A => Option[B]) = fa flatMap f 
    def pure[A](x: A) = Some(x) 
    } 

    def apply[F[_] : Monad]: Monad[F] = implicitly[Monad[F]] 

} 

volta abbiamo quelli, possiamo creare il trasformatore, che fondamentalmente solo avvolge lo F[List[A]] e inoltra la chiamata alla sua funzione e flatMap all'elenco chiamando map sul functor contenente e quindi chiamando map risp. flatMap risp. sul contenuto List/s.

final case class ListT[F[_] : Monad, A](fa: F[List[A]]) { 
    def map[B](f: A => B) = ListT(Monad[F].map(fa)(_ map f)) 

    def flatMap[B](f: A => ListT[F, B]) = ListT(Monad[F].flatMap(fa) { _ match { 
    case Nil => Monad[F].pure(List[B]()) 
    case list => list.map(f).reduce(_ ++ _).run 
    }}) 

    def ++(that: ListT[F,A]) = ListT(Monad[F].flatMap(fa) { list1 => 
    Monad[F].map(that.run)(list1 ++ _) 
    }) 

    def run = fa 
} 

Una volta che abbiamo finito con la modifica, si può ottenere l'oggetto risultante chiamando il metodo run sull'oggetto ListT. Se lo desideri, puoi anche aggiungere altre operazioni specifiche come in scalaz. Questo dovrebbe essere abbastanza semplice. Ad esempio, un :: potrebbe apparire come segue:

def ::(x: A) = ListT(Monad[F].map(fa)(x :: _)) 

Usage:

scala> ListT(Option(List(1,2,3))) 
res6: ListT[Option,Int] = ListT(Some(List(1, 2, 3))) 

scala> res6.map(_+45) 
res7: ListT[Option,Int] = ListT(Some(List(46, 47, 48))) 

scala> 13 :: res7 
res8: ListT[Option,Int] = ListT(Some(List(13, 46, 47, 48))) 

scala> res8.run 
res10: Option[List[Int]] = Some(List(13, 46, 47, 48)) 
Problemi correlati