是否可以像旧的 CanBuildFrom 那样隐式获取集合的构建器?

Is it possible to implicitly get a builder for a collection, as in the old CanBuildFrom?

我有一些旧代码依赖隐式 CanBuildFrom 来构建指定为类型参数的类型的集合。从 2.12 开始,在新的集合库中,替换 BuildFrom 不提供无参数 Builder 工厂方法。我想要的是集合的 IterableFactory,但这些是伴随对象,不是隐含的。 我能以某种方式移植它而不引入我自己的隐式工厂 class 为库中的每个集合 class 包装一个 Factory 吗? 我知道有那些工厂有很多口味,但即使我不得不为那些接受隐含证据的人添加一个特例,它仍然比我有的要好得多。

新代码中一个明智的选择可能是将 IterableFactory 作为一个(值)参数而不是依赖显式类型参数,但它需要在太多地方进行更改,所以我会而是坚持当前的架构并做样板文件。

您可以使用类型参数而不是隐式参数。

def listToString[A, CC[x] <: Iterable[x]](
    list: collection.IterableOps[A, CC, CC[A]]
): CC[String] =
  list.map(x => x.toString)

Using it:

listToString(List(1, 2, 3, 4))
-> List(1, 2, 3, 4): List[String]

listToString(Set("foo", "bar", "baz"))
-> Set(foo, bar, baz): scala.collection.immutable.Set[String]

如果您想使用 Builder 逐个元素地构建一个通用集合,并且没有先前存在的集合,您可以使用隐式 Factory 参数。例如:

import scala.collection.Factory

class Filler[T](makeElement: Int => T) {
  def apply[C[_]](n: Int)(implicit factory: Factory[T, C[T]]): C[T] = {
    val builder = factory.newBuilder
    for (i <- 1 to n) builder += makeElement(i)
    builder.result()
  }
}

你可以这样使用它:

scala> val fill = new Filler(_.toString)
fill: Filler[String] = Filler@154f8280

scala> fill[Vector](10)
res0: Vector[String] = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> fill[Set](10)
res1: Set[String] = HashSet(8, 4, 9, 5, 10, 2, 7, 3, 6, 1)

scala> fill[Array](10).toSeq
res2: Seq[String] = ArraySeq(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

标准库中为 StringArray 提供了单独的隐式 Factory。对于所有 Iterables(ListMapSet 等)它都有效,因为 Iterables 的伴随对象扩展了 IterableFactory class,提供了一个implicit def iterableFactory[A]: Factory[A, CC[A]]方法。