为什么期权不可遍历? [英] Why is Option not Traversable?
问题描述
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(一个"-> 1,2->两个")))res1:Seq [Any] = List(1、3、2、4,(一个,1),(2,两个))
PS2:我知道我可以写: Seq(Set(1,3,2),Seq(4),Option(5).toSeq).flatten
或其他难看的东西.>
PS3:在最近的一个月中有一些工作要做,以使 Option
看起来更像 Traversable
,而无需实现它:另一项提交
让 flatMap
返回 Option
而不是 Traversable 可能会有挑战代码>.尽管这早于整个2.8
CanBuildFrom
机制.
问题是问曾经在邮件列表中出现过一次,但未引起任何回应.
这是一个例子:
密封特征OptionX [+ A]扩展了Traversable [A] {def foreach [U](f:(A)=> U):单位= if(!isEmpty)f(get)def得到:Adef isDefined:布尔值def getOrElse [B> ;: A](默认值:=> B):B}案例类SomeX [+ A](a:A)扩展了OptionX [A] {覆盖def isEmpty = falsedef get = adef isDefined = truedef getOrElse [B> ;: A](默认:=> B)= a}案例对象NoneX扩展OptionX [Nothing] {覆盖def isEmpty = truedef get = sys.error("none")def isDefined =假def getOrElse [B](默认:=> B)=默认}对象O扩展了App {val s:OptionX [Int] = SomeX(1)值n:OptionX [Int] = NoneXs.foreach(i => println("some" + i))n.foreach(i => println(不应该打印" + i))println(s.map(_ +!"))}
最后一行返回 List("1!")
而不是 Option
.可能有人可以提出一个 CanBuildFrom
,它会产生一个 SomeX("1!")
.我的尝试没有成功:
对象OptionX {隐式def canBuildFrom [Elem] =新的CanBuildFrom [Traversable [_],Elem,OptionX [Elem]] {def builder()= new Builder [Elem,OptionX [Elem]] {var current:OptionX [Elem] = NoneXdef + =(elem:Elem):this.type = {如果(current.isDefined)sys.error(已经定义")否则当前= SomeX(elem)这}def clear(){当前= NoneX}def result():OptionX [Elem] =当前}def apply()= builder()def apply(from:Traversable [_])= builder()}}
我需要显式地传递隐式:
scala>导入o._导入o._斯卡拉>val s:OptionX [Int] = SomeX(1)s:o.OptionX [Int] = SomeX(1)斯卡拉>s.map(_ + 1)(OptionX.canBuildFrom [Int])res1:o.OptionX [Int] = SomeX(2)斯卡拉>s.map(_ + 1)res2:Traversable [Int] =列表(2)
因此,我能够解决此问题,并通过使 OptionX
使 SomeX(1).map(1 +)
返回 OptionX
>扩展 TraversableLike [A,OptionX [A]]
并覆盖 newBuilder
.
但是随后我在 SomeX(1)++ SomeX(2)
或 for(i<-SomeX(1); j<-List(1,2))产生(i + j)
.因此,我认为不可能有扩展 Traversable
的选项,并且在返回最具体的类型方面做得很明智.
除了可行性和编码风格之外,我不确定让 Option
在所有情况下都像 Traversable
一样好. Option
表示不总是定义的值,而 Traversable
定义其中可以包含多个元素的集合的方法,例如 drop(n)
, splitAt(n)
, take(n)
, ++
.尽管如果 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)
-
一些(1)++一些(2)
Is there any rational for Option
not being Traversable
?
In Scala 2.9, Seq(Set(1,3,2),Seq(4),Option(5)).flatten
doesn't compile and simply having it to implement the Traversable
trait seams rational to me. If it's not the case, there must be something I don't see that don't allow it. What is it?
PS: While trying to understand, I achieved awful things that compile, like:
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: I know I can write: Seq(Set(1,3,2),Seq(4),Option(5).toSeq).flatten
or other ugly thing.
PS3: There seams to be work in the last month to make Option
look more like Traversable
without implementing it: commit, another commit
There may be challenges around having flatMap
return an Option
rather than a Traversable
. Though that predates the whole 2.8 CanBuildFrom
machinery.
The question was asked once before on the mailing list but didn't elicit a response.
Here is an illustration:
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(_ + "!"))
}
The last line returns a List("1!")
instead of Option
. May be somebody can come up with a CanBuildFrom
that would yield an SomeX("1!")
. My attempt did not succeed:
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()
}
}
I need to pass the implicit explicitly:
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)
Edit:
So I was able to work around the issue and have SomeX(1).map(1+)
return an OptionX
by having OptionX
extend TraversableLike[A, OptionX[A]]
and overriding newBuilder
.
But then I get runtime errors on SomeX(1) ++ SomeX(2)
or for (i <- SomeX(1); j <- List(1,2)) yield (i+j)
. So I don't think it's possible have option extend Traversable
and do something sane in terms of returning the most specific type.
Beyond feasibility, coding style wise, I'm not sure it's a good thing to have Option
behave like a Traversable
in all circumstances. Option
represent values that are not always defined, while Traversable
defines methods for collections that can have multiple elements in it like drop(n)
, splitAt(n)
, take(n)
, ++
. Although it would offer convenience if Option
was also a Traversable
, I think it may make intent less clear.
Using a toSeq
where necessary seems like a painless way to indicate that I want my option to behave like a Traversable
. And for certain recurring use cases, there is the option2Iterable
implicit conversion - so for instance this already works (they all return 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)
这篇关于为什么期权不可遍历?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!