CanBuildFrom如何知道某个类型是否可以从另一个类型构建?

时间:2013-12-24 14:18:06

标签: scala

我读了official doc,但我还是不明白它是如何运作的。例如:

class A {
  type Self
}
def seqToSet[T <: A](seq: Seq[T])
                    (implicit cbf: CanBuildFrom[Seq[T], T#Self, Set[T]]) {}

上面的代码可以编译......但是怎么样? Scala如何知道Set可以构建Seq?如何才能确保将T#Self(几乎任何类型)放入Set[T]

2 个答案:

答案 0 :(得分:3)

一般不知道。您应该提供CanBuildFrom

对于像这样的简单案例,您可以使用breakOut

class A1 extends A { type Self = A1 }
seqToSet(new A1 :: Nil)(collection.breakOut)
// compiles fine

对于更复杂的案例:

case class A2(val i: Int) extends A {
  type Self = Int
}

implicit val A2cbf = new CanBuildFrom[Seq[A2],A2#Self,Set[A2]] {
  import scala.collection.mutable.Builder
  class A2Builder extends Builder[A2#Self,Set[A2]] {
    var es = List[A2]()
    def +=(elem: A2#Self): this.type = { es ::= A2(elem); this }
    def clear(): Unit = es = Nil
    def result(): Set[A2] = es.toSet
  }
  def apply() = new A2Builder
  def apply(from: Seq[A2]) = apply()
}

seqToSet(new A2 :: Nil)(collection.breakOut)
// compiles fine

您应提供Builder(使用CanBuildFrom),使其接受方法A2#Self中的+=并返回Set[A2]作为结果。在代码示例中,它使用A2创建新的elemA2(elem)

让我们用几乎相同的参数创建更有用的方法:

def seqToSet[T <: A](seq: Seq[T])(f: T => T#Self)
                    (implicit cbf: CanBuildFrom[Seq[T], T#Self, Set[T]]) = {
  seq.map{f}: Set[T]
}

seqToSet(A2(1) :: A2(2) :: Nil){ a2 => a2.i + 1 }
// Set[A2] = Set(A2(3), A2(2))

此方法通过转换集合类型为A提供了一些奇怪的转换。

答案 1 :(得分:2)

CanBuildFrom[A,B,C]是隐含的,它遵循由语言规范确定的隐式解析规则。您实际上可以直接查看Where does Scala look for implicits?以查找所有详细信息。

所以答案是:

def seqToSet[T <: A](seq: Seq[T])
                    (implicit cbf: CanBuildFrom[Seq[T], T#Self, Set[T]]) {}
如果范围中有CanBuildFrom[Seq[T],T#Self, Set[T]]

将编译,如果没有,则编译失败。如果有,有人为它提供了一个实现,这意味着可以进行操作(CanBuildFrom是一个特征,所以实例是它的具体实现)

对于集合,图书馆设计师决定遵循的接近是将隐含位置放在伴随对象中:上面你看到来自2.10.3的Seq.scala

trait Seq[+A] extends PartialFunction[Int, A]
                      with Iterable[A]
                      with GenSeq[A]
                      with GenericTraversableTemplate[A, Seq]
                      with SeqLike[A, Seq[A]] {
  override def companion: GenericCompanion[Seq] = Seq

  override def seq: Seq[A] = this
}

/** $factoryInfo
 *  The current default implementation of a $Coll is a `List`.
 *  @define coll sequence
 *  @define Coll `Seq`
 */
object Seq extends SeqFactory[Seq] {
  /** $genericCanBuildFromInfo */
  implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, Seq[A]] = ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]]

  def newBuilder[A]: Builder[A, Seq[A]] = immutable.Seq.newBuilder[A]
}