scala - flatMap、flatTap、evalMap 和 evalTap 之间的区别

标签 scala functional-programming scala-cats fs2

在功能流的 Scala fs2 库中:

我试图理解 flatMapflatTapevalMapevalTap 之间的区别。它们似乎都执行相同的操作,即流值的转换。

它们有什么区别以及何时应该使用它们?

最佳答案

传统上,tap 之类的函数允许您观察(或查看)流中的元素,但会丢弃观察效果的结果。例如,在 fs2 中,您可以看到 evalTap 的签名是:

def evalTap[F2[x] >: F[x]](f: (O) ⇒ F2[_])(implicit arg0: Functor[F2]): Stream[F2, O]

请注意 f 是来自 O => F2[_] 的函数,意思是“您采用一个 O 值并返回一个效果输入存在 Functor 的 F2”,但它不会影响流的返回类型,仍然是 O

例如,如果我们想将流的元素发送到控制台,我们可以这样做:

import cats.effect.{ExitCode, IO, IOApp}
import cats.implicits._

object Test extends IOApp {
  override def run(args: List[String]): IO[ExitCode] = {
    fs2
      .Stream(1, 2, 3)
      .covary[IO]
      .evalTap(i => IO(println(i)))
      .map(_ + 1)
      .compile
      .drain
      .as(ExitCode.Success)
  }
}

这将产生1 2 3

您可以看到我们使用 evalTap 将流的每个元素发送到控制台,其中我们有一个 IO[Unit] 类型的效果,但我们可以立即在管道的下一步中映射每个此类元素,因为它不会影响流的结果类型。

我找不到 flatTap 但我认为它们在 fs2 中通常是相同的 ( https://github.com/functional-streams-for-scala/fs2/issues/1177 )

另一方面,像 flatMap 这样的函数确实会导致流的返回类型发生变化。我们可以看到签名:

def flatMap[F2[x] >: F[x], O2](f: O => Stream[F2, O2]): Stream[F2, O2] =

请注意,与 evalTap 不同的是,执行 f 的结果是 O2,它也编码在返回类型中。如果我们采用与上面相同的示例:

fs2
  .Stream(1, 2, 3)
  .covary[IO]
  .flatMap(i => fs2.Stream(IO(println(i))))
  .map(_ + 1)
  .compile
  .drain
  .as(ExitCode.Success)

这将不再编译,因为 flatMap 返回一个 Stream[IO, Unit],这意味着 println 的执行和事实它返回 Unit 直接影响下游组合器。

evalMapflatMap 的别名,它允许您省略 Stream 类型的包装,并且通常按照 flatMap:

def evalMap[F2[x] >: F[x], O2](f: O => F2[O2]): Stream[F2, O2] =
  flatMap(o => Stream.eval(f(o)))

使用起来比较方便一些。

关于scala - flatMap、flatTap、evalMap 和 evalTap 之间的区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58821246/

相关文章:

java - Scala:为什么 java.lang.IllegalArgumentException:无法将给定对象格式化为日期?

vector - 如何以函数式方式将字符串向量转换为整数向量?

java - 将相同的对象添加到上下文和列表中

scala - 猫的 NonEmptyList 与 scala stdlib::

scala - 将 OOP "decorator"重构为释放 monad 结构

scala - 获取Scala REPL历史记录(从sbt控制台)

java - 将 Java 库导入 Scala sbt 项目

scala - 如何使用 2d GUI (nifty-gui) 正确渲染 3d 对象?

function - 为什么我收到此错误消息 “UnboundLocalError: local variable ' sigma_opt在分配前被引用”

scala - Dotty 将如何改变 Scala 中的纯函数式编程?