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?
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)
}
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)
You add toFoo
to String
through the pimp my library pattern. Then it becomes:
val foo = Source fromFile f mkString () toFoo