Add .tap to the standard library?

As a Scala programmer, I often use this following pattern:

extension [A](a: A)
  def tap: A = {
    println(a); a

  def tap(f: A => Unit): A = {
    f(a); a

It’s very useful for debugging, especially with long chains of operations such as:


Being able to simply insert a .tap into a chain of operations is very quick and clean


as opposed to having to assign part of the expression to a val, like so:

val part1 = getResults



Basically it allows you to see the value of any expression in your program at any point, without having to do a lot of refactoring.

I find myself having to redefine this for each new project but also since it’s for debugging I don’t necessarily want to commit it either; maybe the standard library is the right place? I think many others use this pattern also.

And you might also say, “Shouldn’t you be using a debugger anyway?”, but in a debugger you can’t see the result of part of an expression, only the whole expression, so .tap works very nicely in those situations, which in Scala, happen quite often.

1 Like

This feels like something that can become a small library with zero dependencies, if this feature doesn’t exist in one the existing logging libraries.
I think it’s better in a library because I can think of several features (positioning, argument name via reflection, etc.) that people may want to have and something that can be easily evolved, unlike the Scala standard library.


Presumably, it will be inline eventually. For those stuck on Scala 2, don’t neglect -opt.

➜  ~ scala -J--enable-preview '-opt:inline:**' -J--add-exports -Jjdk.jdeps/ -nobootcp
Welcome to Scala 2.13.13 (OpenJDK 64-Bit Server VM, Java 21.0.2).
Type in expressions for evaluation. Or try :help.

scala> import util.chaining._
import util.chaining._

scala> def f(s: String) = s.tap(println).length
def f(s: String): Int

scala> :javap #f
  public int f(java.lang.String);
    descriptor: (Ljava/lang/String;)I
    flags: (0x0001) ACC_PUBLIC
      stack=2, locals=2, args_size=2
         0: getstatic     #26                 // Field scala/util/ChainingOps$.MODULE$:Lscala/util/ChainingOps$;
         3: pop
         4: getstatic     #29                 // Field scala/util/package$chaining$.MODULE$:Lscala/util/package$chaining$;
         7: pop
         8: getstatic     #34                 // Field scala/Console$.MODULE$:Lscala/Console$;
        11: aload_1
        12: invokevirtual #38                 // Method scala/Console$.println:(Ljava/lang/Object;)V
        15: aload_1
        16: invokevirtual #44                 // Method java/lang/String.length:()I
        19: ireturn
        line 1: 0
        line 1: 8
        line 1: 15
        Start  Length  Slot  Name   Signature
            0      20     0  this   L$line7/$read$$iw;
            0      20     1     s   Ljava/lang/String;
      Name                           Flags
      s                              final


It’s awkward to surgically inline from the util.chaining package object.

1 Like

90% of the times when I want to use tap it’s temporary. Adding an import and having to clean it up afterwards is usually more annoying than introducing a local val… It should really be in scala or Predef IMO (“part of the language” in a sense).


.tap exists as sjrd pointed out.
Also isn’t there .tapEach for collections?

1 Like

Ah I didn’t realize that that already existed, thanks!


The import is quite annoying indeed, and results in people just not knowing it exists.

I still add it all the time as tap and pipe are just too practical and readable.


Thanks for the reminder, I forgot to say, if you’re stuck on Scala 2, don’t forget


Probably you already have that for scala.annotation, so just stick chaining on that list.


Thanks for sharing these insights mate as I found it very much useful and informative.

1 Like