Scala flatMap、flatTap、evalMap和evalTap之间的差异

Scala flatMap、flatTap、evalMap和evalTap之间的差异,scala,functional-programming,scala-cats,fs2,Scala,Functional Programming,Scala Cats,Fs2,在功能流的Scala fs2库中: 我试图理解flatMap、flatTap、evalMap和evalTap之间的区别。它们似乎都执行相同的操作,即流值的转换 它们的区别是什么以及何时应该使用它们?传统上,点击类似的函数允许您观察(或窥视)流中的元素,但放弃观察效果的结果。例如,在fs2中,您可以看到evalTap的签名是: def evalTap[F2[x] >: F[x]](f: (O) ⇒ F2[_])(implicit arg0: Functor[F2]): Stream[F2,

在功能流的Scala fs2库中:

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


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

传统上,
点击类似的函数允许您观察(或窥视)流中的元素,但放弃观察效果的结果。例如,在fs2中,您可以看到
evalTap
的签名是:

def evalTap[F2[x] >: F[x]](f: (O) ⇒ F2[_])(implicit arg0: Functor[F2]): Stream[F2, O]
请注意,
f
是来自
O=>F2[\u]
的函数,意思是“您获取一个
O
值并返回一个存在函子的效果类型
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)
  }
}
这将产生
13

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

我找不到
flatTap
,但我认为它们在fs2中基本相同()

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

def flatMap[F2[x] >: F[x], O2](f: O => Stream[F2, O2]): Stream[F2, O2] =
请注意,执行
f
的结果是
O2
,这与
evalTap
不同,它也在返回类型中编码。如果我们采用与上述相同的示例:

fs2
  .Stream(1, 2, 3)
  .covary[IO]
  .flatMap(i => fs2.Stream(IO(println(i))))
  .map(_ + 1)
  .compile
  .drain
  .as(ExitCode.Success)
这将不再编译,因为
flatMap
返回
流[IO,Unit]
,这意味着
println
的执行以及它返回
Unit
的事实直接影响下游组合器

evalMap
flatMap
的别名,它允许您省略
类型的包装,通常根据
flatMap
实现:

def evalMap[F2[x] >: F[x], O2](f: O => F2[O2]): Stream[F2, O2] =
  flatMap(o => Stream.eval(f(o)))
使用起来更方便一点

您可能会发现了解flatTap与flatMap的不同之处非常有用:)