I'm not quite sure what Step means in scalaz, but the ListT implementation ListT pretty straight forward. Depending on how many operations you want to put on it, this may be a bit of work, but the basic operations of a monad can be implemented as follows.
First we need classes for monad and functor (we could also add applicative ones, but this is not necessary for this example):
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]] }
Once we get them, we can create a transformer that basically just wraps F[List[A]] and redirects its map and flatMap to a list, calling map on the containing functor, and then calling map or flatMap respectively. on containing 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 }
As soon as we finish with the modification, we will get the received object by calling the run method of the ListT object. If you want, you can also add other list operations, for example, in scalaz. This should be pretty straight forward. For example, :: might look like this:
def ::(x: A) = ListT(Monad[F].map(fa)(x :: _))
Using:
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))