我最近写了以下Scala:
val f: File = ... // pretend this file came from somewhere
val foo = toFoo(io.Source.fromFile(f).mkString)
我真的不喜欢这种流动的方式。要了解正在发生的事情,您必须从中间的f
开始,向左阅读fromFile
,向右阅读mkString
,再向左阅读toFoo
。啊。
特别是在习惯了序列的功能转换之后,这很难理解。我的下一次尝试看起来像这样:
val foo = Some(f)
.map(io.Source.fromFile)
.map(_.mkString)
.map(toFoo)
.get
我更喜欢这种流程。你可以看到会发生什么这是Option
类的好用吗?还是我在滥用它?有没有更好的模式可以用来实现相同的流程?
答案 0 :(得分:26)
这完全没问题。但是,Scalaz中的方法|>
可以做得更好,如果你不想要所有的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
就个人而言,我倾向于编写大量需要括号的代码,而且在大多数情况下我比运算符更喜欢方法,所以在我的代码中我通常称|>
“使用”,但这是相同的交易:< / p>
f.use(io.Source.fromFile).use(_.mkString).use(toFoo)
在Scala 2.11或更高版本中,您可以使用(略微)较少的语法获得相同的行为并提高性能:
implicit class Piper[A](private val a: A) extends AnyVal {
def |>[B](f: A => B) = f(a)
}
答案 1 :(得分:5)
我对这里给出的其他答案没有任何问题,但您是否考虑将toFoo的名称更改为“流动”更好的内容?我的意思是,toFoo真的闻起来应该在表达式的 right 上,但是如果你把它重命名为其他东西,它也可能适合 left 。
// 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)
答案 2 :(得分:4)
您通过pimp my library模式将toFoo
添加到String
。然后它变成:
val foo = Source fromFile f mkString () toFoo