管道if的惯用解决方案



在我们使用Scala 2.12的项目中,我们从Scala 2.13复制了ChainingOps。我们经常使用它,我们增加了一个方法:pipeIf-当谓词是false时,我们传递不变的值。

def pipe[B](f: A => B): B = f(self)
def pipeIf(cond: Boolean)(f: A => A): A = if (cond) f(self) else self

我现在正在尝试移植到Scala 2.13,这个pipeIf让我有点担心。这个包中的所有其他方法(pipetap)已经包含在Scala 2.13库中。我宁愿完全移除我们的包装,但是这个pipeIf留在那里,像一个疼痛的拇指。

处理"有条件的管道"的惯用方法是什么?它并没有那么糟糕,但一个缺点是它阻止使用下划线形式。使用详细的if/else形式是正常的,或者有其他方法吗?

val newState = state.pipeIf(dt != 0)(_.simulate(dt))
val newState = state.pipe(state => if (dt != 0) state.simulate(dt) else state )

为什么不把它放在一个普遍可用的隐式类中呢?

implicit class Kestrel[A](private val repr: A) {
def pipeIf(cond: Boolean)(f: A=>A): A =
if (cond) f(repr) else repr
def pipeIf(cond: A=>Boolean)(f: A=>A): A =
if (cond(repr)) f(repr) else repr
}
5.pipeIf(_ < 8)(_ * 2)  //res0: Int = 10
5.pipeIf(false)(_ * 2)  //res1: Int = 5

您提到了"有条件的管道"。也许我们可以将其抽象为"条件函数应用"。由于没有更好的术语,我选择了branch

object Branch {
@inline def branch[A, B](cond: A => Boolean, f: A => B, fElse: A => B)(a: A): B = if (cond(a)) f(a) else fElse(a)
@inline def branch[A, B](cond: Boolean, f: A => B, fElse: A => B)(a: A): B = if (cond) f(a) else fElse(a)
}

那么你可以这样做:

val newState = state.pipe(branch(dt != 0, _.simulate(dt), identity))

对于tap我也创建了branchU:

object Branch {
//...
@inline def branchU[A](cond: A => Boolean, f: A => Any)(a: A): Unit = if (cond(a)) f(a) else ()
@inline def branchU[A](cond: Boolean, f: A => Any)(a: A): Unit = if (cond) f(a) else ()
}

允许:

state.tap(branchU(dt != 0, println))

所有这些都是为了保持对下划线形式的访问。

最新更新