Search code examples
scalaiofunctional-programmingscala-cats

Transform a List of IO's into an IO of a List


I want to read a list of files using cats-effect's IO monad like this:

def readFile(file: File): IO[Either[CouldNotReadFromFileError, String]] = ???

// lists all the files I want to read
// returns an Either b/c this might encounter i/o problems
def findFiles(): IO[Either[Throwable, Array[File]]] = ???

// reads all files and saves their content in an Array[String]
// ignores files it could not read or find
def readFiles(): IO[Array[String]] = for {
    filesE <- listFiles
    files = filesE match {
      case Left(err) =>
        log.error("An error happened while reading files: " + err.getMessage)
        List[File]()
      case Right(fs) => fs.toList.map(readFile)
    }
    // files has type: List[IO[Either[CouldNotReadFromFileError, String]]]
    // to continue here I'd like to have a: IO[List[Either[CouldNotReadFromFileError, String]]]
    ???
} yield ???

Now to continue my computation inside the for-yield-construction I'd like to transform my List[IO[Either[CouldNotReadFromFileError, String]]] into a IO[List[Either[CouldNotReadFromFileError, String]]]. I know that I can probably do something like this using cat's traverse, but cannot figure out how exactly. Any help is greatly appreciated.


Solution

  • sequence is enough for what you want:

    import java.io.File
    import cats.effect.IO
    import cats.implicits._
    
    final class CouldNotReadFromFileError extends RuntimeException("message")
    
    object TestTest {
      def readFile(file: File): IO[Either[CouldNotReadFromFileError, String]] = ???
    
      def findFiles: IO[Either[Throwable, Array[File]]] =
        ???
    
      // reads all files and saves their content in an Array[String]
      // ignores files it could not read or find
      def readFiles(): IO[Array[String]] =
        for {
          filesE <- findFiles
          files = filesE match {
            case Left(err) =>
              List.empty
            case Right(fs) =>
              fs.toList.map(readFile)
          }
          // The type ascription below is just for demonstration purposes.
          // You don't need to keep it there.
          a <- files.sequence: IO[List[Either[CouldNotReadFromFileError, String]]]
        } yield {
          ???
        }
    }