Is Option wrapping a value a good pattern?

2020-05-26 16:50发布

问题:

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?

回答1:

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)
}


回答2:

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)


回答3:

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

val foo = Source fromFile f mkString () toFoo


标签: scala