为什么选项不可穿越?

时间:2012-01-03 22:22:38

标签: scala collections

Option不是Traversable是否有理由?

在Scala 2.9中,Seq(Set(1,3,2),Seq(4),Option(5)).flatten无法编译,只是让它实现Traversable特征接缝对我合理。如果不是这样,那么我必须看到一些不允许的东西。它是什么?

PS:在尝试理解时,我实现了很多可编译的东西,比如:

scala> Seq(Set(1,3,2),Seq(4),Map("one"->1, 2->"two")).flatten
res1: Seq[Any] = List(1, 3, 2, 4, (one,1), (2,two))

PS2:我知道我可以写:Seq(Set(1,3,2),Seq(4),Option(5).toSeq).flatten或其他丑陋的东西。

PS3:上个月有接缝可以让Option看起来更像Traversable而不执行它:commitanother commit

5 个答案:

答案 0 :(得分:5)

flatMap返回Option而不是Traversable可能会遇到挑战。虽然这早于2.8 CanBuildFrom机制。

问题是asked之前曾在邮件列表上发过一次,但没有得到答复。

以下是插图:

sealed trait OptionX[+A] extends Traversable[A] {
  def foreach[U](f: (A) => U): Unit = if (!isEmpty) f(get)
  def get: A
  def isDefined: Boolean
  def getOrElse[B >: A](default: => B): B
}

case class SomeX[+A](a: A) extends OptionX[A] {
  override def isEmpty = false
  def get = a
  def isDefined = true
  def getOrElse[B >: A](default: => B) = a
}

case object NoneX extends OptionX[Nothing] {
  override def isEmpty = true
  def get = sys.error("none")
  def isDefined = false
  def getOrElse[B](default: => B) = default
}

object O extends App {
  val s: OptionX[Int] = SomeX(1)
  val n: OptionX[Int] = NoneX
  s.foreach(i => println("some " + i))
  n.foreach(i => println("should not print " + i))
  println(s.map(_ + "!"))
}

最后一行返回List("1!")而不是Option。可能有人可以提出CanBuildFrom来产生SomeX("1!")。我的尝试没有成功:

object OptionX {
  implicit def canBuildFrom[Elem] = new CanBuildFrom[Traversable[_], Elem, OptionX[Elem]] {
    def builder() = new Builder[Elem, OptionX[Elem]] {
      var current: OptionX[Elem] = NoneX
      def +=(elem: Elem): this.type = {
        if (current.isDefined) sys.error("already defined")
        else current = SomeX(elem)
        this
      }
      def clear() { current = NoneX }
      def result(): OptionX[Elem] = current
    }
    def apply() = builder()
    def apply(from: Traversable[_]) = builder()
  }
}

我需要明确地传递隐含的内容:

scala> import o._
import o._

scala> val s: OptionX[Int] = SomeX(1)
s: o.OptionX[Int] = SomeX(1)

scala> s.map(_+1)(OptionX.canBuildFrom[Int])
res1: o.OptionX[Int] = SomeX(2)

scala> s.map(_+1)
res2: Traversable[Int] = List(2)

修改

所以我能够解决这个问题,让SomeX(1).map(1+)通过OptionX扩展OptionX并覆盖TraversableLike[A, OptionX[A]]newBuilder返回SomeX(1) ++ SomeX(2)

但后来我在for (i <- SomeX(1); j <- List(1,2)) yield (i+j)Traversable上遇到了运行时错误。因此,我不认为有可能选择扩展Option并在返回最具体的类型方面做一些理智的事情。

除了可行性,明智的编码风格之外,我不确定Traversable在所有情况下都表现得像Option一样好。 Traversable表示并非始终定义的值,而drop(n)定义可包含多个元素的集合的方法,如splitAt(n)take(n)++,{ {1}}。虽然如果Option也是Traversable,它会提供方便,但我认为这可能会使意图不那么明确。

在必要时使用toSeq似乎是一种无痛的方式来表明我希望我的选项表现得像Traversable。对于某些重复出现的用例,存在option2Iterable隐式转换 - 例如,这已经有效(它们都返回List(1,2)):

  • List(Option(1), Option(2), None).flatten
  • for (i <- List(0,1); j <- Some(1)) yield (i+j)
  • Some(1) ++ Some(2)

答案 1 :(得分:4)

它不是Traversable,因为您无法为其实现scala.collection.mutable.Builder

嗯,即使如此,它也可能是Traversable,但这会导致很多方法返回Option现在返回Traversable。如果您想查看这些方法,请查看采用CanBuildFrom参数的方法。

让我们拿你的示例代码来说明原因:

Seq(Set(1,3,2),Seq(4),Option(5)).flatten

那应该等于:

Seq(1, 2, 3, 4, 5)

现在,让我们考虑一下这个选择:

Option(Set(1,3,2),Seq(4),Option(5)).flatten

这有什么价值?

答案 2 :(得分:1)

原因是在某些情况下应用了implicits,类型会变得不那么精确。您仍然会有Option值,但静态返回类型将类似于Iterable,e。 G。不是“最精确的”。

答案 3 :(得分:1)

也许我是密集的,但我不明白为什么有人会需要这个。此外,它还需要None Traversable,这似乎在语义上可疑。

他们说设计已经完成,而没有任何东西可以添加,但没有什么可以带走。当然,这并不是说Scala标准库是完美的。

答案 4 :(得分:0)

从Scala 2.13开始, Option now extends IterableOnce ,基本上是新的TraversableOnce,现在TraversableOnce is deprecated支持更简单的类型层次结构。在官方Scala网站上,Option现在定义为

sealed abstract class Option[+A] extends IterableOnce[A] with Product with Serializable