为什么期权不可遍历? [英] Why is Option not Traversable?

查看:39
本文介绍了为什么期权不可遍历?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