Традиционно tap
подобные функции позволяют вам наблюдать (или заглядывать) за элементами в потоке, но отбрасывают результат эффекта наблюдения. Например, в fs2 вы можете увидеть подпись для evalTap
:
def evalTap[F2[x] >: F[x]](f: (O) ⇒ F2[_])(implicit arg0: Functor[F2]): Stream[F2, O]
Обратите внимание, что f
является функцией из O => F2[_]
, что означает «вы берете значение O
и возвращаете тип эффекта F2
, для которого существует Functor», но это не влияет на возвращаемый тип потока, который по-прежнему 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]
, но мы можем сразу же map
каждый такой элемент на следующем шаге конвейера, так как это не повлияло на тип результата потока.
Я не смог найти 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
, напрямую влияет на нижестоящие комбинаторы.
evalMap
— это псевдоним для flatMap
, который позволяет вам опустить перенос типа Stream
и обычно реализуется в терминах flatMap
:
def evalMap[F2[x] >: F[x], O2](f: O => F2[O2]): Stream[F2, O2] =
flatMap(o => Stream.eval(f(o)))
Что несколько удобнее в использовании.
person
Yuval Itzchakov
schedule
12.11.2019