准引号

定义和导入详细信息

语言
此文档页面专门针对 Scala 2 中发布的功能,这些功能要么已在 Scala 3 中删除,要么已被替代方案替换。除非另有说明,此页面中的所有代码示例均假定你正在使用 Scala 2。

Denys Shabalin 实验性

修饰符

除包和包对象之外的每个定义都关联了一个修饰符对象,其中包含以下数据

  1. FlagSet,一组表征给定定义的位。
  2. 名称内的私有(例如 fooprivate[foo] def f 中)。
  3. 注释列表。

准引号让你可以通过对 ModifiersFlagSet 和注释取消引号的本机支持轻松处理这些字段

scala> val f1 = q"${Modifiers(PRIVATE | IMPLICIT)} def f"
f1: universe.DefDef = implicit private def f: scala.Unit

scala> val f2 = q"$PRIVATE $IMPLICIT def f"
f2: universe.DefDef = implicit private def f: scala.Unit

scala> val f3 = q"private implicit def f"
f3: universe.DefDef = implicit private def f: scala.Unit

所有这些准引号都会产生等效的树。还可以将未引用的标志与源代码中内联提供的标志结合起来,但应在内联标志之前使用未引用的标志

scala> q"$PRIVATE implicit def foo"
res10: universe.DefDef = implicit private def foo: scala.Unit

scala> q"implicit $PRIVATE def foo"
<console>:32: error: expected start of definition
              q"implicit $PRIVATE def foo"
                         ^

要提供定义注释,必须取消引用新形状的树

scala> val annot = q"new foo(1)"
annot: universe.Tree = new Foo(1)

scala> val f4 = q"@$annot def f"
f4: universe.DefDef = @new foo(1) def f: scala.Unit

scala> val f5 = q"@foo(1) def f"
f5: universe.DefDef = @new foo(1) def f: scala.Unit

在解构中,可以提取 Modifiers 或注释,但不能单独提取标志

scala> val q"$mods def f" = q"@foo implicit def f"
mods: universe.Modifiers = Modifiers(<deferred> implicit, , Map())

scala> val q"@..$annots implicit def f" = q"@foo @bar implicit def f"
annots: List[universe.Tree] = List(new foo(), new bar())

考虑到定义可能包含在类型检查期间添加到树中的各种低级标志,建议始终提取完整的修饰符,否则你的模式可能不详尽。如果你不关心它们,只需使用通配符

scala> val q"$_ def f" = q"@foo @bar implicit def f"

模板

模板是定义树中的常见抽象,用于新表达式、类、特征、对象和包对象。尽管目前没有插值器,但我们可以使用新表达式的示例来说明其结构(类似的处理将应用于所有其他带有模板的树)

q"new { ..$earlydefns } with ..$parents { $self => ..$stats }"

模板包括

  1. 早期定义。一系列 valtype 定义。类型定义仍然允许,但它们已被弃用,未来将被移除

     scala> val withx = q"new { val x = 1 } with RequiresX"
     withx: universe.Tree = ...
    
     scala> val q"new { ..$earlydefns } with RequiresX" = withx
     earlydefns: List[universe.Tree] = List(val x = 1)
    
  2. 父级列表。类型标识符列表,其中列表中的第一个标识符可以具有可选类型和值参数。这是因为第一个父级必须是一个类,而后续父级只是尚未接受参数的特征

     scala> val q"new ..$parents"  = q"new Foo(1) with Bar[T]"
     parents: List[universe.Tree] = List(Foo(1), Bar[T])
    

    第一个父级具有不寻常的形状,它是术语和类型树的组合

     scala> val q"${tq"$name[..$targs]"}(...$argss)" = parents.head
     name: universe.Tree = Foo
     targs: List[universe.Tree] = List()
     argss: List[List[universe.Tree]] = List(List(1))
    

    其他父级只是普通的类型树

     scala> val tq"$name[..$targs]" = parents.tail.head
     name: universe.Tree = Bar
     targs: List[universe.Tree] = List(T)
    
  3. 自身类型定义。一个 val 定义,可用于定义 this 的别名,并通过 tpt 提供自身类型

     scala> val q"new { $self => }" = q"new { self: T => }"
     self: universe.ValDef = private val self: T = _
    
     scala> val q"$mods val $name: $tpt" = self
     mods: universe.Modifiers = Modifiers(private, , Map())
     name: universe.TermName = self
     tpt: universe.Tree = T
    
  4. 主体语句列表。

     scala> val q"new { ..$body }" = q"new { val x = 1; def y = 'y }"
     body: List[universe.Tree] = List(val x = 1, def y = scala.Symbol("y"))
    

Val 和 Var 定义

valvar 分别允许你定义不可变值和可变变量。此外,它们还可以用于表示 函数方法 参数。

