Nie jestem do końca pewien, co oznacza krok w skalowaniu, ale wdrożenie ListT
jest całkiem proste. W zależności od tego, ile operacji chcesz nałożyć, może to być trochę pracy, ale podstawowe operacje monady mogą być realizowane w następujący sposób.
Najpierw musimy typeclasses dla monady i funktora (możemy również dodać aplikacyjnych, ale nie jest to konieczne dla tego przykładu):
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]]
}
Gdy mamy tych, możemy stworzyć transformator, który w zasadzie tylko opakowuje F[List[A]]
i przekazuje połączenie do swojej funkcji map
i flatMap
do listy, wywołując map
na funcie zawierającym, a następnie wywołując map
lub flatMap
odp. na zawartych 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
}
Gdy skończymy z modyfikacji, możemy uzyskać obiekt wynikowy przez wywołanie metody run
na obiekcie ListT
. Jeśli chcesz, możesz także dodać inne operacje specyficzne dla listy, takie jak w skalaz. To powinno być całkiem proste. Na przykład ::
mógłby wyglądać następująco:
def ::(x: A) = ListT(Monad[F].map(fa)(x :: _))
Zastosowanie:
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))
Wersja 7.x nie używa 'Step', wydaje się bardzo prosta. https://github.com/scalaz/scalaz/blob/v7.0.3/core/src/main/scala/scalaz/ListT.scala – huynhjl
@huynhjl Tak, byłem pewien, że widziałem 7 implementację z 'Step', choć . – ziggystar