10

In Java you can call peek(x -> println(x)) on a Stream and it will perform the action for each element and return the original stream, unlike foreach which is Unit. Is there something similar in Scala, ideally something which works on all Monady types, allowing you to "pass through" the original Monad while performing a side effect action? (Logging, e.g.)

It is of course easily implemented:

def tap[A, U](a: A)(action: (A) => U): A = {
  action(a)
  a
}

but I'm hoping for something a bit more elegant or idiomatic.

3 Answers3

5

One way to solve this is using implicits:

class Tappable[A](a: A) {
  def tap[U](action: (A) => U): A = {
    action(a)
    a
  }
}

implicit def any2Tappable[A](a: A): Tappable[A] = new Tappable[A](a)

Which can then be used natively:

connection.tap(_.connect()) match {
  case c if c.getResponseCode == 304 => None
  ...

Any other answers?

0

I would not make this issue too complex, potentially there is a side-effect, but

  a.map(b => { log(b); b }).map(...) 

type construct would pretty much do it for me.

Manabu Tokunaga
  • 956
  • 10
  • 19
0

If using Cats there is flatTap [1], which returns the same F[A].

FS2 also has evalTap [2].

[1] https://github.com/typelevel/cats/blob/main/core/src/main/scala/cats/FlatMap.scala#L172

[2] https://github.com/typelevel/fs2/blob/main/core/shared/src/main/scala/fs2/Stream.scala#L1044

(The line numbers in the links are likely to change over time...)

flurdy
  • 3,782
  • 29
  • 31