2

我正在尝试在 scala 中使用猫效果,并且在世界尽头我有以下类型: IO[Vector[IO[Vector[IO[Unit]]]]]

我发现只有一种方法可以运行它:

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

但它看起来很丑陋。请帮助我了解如何执行复杂的副作用。

更新:

1)首先IO- 我打开 excel 文件并获取行向量,即IO[Vector[Row]].

2)第二IO- 我对每一行的数据库执行查询。我不能用Vector[_],组成 IO monad

3)第三- 我使用DBIO为 excel 中的每一行创建 PDF 文件。Vector[Results]

所以我有这样的功能:

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

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

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

4

1 回答 1

3

举例来说,这是我刚刚用相同类型编造出来的无意义动作:

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))
        }
      }
    }
  }

这里我们从标准输入读取一个字符串,将其解析为一个整数,多次查看当前时间,每次打印两次。

展平这种类型的正确方法是使用sequence重新排列图层:

import cats.implicits._

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

(或者类似的东西——你可以用很多合理的方式来写这个。)

该程序具有 type IO[Unit],并且按我们预期的方式工作:

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

但是,任何时候你看到一个涉及多层IO和这样的集合的深度嵌套类型时,最好的办法可能是首先避免陷入这种情况(通常通过使用traverse)。在这种情况下,我们可以像这样重写我们的原始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))
        }
      }
    }
  }

这将与我们的工作方式完全相同program,但我们通过将map原始中的 s替换actionsflatMapor来避免嵌套traverse。知道你在哪里需要哪个是你通过练习学到的东西,但是当你开始时,最好尽可能地走最小的步骤并遵循类型。

于 2020-01-13T10:02:50.723 回答