此文档页面特定于 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
需要注意的是,在默认情况下提取 Name
、TermName
或 Modifiers
的位置不会执行取消提升
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)
}
}
此处必须注意一些细微差别
-
类似于
Liftable
,Unliftable
在伴随对象中定义了一个辅助apply
函数,以简化Unliftable
实例的创建。它采用类型参数T
以及部分函数PartialFunction[Tree, T]
,并返回Unliftable[T]
。在定义部分函数的所有输入中,它都应无条件返回T
的实例。 -
我们只为运行时环境定义了
Unliftable
,在宏中将不可用。(请参阅 共享可提升项实现) -
此不可提升项中使用的模式仅匹配以
_root_
开头的对Point
的完全限定引用。它不会匹配引用的其他可能形状;必须手动指定这些形状。此问题是由缺乏 卫生 导致的。 -
该模式仅匹配具有文字
Int
参数的树。它不适用于可能计算为Int
的其他表达式。
标准不可提升项
类型 | 表示形式 | 值 |
---|---|---|
Byte 、Short 、Int 、Long |
q"0" |
0 |
Float |
q"0.0" |
0.0 |
Double |
q"0.0D" |
0.0D |
Boolean |
q"true" 、q"false" |
true 、false |
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 中均已定义。所有类型参数本身都必须是未提升的。