EDIT here is the real solution. I left in the original post because I think its worthwhile seeing the pattern. What works for Klesli works for IterateeT
import java.io.{ BufferedReader, File, FileReader }
import scalaz._, Scalaz._, effect._, iteratee.{ Iteratee => I, _ }
object IterateeIOExample {
type ErrorOr[+A] = EitherT[IO, Throwable, A]
def openFile(f: File) = IO(new BufferedReader(new FileReader(f)))
def readLine(r: BufferedReader) = IO(Option(r.readLine))
def closeReader(r: BufferedReader) = IO(r.close())
def tryIO[A, B](action: IO[B]) = I.iterateeT[A, ErrorOr, B] {
EitherT.fromEither(action.catchLeft).map(r => I.sdone(r, I.emptyInput))
}
def enumBuffered(r: => BufferedReader) = new EnumeratorT[String, ErrorOr] {
lazy val reader = r
def apply[A] = (s: StepT[String, ErrorOr, A]) => s.mapCont(k =>
tryIO(readLine(reader)) flatMap {
case None => s.pointI
case Some(line) => k(I.elInput(line)) >>== apply[A]
})
}
def enumFile(f: File) = new EnumeratorT[String, ErrorOr] {
def apply[A] = (s: StepT[String, ErrorOr, A]) =>
tryIO(openFile(f)).flatMap(reader => I.iterateeT[String, ErrorOr, A](
EitherT(
enumBuffered(reader).apply(s).value.run.ensuring(closeReader(reader)))))
}
def main(args: Array[String]) {
val action = (
I.consume[String, ErrorOr, List] %=
I.filter(a => a.count(_ == '0') >= 25) &=
enumFile(new File(args(0)))).run.run
println(action.unsafePerformIO().map(_.size))
}
}
===== Original Post =====
I feel like you need an EitherT in the mix. Without EitherT you are just ending up with a 3 Lefts or Rights. With EitherT it would propergate the left.
I think what you really want is
type ErrorOr[+A] = EitherT[IO, Throwable, A]
I.iterateeT[A, ErrorOr, B]
The following code mimics how you are currently composing things. Because IterateeT has no concept of left and right, when you compose it, you just end up with a bunch of IO/Id's.
scala> Kleisli((a:Int) => 4.right[String].point[Id])
res11: scalaz.Kleisli[scalaz.Scalaz.Id,Int,scalaz.\/[String,Int]] = scalaz.KleisliFunctions$$anon$18@73e771ca
scala> Kleisli((a:Int) => "aa".left[Int].point[Id])
res12: scalaz.Kleisli[scalaz.Scalaz.Id,Int,scalaz.\/[String,Int]] = scalaz.KleisliFunctions$$anon$18@be41b41
scala> for { a <- res11; b <- res12 } yield (a,b)
res15: scalaz.Kleisli[scalaz.Scalaz.Id,Int,(scalaz.\/[String,Int], scalaz.\/[String,Int])] = scalaz.KleisliFunctions$$anon$18@42fd1445
scala> res15.run(1)
res16: (scalaz.\/[String,Int], scalaz.\/[String,Int]) = (\/-(4),-\/(aa))
In the following code, instead of using Id, we use an EitherT. Since EitherT has the same bind behaviour as Either, we end up with what we want.
scala> type ErrorOr[+A] = EitherT[Id, String, A]
defined type alias ErrorOr
scala> Kleisli[ErrorOr, Int, Int]((a:Int) => EitherT(4.right[String].point[Id]))
res22: scalaz.Kleisli[ErrorOr,Int,Int] = scalaz.KleisliFunctions$$anon$18@58b547a0
scala> Kleisli[ErrorOr, Int, Int]((a:Int) => EitherT("aa".left[Int].point[Id]))
res24: scalaz.Kleisli[ErrorOr,Int,Int] = scalaz.KleisliFunctions$$anon$18@342f2ceb
scala> for { a <- res22; b <- res24 } yield 2
res25: scalaz.Kleisli[ErrorOr,Int,Int] = scalaz.KleisliFunctions$$anon$18@204eab31
scala> res25.run(2).run
res26: scalaz.Scalaz.Id[scalaz.\/[String,Int]] = -\/(aa)
You can replace Keisli with IterateeT and Id with IO to get what you need.