Iterator[String]
An Iterator[String] is what you get after invoking scala.io.Source.fromFile(file).getLines (see foreach school). When you import implicits from com.ambiata.origami.FoldableM you can turn this Iterator into a FoldableM and apply FoldMs to it.
State folding
Let's go back to the example of counting the number of lines and totalLineSize of a file described in foreach school:
import com.ambiata.origami._, Origami._
import scalaz._, Scalaz._
val source = scala.io.Source.fromFile("file.txt")
val totalSize: Fold[String, Int] =
fromMonoidMap[String, Int](_.size)
val result: (Int, Int) =
(count <*> totalSize).run(source)
(see implicits for a description of imports)
This solves the "composition" problem because it is now very easy to develop and test a new piece of folding, totalSize, and add it to the current processing, count.
This is however not very clean because:
-
the fold is executed right away, as a side-effect
-
there is no resource management
The IO monad
We need, at least, to handle side effects. In order to do this we can execute our fold in the IO monad:
import com.ambiata.origami._, Origami._
import scalaz._, Scalaz._
import scalaz.effect._
val source = scala.io.Source.fromFile("file.txt")
val totalSize: Fold[String, Int] =
fromMonoidMap[String, Int](_.size)
val result: IO[(Int, Int)] =
(count <*> totalSize).into[IO].run(source)
This is better, now we need to close the source when we are done with it.
SafeTIO
import com.ambiata.origami._, Origami._
import scalaz._, Scalaz._
val source = scala.io.Source.fromFile("file.txt")
val totalSize: Fold[String, Int] =
fromMonoidMap[String, Int](_.size)
val result: SafeTIO[(Int, Int)] =
(count <*> totalSize).into[SafeTIO].run(source)
SafeTIO is a type alias for SafeT[IO, ?] (see Type aliases for more definitions) and is just a small layer on top of IO to register "finalizers" which will execute when an IO action executes whether it succeeds or not (see SafeT).
The result is a SafeTIO[(Int, Int)] which we can run to get a proper IO value:
val ioResult: IO[(Int, Int)] =
result.run