1
def foo(l: List[Int]) =
  List(1,2,3)
    .map(_ + 1)               // A
    .map(x => {println(x);x}) // B
    .map(_ + 2)               // C
    .map(x => {println(x);x}) // D
    .map(_ + 3)

The intent is to print or log to a file after step A and C and this is one of the implementations. Is this a good practice?

Or break it into multiple temp variables,

def foo(l: List[Int]) = {
  val nums = List(1,2,3).map(_ + 1)
  nums.foreach(println)
  val nums2 = nums.map(_ + 2)
  nums2.foreach(println)
  nums2.map(_ + 3)

}

Is there any better or straightforward way todo? Thanks

Update 1

Let me further clarify my intent. The idea is to move side effects println(...) out of map(...) function but yet I want to print the result along the way. I don't see the need to optimize line B and D.

Community
  • 1
  • 1
thlim
  • 2,908
  • 3
  • 34
  • 57
  • 1
    You might be thinking of something like the Kestrel Pattern, mentioned [here](https://stackoverflow.com/questions/41815793/filter-and-report-multiple-predicates/41816281#41816281) and [here](https://stackoverflow.com/questions/23231509/what-is-the-added-value-of-the-kestrel-functional-programming-design-pattern-s). – jwvh Mar 13 '18 at 06:34

3 Answers3

1

One way would be to create a class which inherits from the List class and overrides the .map method.

Problem is List can't be inherited as it's sealed.

A solution would thus be to wrap the list in a new class and write a .map method which would both call the original List.map and print the result of the map for each mapped element:

class PrintList[T](l: List[T]) {

  def map[B](f: T => B): PrintList[B] = {
    new PrintList[B](l.map { e =>
      val v = f(e); println(v); v
    })
  }

  def toList(): List[T] = l

  override def toString(): String = "Print" + l.toString
}

Applied this way:

new PrintList(List(1, 2, 3)).map(_ + 1).map(_ + 2)

prints:

2
3
4
4
5
6

and returns:

println(new PrintList(List(1, 2, 3)).map(_ + 1).map(_ + 2))
> PrintList(4, 5, 6)
println(new PrintList(List(1, 2, 3)).map(_ + 1).map(_ + 2).toList)
> List(4, 5, 6)
Xavier Guihot
  • 54,987
  • 21
  • 291
  • 190
  • yes, this could potentially be the answer but I am uncomfortable to use wrapper class right now. – thlim Mar 10 '18 at 11:07
0

Maybe you refactor function + logging to a new function:

def loggedFun (x: Int)(f: Int => Int) : Int = { val y = f(x); println (y); y }

which could be used like this:

List(1,2,3).map(loggedFun (_)(_ + 1)).map(loggedFun (_)(_ + 2)).map(_ + 3)

and further be generalized for the Type (Int → A).

def loggedFun[A] (x: A)(f: A => A) : A = { val y = f(x); println (y); y }

I'm pretty sure, there is a name for such functions from A to A in category theory, but I don't know which; I'm sorry.

user unknown
  • 35,537
  • 11
  • 75
  • 121
  • I thought something along that line after I posted my question. I wonder why List does not provide something like `foreach(...)` and returns the list unchanged. – thlim Mar 10 '18 at 11:09
0

The non trivial answer to this trivial question is Writer monad. A good example of the said pattern is here, http://blog.tmorris.net/posts/the-writer-monad-using-scala-example/

thlim
  • 2,908
  • 3
  • 34
  • 57