在Scala中,我可以隐式地将某些文字转换为我的自定义类型吗?

Jea*_*let 4 macros scala literals implicit-conversion

在我的应用程序中,我正在跟踪用户拥有的信用数量.要添加一些类型检查,我使用的Credits类与此类似:

case class Credits(val numCredits: Int) extends Ordered[Credits] {
   ...
}
Run Code Online (Sandbox Code Playgroud)

假设我有一个def accept(creds: Credits): Unit我想要调用的函数.有没有办法让我称之为

process(Credits(100))
process(0)
Run Code Online (Sandbox Code Playgroud)

但不是这个?

process(10)
Run Code Online (Sandbox Code Playgroud)

也就是说,我想仅从文字中提供隐式转换,0而不是其他.现在,我只是val Zero = Credits(0)在伴侣对象中,我认为这是一个相当不错的练习,但无论如何我都会对答案感兴趣,包括其他评论,例如:

  • 这可以通过2.10中的宏隐式转换来完成吗?
  • 应该Credits扩展AnyVal而不是2.10中的案例类?

Edm*_*984 10

这种编译时检查是使用宏的良好地形,将在2.10中提供

一个非常聪明的人Jason Zaugg已经实现了类似于你需要的东西,但它适用于正则表达式:正则表达式编译时检查.

您可能希望查看其Macrocosm以了解它是如何完成的以及如何以相同的目的编写自己的宏.

https://github.com/retronym/macrocosm

如果你真的想了解更多有关宏的知识,首先我会说你需要勇敢,因为文档现在很少,而API很可能会改变.Jason Zaugg的作品与2.10-M3的编辑很好,但我不确定它是否适用于较新的版本.

如果你想从一些读数开始:

现在,谈到主题,Scala宏是CAT:"编译时AST转换".抽象语法树是编译器表示源代码的方式.编译器将后续转换应用于AST,并在最后一步实际生成java字节码.

现在让我们来看看Jason Zaugg的代码:

 def regex(s: String): scala.util.matching.Regex = macro regexImpl

  def regexImpl(c: Context)(s: c.Expr[String]): c.Expr[scala.util.matching.Regex] = {
    import c.universe._

    s.tree match {
      case Literal(Constant(string: String)) =>
        string.r // just to check
        c.reify(s.splice.r)
    }
  }
Run Code Online (Sandbox Code Playgroud)

正如您所见,regex是一个特殊的函数,它通过调用宏regexImpl获取String并返回Regex

宏函数在第一个参数列表中接收上下文,在第二个参数列表中以c.Expr [A]的形式列出宏的参数并返回c.Expr [B].请注意,c.Expr是路径依赖类型,即它是在Context中定义的类,因此如果您有两个上下文,则以下内容是非法的

val c1: context1.Expr[String] = ...
val c2: context2.Expr[String] = ...
val c3: context1.Expr[String] = context2.Expr[String] // illegal , compile error
Run Code Online (Sandbox Code Playgroud)

现在,如果你看看代码中发生了什么:

  • 在s.tree上有匹配块匹配
  • 如果s.tree是一个包含常量String的Literal,则调用string.r

这里发生的是在Predef.scala中定义了从字符串到StringOps的隐式转换,它在每个scala源的编译中自动导入

implicit def augmentString(x: String): StringOps = new StringOps(x)

StringOps扩展了scala.collection.immutable.StringLike,其中包含:

def r: Regex = new Regex(toString)
Run Code Online (Sandbox Code Playgroud)

由于宏是在编译时执行的,这将在编译时执行,如果抛出异常(即从无效的正则表达式字符串创建正则表达式的行为),编译将失败


注意:不幸的是,API非常不稳定,如果你查看 http://scalamacros.org/documentation/reference.html,你会看到一个关于Context.scala的断开链接.正确的链接是 https://github.com/scala/scala/blob/2.10.x/src/reflect/scala/reflect/makro/Context.scala