每个 valvar 由四个组件组成:修饰符、名称、类型树和右侧

scala> val valx = q"val x = 2"
valx: universe.ValDef = val x = 2

scala> val q"$mods val $name: $tpt = $rhs" = valx
mods: universe.Modifiers = Modifiers(, , Map())
name: universe.TermName = x
tpt: universe.Tree = <type ?>
rhs: universe.Tree = 2

如果 val 的类型没有由用户明确指定,则 空类型 用作 tpt

valvar 是不相交的(它们不匹配彼此)

scala> val q"$mods val $name: $tpt = $rhs" = q"var x = 2"
scala.MatchError: var x = 2 (of class scala.reflect.internal.Trees$ValDef)
  ... 32 elided

Vars 在其修饰符中始终具有 MUTABLE 标志

scala> val q"$mods var $name: $tpt = $rhs" = q"var x = 2"
mods: universe.Modifiers = Modifiers(<mutable>, , Map())
name: universe.TermName = x
tpt: universe.Tree = <type ?>
rhs: universe.Tree = 2

模式定义

模式定义允许你使用 Scala 模式匹配功能来定义变量。与 valvar 定义不同,模式定义不是一类,它们通过常规 valvar 和模式匹配的组合来表示

scala> val patdef = q"val (x, y) = (1, 2)"
patdef: universe.Tree =
{
  <synthetic> <artifact> private[this] val x$2 = scala.Tuple2(1, 2): @scala.unchecked match {
    case scala.Tuple2((x @ _), (y @ _)) => scala.Tuple2(x, y)
  };
  val x = x$2._1;
  val y = x$2._2;
  ()
}

这种表示对这种定义的使用有一些副作用

  1. 由于单个定义经常被分解为多个较低级别的定义,因此你必须始终使用取消引用拼接来将模式定义取消引用到其他树中

     scala> val tupsum = q"..$patdef; a + b"
     tupsum: universe.Tree =
     {
       <synthetic> <artifact> private[this] val x$3 = scala.Tuple2(1, 2): @scala.unchecked match {
         case scala.Tuple2((x @ _), (y @ _)) => scala.Tuple2(x, y)
       };
       val x = x$3._1;
       val y = x$3._2;
       a.$plus(b)
     }
    

    否则,如果使用常规取消引用,则定义将嵌套在一个块中,这将使它们在预期使用的范围内不可见

     scala> val wrongtupsum = q"$patdef; a + b"
     wrongtupsum: universe.Tree =
     {
       {
         <synthetic> <artifact> private[this] val x$3 = scala.Tuple2(1, 2): @scala.unchecked match {
           case scala.Tuple2((x @ _), (y @ _)) => scala.Tuple2(x, y)
         };
         val x = x$3._1;
         val y = x$3._2;
         ()
       };
       a.$plus(b)
     }
    
  2. 人们只能构造模式定义,而不能解构它们。

模式定义的通用形式包括修饰符、模式、归属类型和右侧

q"$mods val $pat: $tpt = $rhs"

类似地,还可以构造可变模式定义

q"$mods var $pat: $tpt = $rhs"

类型定义

类型定义有两种可能的形状:抽象类型定义和别名类型定义。

抽象类型定义具有以下形状

scala> val q"$mods type $name[..$tparams] >: $low <: $high" =
           q"type Foo[T] <: List[T]"
mods: universe.Modifiers = Modifiers(<deferred>, , Map())
name: universe.TypeName = Foo
tparams: List[universe.TypeDef] = List(type T)
low: universe.Tree = <empty>
high: universe.Tree = List[T]

每当其中一个边界不可用时,它就会表示为 空树。此处每个类型参数本身都是一个类型定义。

另一种类型的定义是类型别名

scala> val q"$mods type $name[..$args] = $tpt" =
           q"type Foo[T] = List[T]"
mods: universe.Modifiers = Modifiers(, , Map())
name: universe.TypeName = Foo
args: List[universe.TypeDef] = List(type T)
tpt: universe.Tree = List[T]

由于类型别名和抽象类型的底层统一表示,一个匹配另一个

scala> val q"$mods type $name[..$args] = $tpt" = q"type Foo[T] <: List[T]"
mods: universe.Modifiers = Modifiers(<deferred>, , Map())
name: universe.TypeName = Foo
args: List[universe.TypeDef] = List(type T)
tpt: universe.Tree =  <: List[T]

其中 tpt 具有 TypeBoundsTree(low, high) 形状。

方法定义

每个方法都由修饰符、名称、类型参数、值参数、返回类型和正文组成

