Ответ 1
Могут возникнуть проблемы с тем, что flatMap
возвращает Option
, а не Traversable
. Хотя это предшествовало всему оборудованию 2.8 CanBuildFrom
.
Вопрос был задан ранее в списке рассылки, но не вызвал ответа.
Вот иллюстрация:
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)
Edit:
Итак, мне удалось обойти проблему и SomeX(1).map(1+)
вернуть OptionX
, имея OptionX
extend 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)
, ++
. Хотя было бы удобно, если 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)