3

What is the idiomatic way of printing (or doing whatever thing I need to do) and returning value in Scala? For example,

Seq(1,2,3)
  .map(_ * 2)
  .xxx(println) // Here I want to print the intermediate sequence
  .foldLeft(0)(_ + _)

One way I can think of is using implicit but I don't really like to monkey patch standard library myself.

Note

In Ruby we can use Object#tap

[1,2,3]
  .map { |i| i * 2 }
  .tap { |i| puts i }
  .reduce(0) { |x, i| x += i }
Shouichi
  • 1,090
  • 1
  • 10
  • 25
  • Just a remark: Implicits are much safer than monkey-patching, since they are only used if you actually import the implicit, and only for methods that do not exist on the objects themselves. They do not change the behavior of the standard library in other places. – Rüdiger Klaehn Nov 09 '15 at 09:04
  • 2
    `def tap[T](x: T) = {println(x); x}`. usage `List(1,2,3).map(_ + 1).map(tap).map(_ + 2)` still monkey patch, but one-liner though – dk14 Nov 09 '15 at 09:06
  • 3
    To add on dk14 answer, you may wanna look at [Kestrel combinators](http://stackoverflow.com/questions/9671620/how-to-keep-return-value-when-logging-in-scala), also this is probably a duplicate. – Ende Neu Nov 09 '15 at 09:13
  • @EndeNeu: Now that you mention it, I vaguely remember posting that exact same code before. – Jörg W Mittag Nov 09 '15 at 09:19
  • @EndeNeu: [Wrong language](http://stackoverflow.com/a/3106118/2988). – Jörg W Mittag Nov 09 '15 at 09:26

2 Answers2

7

Object#tap in Ruby is basically a variant of the K combinator. I don't believe there is an implementation in the Scala standard library, but it is easy to add your own:

implicit class TapExtension[T](o: => T) {
  def tap(f: T => Unit) = { f(o); o }
}

Note: This is an implicit conversion, it is not monkey-patching.

Then, you can use it like so:

Seq(1,2,3)
  .map(_ * 2)
  .tap(println)
  .foldLeft(0)(_ + _)
Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • As is, this does not work as expected. The second overload of `tap` is called in your example, and`println` is just called without any argument (resulting in only a newline being printed). You'd need to either do `.tap(println(_))` or remove the second overload altogether. – Régis Jean-Gilles Nov 09 '15 at 09:22
  • You're right, thanks. I actually wrote this over 5 years ago, and it was literally the first Scala code I had ever written. (I didn't use an implicit class then, obviously.) I never actually used it, otherwise I would have noticed that long ago :-D – Jörg W Mittag Nov 09 '15 at 09:25
  • Thanks! It's sad that there' no standard way of doing this :( – Shouichi Nov 09 '15 at 10:02
1

For completion here is a version of @jörg-w-mittag's function that prints not the entire seq, but each element. This should also work lazily:

implicit class IterableTapExtension[T[A] <: Iterable[A], A](o: T[A]) {
  def tap(f: A => Unit) = { o.map { v => f(v); v } }
}

Seq(1,2,3).map(_ * 2).tap(println).foldLeft(0)(_ + _)

(1 #:: 2 #:: 3 #:: Stream.empty).tap(println).take(2).toList
thoredge
  • 12,237
  • 1
  • 40
  • 55