简体   繁体   中英

How do you flatten a for-comprehension with traverse in scala cats effect?

I have some code structurally identical to this and I'm not sure the best way to clean it up? The trivial IOs and additions are there so that the example compiles without needing additional methods.

I'd really like to not have it be so nested, is there a way to have a single for comprehension that supports both IO and List? I know theres OptionT for the option variant of this problem, but there doesn't seem to be an equivalent ListT.

Any advice would be greatly appreciated

import cats.Traverse
import cats.effect.IO
import cats.implicits._

def exampleDoingSomeThings: IO[Unit] = for {
    ids <- IO.pure(List(1, 2, 3))
    _ <- ids.traverse[IO, Unit](id => for {
      users <- IO.pure(List(id + 4, id + 5, id + 6))
      data <- IO(id + 7)
      otherData <- IO(id + 8)
      _ <- users.traverse[IO, Unit](ud => for {
        innerData <- IO(id + ud)
        innerState <- IO(ud + 9)
        _ <- if (innerState > 15) for {
          _ <- IO(println(s"action1: $id $ud"))
          _ <- IO(println(s"action2: $id $ud"))
        } yield () else IO.pure()
      } yield ())
    } yield ())
  } yield ()

exampleDoingSomeThings.unsafeRunSync

https://scalafiddle.io/sf/S79H1ZI/0

As others have mentioned, you could extract your methods to sub methods. However, if you find that is not enough, you can use libraries like FS2 or Monix to make your life easier. Its really good for handling IO + List things.

You can visualize a stream as a list of items that emits one by one. So, you only need to deal with one at a time.

The above example can be translated as (excluding unused variables):

Monix:

def monixThings: Observable[Unit] = for {
  id <- Observable.fromIterable(List(1, 2, 3))
  ud <- Observable.fromIterable(List(id + 4, id + 5, id + 6))
  innerState <- Observable.pure(ud + 9)
  _ <- Observable.fromTask {
    if (innerState > 15) {
      for {
        _ <- Task.delay(println(s"action1: $id $ud"))
        _ <- Task.delay(println(s"action2: $id $ud"))
      } yield ()
    } else {
      Task.unit
    }
  }
} yield ()

monixThings.completedL.runSyncUnsafe()

https://scalafiddle.io/sf/BDKbGCq/0

FS2:

import cats.effect.IO
import fs2.Stream

object FS2Example extends App {

  def fs2Things = for {
    id <- Stream.emits(List(1, 2, 3))
    ud <- Stream.emits(List(id + 4, id + 5, id + 6))
    innerState <- Stream.emit(ud + 9)
    _ <- Stream.eval {
      if (innerState > 15) {
        for {
          _ <- IO(println(s"action1: $id $ud"))
          _ <- IO(println(s"action2: $id $ud"))
        } yield ()
      } else {
        IO.unit
      }
    }
  } yield ()

  fs2Things.compile.drain.unsafeRunSync()
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM