2

Is there some sort of 'tee' operation on Option in Scala's standard library available? The best I could find is foreach, however its return type is Unit, therefore it cannot be chained.

This is what I am looking for: given an Option instance, perform some operation with side effects on its value if the option is not empty (Some[A]), otherwise do nothing; return the option in any case.

I have a custom implementation using an implicit class, but I am wondering whether there is a more common way to do this without implicit conversion:

object OptionExtensions {
  implicit class TeeableOption[A](value: Option[A]) {
    def tee(action: A => Unit): Option[A] = {
      value foreach action
      value
    }
  }
}

Example code:

import OptionExtensions._

val option: Option[Int] = Some(42)
option.tee(println).foreach(println) // will print 42 twice

val another: Option[Int] = None
another.tee(println).foreach(println) // does nothing

Any suggestions?

proskor
  • 1,382
  • 9
  • 21
  • 1
    I don't presume you'll see anything that implicitly injects side effects into a function in the standard library. – Yuval Itzchakov Feb 09 '17 at 14:12
  • 2
    Side-effects and functional-programming are not friends. – sarveshseri Feb 09 '17 at 14:18
  • There isn't. http://stackoverflow.com/questions/9671620/how-to-keep-return-value-when-logging-in-scala http://stackoverflow.com/questions/16742060/equivalent-to-rubys-tap-method-in-scala – Michael Zajac Feb 09 '17 at 14:40
  • You can simply put your side-effecting operation into the normal map/flatmap methods. – puhlen Feb 09 '17 at 15:06
  • I think your solution is the best way to do what you want to do. I don't think there's anything like that in the standard library. – Bradley Kaiser Feb 09 '17 at 15:14
  • No you can't, @puhlen. If you call `option.map(println)` you end up with an `Option[Unit]` and he wants an `Option[Int]` still. – Karl Bielefeldt Feb 09 '17 at 16:16
  • @KarlBielefeldt `option.map{x => println(x); x}` prints x and gives you `Option[Int]` – puhlen Feb 09 '17 at 16:21
  • It's simply not necessary, as you can define this as a function, like this: `def tee[A,B<:Iterable[A],U](b:B)(f:A=>U):B = {b foreach f; b}` -- and then use it like this: `tee(option)(println)` -- But then again, why? That's what the semicolon is there for, is it not? – Madoc Feb 09 '17 at 16:26
  • @puhlen I was thinking simpler but fair enough. – Karl Bielefeldt Feb 09 '17 at 16:48
  • 2
    According to [this SO answer](http://stackoverflow.com/questions/23231509/what-is-the-added-value-of-the-kestrel-functional-programming-design-pattern-s) it's called "kestrel" in ruby code. (Disclaimer: I don't know ruby.) – jwvh Feb 09 '17 at 18:40

3 Answers3

3

In order to avoid implicit conversion, instead of using method chaining you can use function composition with k-combinator. k-combinator gives you an idiomatic way to communicate the fact that you are going to perform a side effect.

Here is a short example:

object KCombinator {
  def tap[A](a: A)(action: A => Any): A = {
    action(a)
    a
  }
}

import KCombinator._

val func = ((_: Option[Int]).getOrElse(0))
  .andThen(tap(_)(println))
  .andThen(_ + 3)
  .andThen(tap(_)(println))

If we call our func with an argument of Option(3) the result will be an Int with the value of 6 and this is how the console will look like:

3

6

Community
  • 1
  • 1
NetanelRabinowitz
  • 1,534
  • 2
  • 14
  • 26
  • ... except that it doesn't work. :) Calling func(None) results in 0 and 3 being printed to stdout, which is not what is required. I accepted the answer nonetheless, since the idea was the right one and using `def tap[A](action: A => Any)(value: A): Option[A] = { action(value); Some(value) }` does the trick: `option.flatMap(tap(println)).foreach(println)` prints 42 twice if option is not empty; otherwise it does nothing. – proskor Feb 11 '17 at 18:35
  • It's printing 0 and 3 only because I called getOrElse(0). Returning Some(value) is wrong because you will end up with a nested Option of Option of... you get the idea which is not what you want. I don't think it make a lot of sense to short circuit inside tap itself. You can pattern math inside the action of tap to get the exact same behaviour that you are looking for. – NetanelRabinowitz Feb 11 '17 at 22:33
  • True, but I think it depends on the way the composition is done. In this sense I don't think that returning `Some(value)` is inherently *wrong*, only when using `andThen`. – proskor Feb 12 '17 at 14:04
1

There is not an existing way to accomplish this in the standard library, because side effects are minimized and isolated in functional programming. Depending on what your actual goals are, there are a couple different ways to idiomatically accomplish your task.

In the case of doing a lot of println commands, instead of sprinkling them throughout your algorithm, you would typically gather them in a collection, then do one foreach println at the end. This minimizes the side effects to the smallest possible impact. That goes with any other side effect. Try to find a way to squeeze it into the smallest possible space.

If you are trying to chain a series of "actions," you should look into futures. Futures basically treat an action as a value, and provide a lot of useful functions to work with them.

Karl Bielefeldt
  • 47,314
  • 10
  • 60
  • 94
1

Simply use map, and make your side effecting functions conform to action: A => A rather than action: A => Unit.

 def tprintln[A](a: A): A = {
    println(a)
    a
 }
 another.map(tprintln).foreach(println) 
Jon Anderson
  • 696
  • 4
  • 9