为什么 Scala Option.tapEach return Iterable,而不是 Option?

Why does Scala Option.tapEach return Iterable, not Option?

Option.tapEachscaladoc 声明“returns:与此 相同的逻辑集合”正如对名为在 tap & foreach 之后。但是,它不是 return Option,而是 IterableList:

支持
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 进行验证)

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

似乎 Option 是否被认为是一个完整的集合有点像 Make Option extend IterableOnce #8038. I think the relevant comment 上的讨论所表明的蠕虫是

So it can definitely be a IterableOnce because you can get an iterator of zero to one elements. But it can't be a Iterable because you you can't implement fromSpecific(c: IterableOnce[A]): K without throwing away data.

然而 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 而不是完整的 IterableIterableOnceIterable 小,因此如果需要 Iterable 的功能,则根据 docs

通过隐式转换提供

This member is added by an implicit conversion from Option[A] to Iterable[A] performed by method option2Iterable in scala.Option.

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

因此 Iterable[Int] return 类型。

还要考虑以下note

Many of the methods in here are duplicative with those in the Traversable hierarchy, but they are duplicated for a reason: the implicit conversion tends to leave one with an Iterable in situations where one could have retained an 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)