【发布时间】:2015-05-22 08:45:43
【问题描述】:
我正在试验一组自定义容器函数,并从 Scala 的集合库中获得了关于 CanBuildFrom[-From, -Elem, -To] 隐式参数的灵感。
在 Scala 的集合中,CanBuildFrom 支持对函数的返回类型进行参数化,例如 map。在内部,CanBuildFrom 参数像工厂一样使用:map 在其输入元素上应用它的一阶函数,将每个应用程序的结果输入到 CanBuildFrom 参数中,最后调用 CanBuildFrom 的 result 方法来构建最终map 调用的结果。
据我了解,CanBuildFrom[-From, -Elem, -To] 的 -From 类型参数仅在 apply(from: From): Builder[Elem, To] 中使用,它创建了一个预初始化了一些元素的 Builder。在我的自定义容器函数中,我没有那个用例,所以我创建了自己的 CanBuildFrom 变体 Factory[-Elem, +Target]。
现在我可以拥有一个特质Mappable
trait Factory[-Elem, +Target] {
def apply(elems: Traversable[Elem]): Target
def apply(elem: Elem): Target = apply(Traversable(elem))
}
trait Mappable[A, Repr] {
def traversable: Traversable[A]
def map[B, That](f: A => B)(implicit factory: Factory[B, That]): That =
factory(traversable.map(f))
}
还有一个实现Container
object Container {
implicit def elementFactory[A] = new Factory[A, Container[A]] {
def apply(elems: Traversable[A]) = new Container(elems.toSeq)
}
}
class Container[A](val elements: Seq[A]) extends Mappable[A, Container[A]] {
def traversable = elements
}
不幸的是,调用 map
object TestApp extends App {
val c = new Container(Seq(1, 2, 3, 4, 5))
val mapped = c.map { x => 2 * x }
}
产生错误消息 not enough arguments for method map: (implicit factory: tests.Factory[Int,That])That. Unspecified value parameter factory 。当我添加显式导入 import Container._ 或显式指定预期返回类型 val mapped: Container[Int] = c.map { x => 2 * x } 时,错误消失了
当我向Factory 添加一个未使用的第三类参数时,所有这些“变通方法”都变得不必要了
trait Factory[-Source, -Elem, +Target] {
def apply(elems: Traversable[Elem]): Target
def apply(elem: Elem): Target = apply(Traversable(elem))
}
trait Mappable[A, Repr] {
def traversable: Traversable[A]
def map[B, That](f: A => B)(implicit factory: Factory[Repr, B, That]): That =
factory(traversable.map(f))
}
并更改Container中的隐式定义
object Container {
implicit def elementFactory[A, B] = new Factory[Container[A], B, Container[B]] {
def apply(elems: Traversable[A]) = new Container(elems.toSeq)
}
}
所以最后我的问题是:为什么看似未使用的 -Source 类型参数是解析隐式所必需的?
作为一个额外的元问题:如果您没有有效的实现(集合库)作为模板,您如何解决这些问题?
澄清
解释一下为什么我认为隐式解析应该在没有额外的-Source 类型参数的情况下工作可能会有所帮助。
根据this doc entry on implicit resolution,隐式在类型的伴随对象中查找。作者没有提供来自伴随对象的隐式参数的示例(他只解释了隐式转换),但我认为这意味着对Container[A]#map 的调用应该使来自object Container 的所有隐式都可用作隐式参数,包括我的@ 987654352@。这一假设得到了以下事实的支持:提供目标类型(无需额外的显式导入!!)即可获得隐式解析。
【问题讨论】:
-
我目前无法写出完整的答案,但
From的一个重要作用是指导隐式选择。我们希望"foo".map(_.toUpper)和List('f', 'o', 'o').map(_.toUpper)有不同的返回类型,所以它们需要接收不同的实例。 -
它在隐含参数类型的伴生对象中寻找隐含,而不是源类型。