Scala 标准库中的
Option
是否有某种“tee”操作?我能找到的最好的是 foreach
,但是它的返回类型是 Unit
,因此它不能被链接。
这就是我正在寻找的:给定一个
Option
实例,如果选项不为空(Some[A]
),则执行一些对其值有副作用的操作,否则不执行任何操作;无论如何都要返回选项。
我有一个使用隐式类的自定义实现,但我想知道是否有一种更常见的方法可以在不进行隐式转换的情况下执行此操作:
object OptionExtensions {
implicit class TeeableOption[A](value: Option[A]) {
def tee(action: A => Unit): Option[A] = {
value foreach action
value
}
}
}
示例代码:
import OptionExtensions._
val option: Option[Int] = Some(42)
option.tee(println).foreach(println) // will print 42 twice
val another: Option[Int] = None
another.tee(println).foreach(println) // does nothing
有什么建议吗?
为了避免隐式转换,您可以使用带有 k-combinator 的函数组合,而不是使用方法链。 k-combinator 为您提供了一种惯用的方式来传达您将要执行副作用的事实。
这是一个简短的例子:
object KCombinator {
def tap[A](a: A)(action: A => Any): A = {
action(a)
a
}
}
import KCombinator._
val func = ((_: Option[Int]).getOrElse(0))
.andThen(tap(_)(println))
.andThen(_ + 3)
.andThen(tap(_)(println))
如果我们使用参数
Option(3)
调用 func,结果将是一个值为 6
的 Int
这就是控制台的样子:
3
6
标准库中没有现有的方法可以实现这一点,因为在函数式编程中副作用被最小化和隔离。根据您的实际目标,有几种不同的方法可以惯用地完成您的任务。
在执行大量
println
命令的情况下,您通常会将它们收集在一个集合中,而不是将它们散布在整个算法中,然后在最后执行一个 foreach println
。这可以最大限度地减少副作用,将影响降到最低。这与任何其他副作用有关。尝试找到一种方法将其压缩到尽可能小的空间中。
如果您尝试链接一系列“行动”,您应该研究期货。 Futures 基本上将一个动作视为一个值,并提供许多有用的函数来使用它们。
简单地使用map,并使你的副作用函数符合
action: A => A
而不是action: A => Unit
。
def tprintln[A](a: A): A = {
println(a)
a
}
another.map(tprintln).foreach(println)
从 Scala 2.13 开始,我们可以使用 scala.util.ChainingOps#tap。
示例:
import scala.util.chaining._
val result = Option(1)
.tap(x => println(s"Input: $x"))
.map(_ + 1)
.tap(x => println(s"Output: $x"))
结果:
Input: Some(1)
Output: Some(2)
val result: Option[Int] = Some(2)