Scala的类型类模式中隐式定义的运行时成本

时间:2017-11-26 08:29:43

标签: scala typeclass implicits

我正在查看this文章,了解如何使用implicits在Scala中模拟类型类。

如果我没有错,每次使用递归实例声明的类型类方法(Printer a => Printer (Option a))时,都会在运行时构造一个新实例。以下是代码,每次使用print时,都会在运行时创建新的Printer实例。无论如何,在为特定类型(在这种情况下为Option[Int])创建时,是否重用该实例?

trait Printer[T] {
  def print(t: T): String
}

object Printer {
  implicit val intPrinter: Printer[Int] = new Printer[Int] {
    def print(i: Int) = s"$i: Int"
  }

  implicit def optionPrinter[V](implicit pv: Printer[V]): Printer[Option[V]] =
    new Printer[Option[V]] {
      println("New Option Printer")
      def print(ov: Option[V]) = ov match {
        case None => "None"
        case Some(v) => s"Option[${pv.print(v)}]"
      }
    }

}

object Main {
  def print[T](t: T)(implicit p: Printer[T]) = p.print(t)

  def main(args: Array[String]): Unit = {
    val res3 = print(Option(1))
    val res4 = print(Option(2))
    println(s"res3: ${res3}")
    println(s"res4: ${res4}")
  }
}

// New Option Printer
// New Option Printer
// res3: Option[1: Int]
// res4: Option[2: Int]

2 个答案:

答案 0 :(得分:3)

你是对的。在您的示例中,每次调用都会创建一个新实例。但是,有几种方法可以解决它。根据我的经验,您最终得出以下经验法则:

首先,不要过早优化,确保其他实例确实存在问题。 如果您确定它与性能相关,那么您只需编写很多val s。

object Printer {
  implicit val intPrinter: Printer[Int] = new Printer[Int] {
    def print(i: Int) = s"$i: Int"
  }

  // make sure this is not visible, as you do not want to have productive code create new instances on demand 
  private[this] def optionPrinter[V](implicit pv: Printer[V]): Printer[Option[V]] =
    new Printer[Option[V]] {
      println("New Option Printer")
      def print(ov: Option[V]) = ov match {
        case None => "None"
        case Some(v) => s"Option[${pv.print(v)}]"
      }
    }

  implicit val intOptPrinter: Printer[Option[Int]] = optionPrinter[Int]
}

我认为,使用shapeless可以实现更高级的解决方案。但是,imho,因为需要深入理解类型类和类型级编程。

答案 1 :(得分:0)

您还可以考虑使用Typelevel的Machinist项目。

它提供了宏以消除与implicit class扩展方法相关的运行时开销。

https://github.com/typelevel/machinist