准引号

取消提升

语言
此文档页面特定于 Scala 2 中提供的功能,这些功能已在 Scala 3 中删除或由替代方案取代。除非另有说明,此页面中的所有代码示例均假定您使用的是 Scala 2。

Denys Shabalin 实验性

取消提升是 提升 的逆向操作:它获取一个树并从中恢复一个值

trait Unliftable[T] {
  def unapply(tree: Tree): Option[T]
}

由于树可能不是我们数据类型的表示,因此 unapply 的返回类型是 Option[T],而不仅仅是 T。此签名使得可以轻松地将 Unliftable 实例用作提取器。

每当给定数据类型有 Unliftable 的隐式实例时,您都可以使用归因语法在模式匹配中使用它

scala> val q"${left: Int} + ${right: Int}" = q"2 + 2"
left: Int = 2
right: Int = 2

scala> left + right
res4: Int = 4

需要注意的是,在默认情况下提取 NameTermNameModifiers 的位置不会执行取消提升

scala> val q"foo.${bar: Int}" = q"foo.bar"
<console>:29: error: pattern type is incompatible with expected type;
 found   : Int
 required: universe.NameApi
       val q"foo.${bar: Int}" = q"foo.bar"
                        ^

还可以成功组合取消引用拼接和取消提升

scala> val q"f(..${ints: List[Int]})" = q"f(1, 2, 3)"
ints: List[Int] = List(1, 2, 3)

scala> val q"f(...${intss: List[List[Int]]})" = q"f(1, 2, 3)(4, 5)(6)"
intss: List[List[Int]] = List(List(1, 2, 3), List(4, 5), List(6))

类似于提升,这会逐个元素取消提升函数的参数,并将结果包装到 List 中。

自备

类似于可提升项,可以定义自己的不可提升项

package Points

import scala.universe._

case class Point(x: Int, y: Int)
object Point {
  implicit val unliftPoint = Unliftable[points.Point] {
    case q"_root_.points.Point(${x: Int}, ${y: Int})" => Point(x, y)
  }
}

此处必须注意一些细微差别

  1. 类似于 LiftableUnliftable 在伴随对象中定义了一个辅助 apply 函数,以简化 Unliftable 实例的创建。它采用类型参数 T 以及部分函数 PartialFunction[Tree, T],并返回 Unliftable[T]。在定义部分函数的所有输入中,它都应无条件返回 T 的实例。

  2. 我们只为运行时环境定义了 Unliftable,在宏中将不可用。(请参阅 共享可提升项实现

  3. 此不可提升项中使用的模式仅匹配以 _root_ 开头的对 Point 的完全限定引用。它不会匹配引用的其他可能形状;必须手动指定这些形状。此问题是由缺乏 卫生 导致的。

  4. 该模式仅匹配具有文字 Int 参数的树。它不适用于可能计算为 Int 的其他表达式。

标准不可提升项

类型 表示形式
ByteShortIntLong q"0" 0
Float q"0.0" 0.0
Double q"0.0D" 0.0D
Boolean q"true"q"false" truefalse
Char q"'c'" 'c'
Unit q"()" ()
String q""" "字符串" """ "字符串"
符号 q"'符号" '符号
术语名称 q"foo", pq"foo" TermName("foo")
类型名称 tq"foo" TypeName("foo")
类型 tt: TypeTree tt.tpe
常量 lit: Literal lit.value
TupleN[...] * q"(1, 2)" (1, 2)

(*) 未提升的元组在 [2, 22] 范围内的所有 N 中均已定义。所有类型参数本身都必须是未提升的。

此页面的贡献者