Search code examples
scalascala-cats

How to combine results with fold without calling unsafe method inside


Let's say that I have the following case classes:

case class Record(a: String, b: String)
case class Result(c: Int, d: Int)

And a function that does a bunch of things and then returns an IO[Result]

def processRecord[record: Record]: IO[Result] = {
  ...
  // Returns an IO[Result]
}

I need a function called save that receives a list of Records and then needs to combine all the results in one single result

def combineStorageResults(result1: Result, result2: Result) = {
  Result(result1.c + result2.c, result1.d/2 + result2.d/2)
}

I've created the following code that does this by using a foldLeft function. However, as you can see, I need to call unsafeRunSync in the middle of the process, just to wrap up the result in an IO at the end... I'm wondering if there is any way to do the same but without having to call unsafeRunSync in the middle of the function:

def save(records: Seq[Record]): Future[Result] = {
    IO(records.foldLeft(Result(0, 0))((previousResult, evt) => {
        val processedResult = processRecord(evt).unsafeRunSync()
        combineStorageResults(previousResult, processedResult)
      }
    )).unsafeToFuture()  
} 

Any help would be greatly appreciated.


Solution

  • You could call processRecord on each record first, and then combine them. Also use traverse from cats (works on List, so convert Seq to List)

    import cats.effect.IO
    import cats.syntax.all._
    
    val processed: IO[List[Result]] = records.toList.traverse(processRecord)
    
    val combined: IO[Result] = processed.map { l =>
      l.foldLeft(Result(0, 0))((previousResult, currentResult) => {
        combineStorageResults(previousResult, currentResult)
      })
    }
    
    combined.unsafeToFuture()