scala> val q"$mods def $name[..$tparams](...$paramss): $tpt = $body" = q"def f = 1"
mods: universe.Modifiers = Modifiers(, , Map())
name: universe.TermName = f
tparams: List[universe.TypeDef] = List()
paramss: List[List[universe.ValDef]] = List()
tpt: universe.Tree = <type ?>
body: universe.Tree = 1

类型参数是 类型定义,值参数是 val 定义。推断出的返回类型表示为 空类型。如果方法的正文是 空表达式,则表示该方法是抽象的。

或者,您还可以解构参数,分离隐式和非隐式参数

scala> val q"def g(...$paramss)(implicit ..$implparams) = $body" =
           q"def g(x: Int)(implicit y: Int) = x + y"
paramss: List[List[universe.ValDef]] = List(List(val x: Int = _))
implparams: List[universe.ValDef] = List(implicit val y: Int = _)
body: universe.Tree = x.$plus(y)

如果方法没有任何隐式参数,这种处理参数的方式仍然有效,并且 implparams 将被提取为空列表

scala> val q"def g(...$paramss)(implicit ..$implparams) = $rhs" =
           q"def g(x: Int)(y: Int) = x + y"
paramss: List[List[universe.ValDef]] = List(List(val x: Int = _), List(val y: Int = _))
implparams: List[universe.ValDef] = List()
body: universe.Tree = x.$plus(y)

辅助构造函数定义

辅助构造函数是具有以下形状的特殊类型的函数

scala> val q"$mods def this(...$paramss) = this(...$argss)" =
           q"def this() = this(0)"
mods: universe.Modifiers = Modifiers(, , Map())
paramss: List[List[universe.ValDef]] = List(List())
argss: List[List[universe.Tree]] = List(List(0))

由于树的底层底层表示,辅助构造函数表示为具有 termNames.CONSTRUCTOR 名称的特殊类型的函数

scala> val q"$mods def $name[..$tparams](...$paramss): $tpt = $body"
         = q"def this() = this(0)"
mods: universe.Modifiers = Modifiers(, , Map())
name: universe.TermName = <init>
tparams: List[universe.TypeDef] = List()
paramss: List[List[universe.ValDef]] = List(List())
tpt: universe.Tree = <type ?>
body: universe.Tree = <init>(0)

类定义

类的结构如下

q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }"

正如您可能已经看到的,extends 之后的右侧部分只是一个 模板。除了它和修饰符之外,类还具有一个主构造函数,该构造函数由构造函数修饰符、类型和值参数组成,这些参数的行为与 方法 修饰符和参数非常相似。

特征定义

在语法上,特征与 非常相似,减去值参数和构造函数修饰符

 q"$mods trait $tpname[..$tparams] extends { ..$earlydefns } with ..$parents { $self => ..$stats }"

处理中的一个重要区别是由 SI-8399 引起的,这是由于为特征设置的 INTERFACE 标志。只有抽象成员,特征模式可能不匹配

scala> val q"trait $name { ..$stats }" = q"trait X { def x: Int }"
scala.MatchError: ...

解决方法是始终使用通配符模式提取修饰符

scala> val q"$_ trait $name { ..$stats }" = q"trait X { def x: Int }"
name: universe.TypeName = X
stats: List[universe.Tree] = List(def x: Int)

对象定义

在语法上,对象与 非常相似,没有构造函数

q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$stats }"

包定义

包是组织源代码的基本原语。您可以用准引号表示它们,如下所示

scala> val pack = q"package mycorp.myproj { class MyClass }"
pack: universe.PackageDef =
package mycorp.myproj {
  class MyClass extends scala.AnyRef {
    def <init>() = {
      super.<init>();
      ()
    }
  }
}

scala> val q"package $ref { ..$body }" = pack
ref: universe.RefTree = mycorp.myproj
body: List[universe.Tree] =
List(class MyClass extends scala.AnyRef {
  def <init>() = {
    super.<init>();
    ()
  }
})

准引号不支持通常在源文件头中使用的内联包定义语法(但它在 AST 方面等同于受支持的语法)。

包对象定义

包对象是包和对象之间的交叉

q"package object $tname extends { ..$earlydefns } with ..$parents { $self => ..$stats }"

除了没有修饰符之外,所有处理属性都等同于对象。

尽管包和常规对象在语法上似乎非常相似,但它们并不匹配

scala> val q"$mods object $name" = q"package object O"
scala.MatchError: ...

scala> val q"package object $name" = q"object O"
scala.MatchError: ...

在内部,它们表示为嵌套在具有给定名称的包中的对象

scala> val P = q"package object P"
P: universe.PackageDef =
package P {
  object `package` extends scala.AnyRef {
    def <init>() = {
      super.<init>();
      ()
    }
  }
}

这也意味着您可以将包对象匹配为包。

此页面的贡献者