Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is Option wrapping a value a good pattern?

Tags:

scala

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?

like image 531
leedm777 Avatar asked Feb 03 '11 17:02

leedm777


2 Answers

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)
}
like image 177
Rex Kerr Avatar answered Sep 23 '22 17:09

Rex Kerr


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)
like image 37
Wilfred Springer Avatar answered Sep 22 '22 17:09

Wilfred Springer