2

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

trait PrettyPrint { def prettyPrint: String }

如果我想拉皮条 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)也许是“implicit”关键字。我宁愿写这样的东西:

@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

4 回答 4

3

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

implicit class PrettyPrintInt(self: Int) = ...
implicit class PrettyPrintDouble(self: Double) = ...
于 2012-11-04T21:13:14.857 回答
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关键字,以及隐式值的唯一名称

于 2012-11-05T11:17:28.083 回答
2

跟进我们在 NativeLibs4Java 的邮件列表上的讨论,我在其中给出了这样一个编译器插件的示例(扩展@extend(Int) def foo = blahimplicit class foo(self: Int) extends AnyVal { def foo = blah })。

我编写了一个更详细的插件,将这些定义扩展为...宏(提供宏可扩展扩展/“皮条客”,没有运行时依赖!)。

鉴于:

@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)
    }
  }
}
于 2013-02-20T09:02:17.233 回答
1

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

于 2012-11-12T20:37:26.930 回答