This doc page is specific to features shipped in Scala 2, which have either been removed in Scala 3 or replaced by an alternative. Unless otherwise stated, all the code examples in this page assume you are using Scala 2.
Denys Shabalin EXPERIMENTAL
Unlifting is the reverse operation to lifting: it takes a tree and recovers a value from it:
trait Unliftable[T] { def unapply(tree: Tree): Option[T] } Due to the fact that the tree may not be a representation of our data type, the return type of unapply is Option[T] rather than just T. This signature makes it easy to use Unliftable instances as extractors.
Whenever an implicit instance of Unliftable is available for a given data type you can use it for pattern matching with the help of an ascription syntax:
scala> val q"${left: Int} + ${right: Int}" = q"2 + 2" left: Int = 2 right: Int = 2 scala> left + right res4: Int = 4 It’s important to note that unlifting will not be performed at locations where Name, TermName or Modifiers are extracted by default:
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" ^ One can also successfully combine unquote splicing and unlifting:
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)) Analogously to lifting, this would unlift arguments of the function, element-wise and wrap the result into a List.
Bring your own
Similarly to liftables one can define your own unliftables:
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) } } Here one must pay attention to a few nuances:
-
Similarly to
Liftable,Unliftabledefines a helperapplyfunction in the companion object to simplify the creation ofUnliftableinstances. It takes a type parameterTas well as a partial functionPartialFunction[Tree, T]and returns anUnliftable[T]. At all inputs where a partial function is defined it is expected to return an instance ofTunconditionally. -
We’ve only define
Unliftablefor the runtime universe, it won’t be available in macros. (see sharing liftable implementations) -
Patterns used in this unliftable will only match a fully qualified reference to
Pointthat starts with_root_. It won’t match other possible shapes of the reference; they have to be specified by hand. This problem is caused by a lack of hygiene. -
The pattern will only match trees that have literal
Intarguments. It won’t work for other expressions that might evaluate toInt.
Standard Unliftables
| Type | Representation | Value |
|---|---|---|
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""" "string" """ | "string" |
Symbol | q"'symbol" | 'symbol |
TermName | q"foo", pq"foo" | TermName("foo") |
TypeName | tq"foo" | TypeName("foo") |
Type | tt: TypeTree | tt.tpe |
Constant | lit: Literal | lit.value |
TupleN[...] * | q"(1, 2)" | (1, 2) |
(*) Unliftable for tuples is defined for all N in [2, 22] range. All type parameters have to be Unliftable themselves.