【发布时间】:2016-03-23 15:43:43
【问题描述】:
如何在 Scala 库中为调用者提供给该库的对象调用特定于类型的代码,其中关于调用哪个特定于类型的代码的决定是在编译时(静态)而不是在运行时?
为了说明这个概念,假设我想创建一个库函数,如果为对象定义了CanMakeDetailedString,则以一种方式打印对象,否则就像.toString。请参阅此示例代码中的nicePrint:
import scala.language.implicitConversions
trait CanMakeDetailedString[A] extends (A => String)
def noDetailedString[A] = new CanMakeDetailedString[A] {
def apply(a: A) = a.toString
}
object Util {
def nicePrint[A](a: A)
(implicit toDetail: CanMakeDetailedString[A] = noDetailedString[A])
: Unit = println(toDetail(a))
def doStuff[A](a: A)
: Unit = { /* stuff goes here */ nicePrint(a) }
}
下面是一些测试代码:
object Main {
import Util._
case class Rototiller(name: String)
implicit val rototillerDetail = new CanMakeDetailedString[Rototiller] {
def apply(r: Rototiller) = s"The rototiller named ${r.name}."
}
val r = Rototiller("R51")
nicePrint(r)
doStuff(r)
}
这是 Scala 2.11.2 中的输出:
The rototiller named R51.
Rototiller(R51)
当我从定义rototillerDetail 的同一范围调用nicePrint 时,Scala 编译器会找到rototillerDetail 并将其隐式传递给nicePrint。但是,当我在同一个范围内调用另一个范围 (doStuff) 中调用 nicePrint 的函数时,Scala 编译器找不到 rototillerDetail。
毫无疑问,这是有充分理由的。不过,我想知道如何告诉 Scala 编译器“如果存在所需类型的对象,请使用它!”?
我能想到两种解决方法,但都不令人满意:
向
doStuff提供implicit toDetail参数。这行得通,但它需要我为每个函数添加一个implicit toDetail参数,这些函数可能在调用堆栈的某个较低位置使用CanMakeDetailedString对象。这会让我的代码变得非常混乱。完全放弃隐式方法,并以面向对象的方式执行此操作,通过覆盖
.toDetail等特殊的新方法,使Rototiller继承自CanMakeDetailedString。
是否有一些技术、技巧或命令行开关可以使 Scala 编译器静态解析正确的隐式对象? (而不是像在面向对象的方法中那样在程序运行时动态地计算出来。)如果不是,这似乎严重限制了库代码对“类型类”或隐式参数的使用程度。换句话说,有什么好的方法来做我上面做得不好的事情?
澄清:我不是在问如何使用implicit val 来做到这一点。我在问如何让 Scala 编译器在库代码中静态选择适合类型的函数,而无需在每个库函数中显式列出每个函数的隐式参数,这些函数可能会在堆栈中被调用到较低的位置。如果它是用隐式或其他任何东西完成的,对我来说并不重要。我只想知道如何编写通用代码,在编译时适当地选择特定类型的函数。
【问题讨论】:
-
那么这是回答了吗?
-
@slouc 我不知道。也许问题是anæsthetized。