简体   繁体   English

使用 Scala 猫效应执行副作用

[英]Performing side effects using scala cats effect

I'm trying to use cats effect in scala and in the end of the world I have the type: IO[Vector[IO[Vector[IO[Unit]]]]]我正在尝试在 Scala 中使用猫效果,并且在世界末日我有这样的类型: IO[Vector[IO[Vector[IO[Unit]]]]]

I found only one method to run it:我发现只有一种方法来运行它:

for {
    row <- rows.unsafeRunSync()
} yield
 for {
   cell <- row.unsafeRunSync()
 } yield cell.handleErrorWith(errorHandlingFunc).unsafeRunSync()

But it looks pretty ugly.但它看起来非常丑陋。 Please help me to understand how I can perform complex side effects.请帮助我了解如何执行复杂的副作用。

UPDATE:更新:

1)First IO - I open excel file and get the vector of rows ie IO[Vector[Row]] . 1)第一个IO - 我打开 excel 文件并获取行向量,即IO[Vector[Row]]

2)Second IO - I perform query to DB for each row. 2)第二次IO - 我对每一行执行数据库查询。 I can't compose IO monad with Vector[_] ,我无法用Vector[_]组合 IO monad,

3)Third IO - I create PDF file for each row from excel using Vector[Results] from DB. 3)第三个IO - 我使用来自 DB 的Vector[Results]为 excel 的每一行创建 PDF 文件。

So I have such functions as:所以我有这样的功能:

1) String=>IO[Vector[Row]] 1) String=>IO[Vector[Row]]

2) Row=>IO[Vector[Results]] 2) Row=>IO[Vector[Results]]

3) Vector[Results] => IO[Unit] 3) Vector[Results] => IO[Unit]

For the sake of example here's a nonsense action I've just made up off the top of my head with the same type:举个例子,这是我刚刚用相同类型的头顶制作的一个无意义的动作:

import cats.effect.IO

val actions: IO[Vector[IO[Vector[IO[Unit]]]]] =
  IO(readLine).flatMap(in => IO(in.toInt)).map { count =>
    (0 until count).toVector.map { _ =>
      IO(System.nanoTime).map { t =>
        (0 until 2).toVector.map { _ =>
          IO(println(t.toString))
        }
      }
    }
  }

Here we're reading a string from standard input, parsing it as an integer, looking at the current time that many times, and printing it twice each time.这里我们从标准输入中读取一个字符串,将它解析为一个整数,多次查看当前时间,并且每次打印两次。

The correct way to flatten this type would be to use sequence to rearrange the layers:展平这种类型的正确方法是使用sequence来重新排列图层:

import cats.implicits._

val program = actions.flatMap(_.sequence).flatMap(_.flatten.sequence_)

(Or something similar—there are lots of reasonable ways you could write this.) (或者类似的东西——你可以用很多合理的方式来写这个。)

This program has type IO[Unit] , and works as we'd expect:该程序的类型为IO[Unit] ,并且按我们的预期工作:

scala> program.unsafeRunSync
// I typed "3" here
8058983807657
8058983807657
8058984254443
8058984254443
8058984270434
8058984270434

Any time you see a deeply nested type involving multiple layers of IO and collections like this, though, it's likely that the best thing to do is to avoid getting in that situation in the first place (usually by using traverse ).但是,每当您看到涉及多层IO和集合的深度嵌套类型时,最好的做法很可能是首先避免陷入这种情况(通常使用traverse )。 In this case we could rewrite our original actions like this:在这种情况下,我们可以像这样重写我们的原始actions

val actions: IO[Unit] =
  IO(readLine).flatMap(in => IO(in.toInt)).flatMap { count =>
    (0 until count).toVector.traverse_ { _ =>
      IO(System.nanoTime).flatMap { t =>
        (0 until 2).toVector.traverse { _ =>
          IO(println(t.toString))
        }
      }
    }
  }

This will work exactly the same way as our program , but we've avoided the nesting by replacing the map s in our original actions with either flatMap or traverse .这将与我们的program完全相同的方式工作,但是我们通过将原始actionsmap替换为flatMaptraverse避免嵌套。 Knowing which you need where is something that you learn through practice, but when you're starting out it's best to go in the smallest steps possible and follow the types.知道你需要什么是你通过练习学到的东西,但是当你开始时,最好尽可能地采取最小的步骤并遵循类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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