11

I recently wrote the following bit of Scala:

val f: File = ... // pretend this file came from somewhere
val foo = toFoo(io.Source.fromFile(f).mkString)

I really didn't like the way this flowed. To understand what's happening, you have to start with f in the middle, read left to fromFile, read right to mkString, read left again to toFoo. Ugh.

Especially after getting used to functional transformations of sequences, this is difficult to read. My next attempt looks like this:

val foo = Some(f)
  .map(io.Source.fromFile)
  .map(_.mkString)
  .map(toFoo)
  .get

I like the flow of this much better. You can see what happens Is this a good use of the Option class? Or am I abusing it? Is there a better pattern that I can use to achieve the same flow?

leedm777
  • 23,444
  • 10
  • 58
  • 87

3 Answers3

27

This is perfectly okay. However, there is a method |> in Scalaz that does one better, and you can create it yourself if you don't want all of Scalaz:

class Piper[A](a: A) { def |>[B](f: A => B) = f(a) }
implicit def pipe_everything[A](a: A) = new Piper(a)

f |> io.Source.fromFile |> {_.mkString} |> toFoo

Personally, I tend to write a lot of code that requires parentheses and I like methods better than operators in most cases, so in my code I normally call |> "use", but it's the same deal:

f.use(io.Source.fromFile).use(_.mkString).use(toFoo)

In Scala 2.11 or later, you can get the same behavior and improved performance with (slightly) less syntax:

implicit class Piper[A](private val a: A) extends AnyVal {
  def |>[B](f: A => B) = f(a)
}
Rex Kerr
  • 166,841
  • 26
  • 322
  • 407
  • it's a snippet that I drop into every project I write! Should be in the standard library really... – Kevin Wright Feb 03 '11 at 18:23
  • @Kevin - Agreed, it should be in the standard library. I also have it in all my projects since I have a standard library that I drop into every project I write, and this is one of the things in it. – Rex Kerr Feb 03 '11 at 20:15
  • An updated version for the current generation: `implicit class Piper[A](a: A) { def |>[B](f: A=>B) = f(a) } extends AnyVal` – Kevin Wright Jan 14 '16 at 13:56
  • @KevinWright - you missed a `private val`, but it's a good point and I've added it to my answer. – Rex Kerr Jan 14 '16 at 19:43
5

I have no problems with the other answers given here, but did you consider changing the name of toFoo into something that 'flows' better? I mean, toFoo really smells like something that should be on the right of an expression, but if you rename it into something else, it might fit on the left as well.

// toFoo, as defined by you
val foo = toFoo(io.Source.fromFile(f).mkString)
// Same function, different name
val foo = createFooFrom(io.Source.fromFile(f).mkString)
Wilfred Springer
  • 10,869
  • 4
  • 55
  • 69
4

You add toFoo to String through the pimp my library pattern. Then it becomes:

val foo = Source fromFile f mkString () toFoo
Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681