【问题标题】:Calling type-specific code from a library function, determined at compile-time从库函数调用特定于类型的代码,在编译时确定
【发布时间】: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 编译器“如果存在所需类型的对象,请使用它!”?

我能想到两种解决方法,但都不令人满意:

  1. doStuff 提供implicit toDetail 参数。这行得通,但它需要我为每个函数添加一个implicit toDetail 参数,这些函数可能在调用堆栈的某个较低位置使用CanMakeDetailedString 对象。这会让我的代码变得非常混乱。

  2. 完全放弃隐式方法,并以面向对象的方式执行此操作,通过覆盖 .toDetail 等特殊的新方法,使 Rototiller 继承自 CanMakeDetailedString

是否有一些技术、技巧或命令行开关可以使 Scala 编译器静态解析正确的隐式对象? (而不是像在面向对象的方法中那样在程序运行时动态地计算出来。)如果不是,这似乎严重限制了库代码对“类型类”或隐式参数的使用程度。换句话说,有什么的方法来做我上面做得不好的事情?


澄清:我不是在问如何使用implicit val 来做到这一点。我在问如何让 Scala 编译器在库代码中静态选择适合类型的函数,而无需在每个库函数中显式列出每个函数的隐式参数,这些函数可能会在堆栈中被调用到较低的位置。如果它是用隐式或其他任何东西完成的,对我来说并不重要。我只想知道如何编写通用代码,在编译时适当地选择特定类型的函数。

【问题讨论】:

标签: scala typeclass implicit


【解决方案1】:

隐式在编译时被解析,因此如果没有更多信息,它就无法知道 doStuff 中的 A 是什么。 可以按照您的建议通过额外的隐式参数或基本类型/接口提供该信息。

您还可以对 A 类型使用反射,使用返回子类型的 getType,将对象强制转换为该类型,并调用具有为您写入字符串详细信息的类型名称的预定义函数。我真的不推荐它,因为任何 OOP 或 FP 解决方案都更好恕我直言。

【讨论】:

  • 这是否意味着我所要求的无法完成(静态选择正确的功能等)?如果我理解正确(希望我错了),类型类在 Scala 中基本上是残废的,除非你“显式”地让每个函数为堆栈中可能需要的所有类型类采用隐式参数。
  • 这是我的理解。另一种解决方案是在 nicePrint 中打开 A 的类型。似乎 Haskell 在这方面做得更好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-02
  • 1970-01-01
相关资源
最近更新 更多