Option
不是Traversable
是否有理由?
在Scala 2.9中,Seq(Set(1,3,2),Seq(4),Option(5)).flatten
无法编译,只是让它实现Traversable
特征接缝对我合理。如果不是这样,那么我必须看到一些不允许的东西。它是什么?
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
而不执行它:commit,another commit
答案 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