首页 > 解决方案 > 为什么 Scala Option.tapEach 返回 Iterable,而不是 Option?

问题描述

状态的scaladoc返回:与此相同的逻辑集合”,正如以&命名的操作所期望的那样。但是,它不返回 a而是由 a 支持:Option.tapEachtapforeachOptionIterableList

scala> import scala.util.chaining._

scala> Option(5).tap(_.foreach(_ => ()))
val res0: Option[Int] = Some(5)

scala> Option(5).tapEach(_ => ())
val res1: Iterable[Int] = List(5)

(已针对 Scala 2.13.5 和 3.0.0-RC1 进行了验证)

是否有充分的理由返回Iterable而不是Option,或者这只是被忽略了(最终可能会被修复)?

标签: scalascala-collections

解决方案


正如Make Option extend IterableOnce #8038中的讨论所表明的那样,似乎是否Option将完整集合视为一个蠕虫罐头。我认为相关评论

所以它绝对可以是 aIterableOnce因为你可以获得一个零到一个元素的迭代器。但这不可能是因为如果不丢弃数据Iterable就无法实现。fromSpecific(c: IterableOnce[A]): K

然而在其定义中tapEach使用fromSpecific

override def tapEach[U](f: A => U): C = fromSpecific(new View.Map(this, { (a: A) => f(a); a })

所以要记住的关键是Option因为 Scala 2.13 是一个IterableOnce但不是一个完整的Iterable. IterableOnce与 相比更小Iterable,因此如果需要它的功能,则根据文档Iterable通过隐式转换提供

该成员是通过隐式转换添加的,Option[A] 由in 中Iterable[A]的方法执行。option2Iterablescala.Option

那是

option2iterable(Option(5)).tapEach(_ => ())

因此Iterable[Int]返回类型。

还要考虑以下注释

这里的许多方法与 Traversable 层次结构中的方法是重复的,但它们之所以重复是有原因的:隐式转换往往会在可以保留选项的情况下留下一个 Iterable。

所以贡献者必须在 Option 中烘焙一个专门的版本来保留类型,或者我们可以提供我们自己的专门的扩展实现,比如

scala> implicit class OptionTapOps[A](v: Option[A]) {
     |   def tapEach[B](f: A => B): Option[A] = { v.foreach(f); v }
     | }
class OptionTapOps

scala> Option(5).tapEach(_ => ())
val res11: Option[Int] = Some(5)

推荐阅读