Scala:没有Boilerplate的拉皮条

时间:2012-11-04 20:57:21

标签: scala metaprogramming implicit-conversion scala-2.10 scala-macros

我大量使用Pimp my Library模式,我想删除样板文件。例如,假设我有一些特质PrettyPrint:

trait PrettyPrint { def prettyPrint: String }

如果我想要pimp Int和Double,我需要编写这样的代码:

implicit def int2PrettyPrint(self: Int) = 
  new PrettyPrint { def prettyPrint = "Int: " + self }
implicit def double2PrettyPrint(self: Double) = 
  new PrettyPrint { def prettyPrint = "Double: " + self }

在上面,我将其归类为样板:1)隐式转换的名称,2)“new”关键字,3)也许参数名称为“self”,4)也许是“隐含”关键字。我宁愿写这样的东西:

@pimp[Int, PrettyPrint] { def prettyPrint = "Int: " + self }
@pimp[Double, PrettyPrint] { def prettyPrint = "Double: " + self }

在上面代码的右侧,假设名称“self”是转换参数。

关于如何做到这一点的想法?

一些注意事项:

1)如果需要,我可以使用Scala 2.10。

2)据我所知,Scala 2.10 中的新隐式类不足以。这是因为每个隐式类只有一个隐式转换。换句话说,像下面这样的代码无法编译,因为PrettyPrint被声明了两次:

implicit class PrettyPrint(self: Int) = ...
implicit class PrettyPrint(self: Double) = ...

4 个答案:

答案 0 :(得分:3)

您可以用不同的方式命名隐式类:

implicit class PrettyPrintInt(self: Int) = ...
implicit class PrettyPrintDouble(self: Double) = ...

答案 1 :(得分:3)

这是另一种解决方案,需要更多的样板预先设置,以换取PrettyPrint的每个特定实例的稍微混乱:

implicit class PrettyPrintable[T]( val self: T ) extends AnyVal { 
  def prettyPrint( implicit impl: PrettyPrint[T]): String = impl.prettyPrint( self ) 
}
trait PrettyPrint[T]{ def prettyPrint( self: T ): String }
object PrettyPrint {
  def apply[T]( impl: T => String ): PrettyPrint[T] = new PrettyPrint[T] {
    def prettyPrint( self: T ) = impl( self )
  }
}

implicit val int2PrettyPrint = PrettyPrint[Int]( "Int: " + _ )
implicit val double2PrettyPrint = PrettyPrint[Double]( "Double: " + _ )
// Or more explicitly:
//implicit val int2PrettyPrint = PrettyPrint{self: Int => "Int: " + self }
//implicit val double2PrettyPrint = PrettyPrint{self: Double => "Double: " + self }

比较

implicit def int2PrettyPrint(self: Int) = new PrettyPrint { def prettyPrint = "Int: " + self } 

为:

implicit val int2PrettyPrint = PrettyPrint[Int]( "Int: " + _ )

您仍然需要implicit关键字,以及隐含值的唯一名称

答案 2 :(得分:2)

对NativeLibs4Java邮件列表中discussion的后续跟踪,我在其中举例说明了such a compiler plugin(将@extend(Int) def foo = blah扩展为implicit class foo(self: Int) extends AnyVal { def foo = blah })。

我写了a more elaborated plugin,将这些定义扩展为......宏(提供宏扩展扩展/“pimps”,没有运行时依赖!)。

假设:

@extend(Any) def quoted(quote: String): String = quote + self + quote

它扩展为:

import scala.language.experimental.macros
implicit class scalaxy$extensions$quoted$1(self: Any) {
  def quoted(quote: String) = macro scalaxy$extensions$quoted$1.quoted
}
object scalaxy$extensions$quoted$1 {
  def quoted(c: scala.reflect.macros.Context)
            (quote: c.Expr[String]): c.Expr[String] = {
    import c.universe._
    val Apply(_, List(selfTree$1)) = c.prefix.tree
    val self = c.Expr[Any](selfTree$1)
    {
      reify(quote.splice + self.splice + quote.splice)
    }
  }
}

答案 3 :(得分:1)

1周后的摘要:看来我需要编写一个编译器插件来获取我指定的确切行为。