|
| 1 | +--- |
| 2 | +layout: doc-page |
| 3 | +title: "Principled Meta Programming - Pattern matching" |
| 4 | +--- |
| 5 | + |
| 6 | +## Overview |
| 7 | + |
| 8 | +Quote pattern matching allows to match a quote against another quote while extracting the contents of defined holes. This will be used in as desugaring of quoted patterns. |
| 9 | + |
| 10 | +```scala |
| 11 | +def optimize(expr: Expr[Int]): Expr[Int] = expr match { |
| 12 | + case '{ 0 + $y } => optimize(y) |
| 13 | + case '{ ${Literal(a)} + ${Literal(b)} } => (a + b).toExpr |
| 14 | + case '{ ${x} + ${y @ Literal(_)} } => optimize('{ $y + $x }) |
| 15 | + ... |
| 16 | + case expr => expr |
| 17 | +} |
| 18 | +``` |
| 19 | + |
| 20 | +Where `x: Expr[Int]`, `y: Expr[Int]`, `a: Int` and `b: Int`. |
| 21 | + |
| 22 | + |
| 23 | +### Quote patterns |
| 24 | + |
| 25 | +Quote extractors are patterns of the shape `'{ ... }` that can match an `Expr[T]`. |
| 26 | + |
| 27 | +```scala |
| 28 | +(expr: Expr[T]) match { |
| 29 | + case '{ pattern } => |
| 30 | +} |
| 31 | +``` |
| 32 | + |
| 33 | +They allow to extract expresions, types and identifiers |
| 34 | + |
| 35 | +```scala |
| 36 | +(expr: Expr[T]) match { |
| 37 | + case '{ ... val x: T = $rhs ... } => |
| 38 | + case '{ ... val $y: T = ... } => |
| 39 | + case '{ ... val z: $t = ... } => |
| 40 | +} |
| 41 | +``` |
| 42 | + |
| 43 | +Where `x: Expr[T]`, `y: Binging[T]` and `z: Type[T]`. |
| 44 | + |
| 45 | + |
| 46 | +#### Desugaring |
| 47 | + |
| 48 | +A pattern with splices such as |
| 49 | + |
| 50 | + |
| 51 | +```scala |
| 52 | +(expr: Expr[T]) match { |
| 53 | + case '{ ... ($x: T) ... val $b: U = ... foo[$t] ... } => |
| 54 | +} |
| 55 | +``` |
| 56 | + |
| 57 | +is transformed to a call to `Matcher.unapply` with the appropriate types for the splices in a tuple. |
| 58 | + |
| 59 | +```scala |
| 60 | +(expr: Expr[T]) match { |
| 61 | + case scala.runtime.quoted.Matcher.unapply |
| 62 | + [... *: Expr[X] *: ... *: Binding[T] *: ... *: Type[V] *: ... *: Unit] |
| 63 | + (... *: x *: ... *: b *: ... *: t *: ... *: ()) |
| 64 | + (/*implicits*/ '{ ... hole[T] ... bindHole[U] ... Hole[V] ... }, reflect) => |
| 65 | +} |
| 66 | +``` |
| 67 | + |
| 68 | + |
| 69 | +### Other expression patterns |
| 70 | + |
| 71 | +```scala |
| 72 | +(expr: Expr[T]) match { |
| 73 | + case Literal(lit) => lit: T |
| 74 | + case Binding(binding) => binding: Binding[T] |
| 75 | + case IsValRef(expr2) => expr2: expr.type |
| 76 | + case IsDefRef(expr) => expr2: expr.type |
| 77 | + |
| 78 | + case '{ ... ${Literal(lit)} ... } |
| 79 | +} |
| 80 | +``` |
| 81 | + |
| 82 | + |
| 83 | +## Spec |
| 84 | + |
| 85 | +### Quote pattern desugaring |
| 86 | + |
| 87 | +Given a quote pattern `'{ pattern }` the desugaring is defined in terms of a splice types `T<pattern>`, a runtime pattern `P<patern>` and extracted bindings `E<pattern>`. |
| 88 | +```scala |
| 89 | +Matcher.unapply[T<pattern>](E<pattern>)(/*implicits*/ '{ P<pattern> }, reflect) |
| 90 | +``` |
| 91 | + |
| 92 | + |
| 93 | + |
| 94 | +`<pattern>` is defined recursively in the following way: |
| 95 | + |
| 96 | +| Quote pattern syntax | Matcher pattern | Splice types | Extracted | |
| 97 | +| ------------- |:-------------:| ----------- | ----------- | |
| 98 | +| `<$x>` where `x: Expr[X]` | `Matcher.hole[X]` | `Tuple1[Expr[T]]` | `Tuple1[Expr[T]](x)` | |
| 99 | +| `<$t>` where `x: Type[X]` | `Matcher.HOLE[X]` | `Tuple1[Type[T]]` | `Tuple1[Type[T]](t)` | |
| 100 | +| `<$n: X>` where `n` is a name of `val`, `var`, `def` or parameter definition | `n: bindHole[X]` | `Tuple1[Binding[X]]` | `Tuple1[Binding[X]](n)` | |
| 101 | +| `<if (e1) e2 else e3>` | `if (<e1>) <e2> else <e3>` | `Concat[T<e1>, Concat[T<e2>, T<e3>]]` | E<`e1`> ++ E<`e2`> ++ E<`e3`>) | |
| 102 | +| `<while (e1) e2>` | `while (<e1>) <e2>` | | E<`e1`> ++ E<`e2`> | |
| 103 | +| `<f[T1, ..., Tn](e1, ..., em)>` | `<f>[<T1>, ..., <Tn>](<e1>, ..., <em>)` | | E<`f`> ++ E<`T1`> ++ ... ++ E<`Tn`> ++ E<`e1`> ++ ... ++ E<`em`> | |
| 104 | +| `<(e1: T1, ..., en: Tn) => body>` | `(<e1: T1>, ..., <en: Tn>) => <body>` | | E<`e1: T1`> ++ ... ++ E<`en: Tn`> ++ E<`body`> | |
| 105 | +| `<lhs = rhs>` | `lhs = <rhs>` | | E<`rhs`> | |
| 106 | +| `<new X>` | `new <X>` | | E<`X`> | |
| 107 | +| `<this>` | `this` | | | |
| 108 | +| `<qual.super[T]>` | `<qual>.super[<T>]` | | E<`qual`> ++ E<`T`) | |
| 109 | +| `<K[T1, ..., Tn]>` | `<K>[<T1>, ..., <Tn>]` | | E<`K`> ++ E<`T1`> ++ ... ++ E<`Tn`> | |
| 110 | +| `<x>` where `x` is an identifier | `x` | | | |
| 111 | +| `<T>` where `T` is an identifier | `T` | | | |
| 112 | + |
| 113 | + |
| 114 | +### Quote pattern match runtime |
| 115 | + |
| 116 | +| Match | Condition | |
| 117 | +| ------------- |:-------------:| |
| 118 | +| matches(`s`, `hole[T]`) for `s` of type `S` | `S <:< T` | |
| 119 | +| matches(`T`, `Hole[T]`) | | |
| 120 | +| matches(`n: X1`, `n: bindHole[X2]`) | matches(`X1`, `X2`) | |
| 121 | +| matches(`if (s1) s2 else s3`, `if (p1) p2 else p3`) | matches(`s1`, `p1`) && matches(`s2`, `p2`) && matches(`s3`,`p3`) | |
| 122 | +| matches(`while (s1) s2`, `while (p1) p2`) | matches(`s1`, `p1`) && matches(`s2`, `p2`) | |
| 123 | + |
| 124 | +| Match | Result | |
| 125 | +| ------------- |:-------------:| |
| 126 | +| result(`s`, `hole[T]`) for `s` of type `S` | `s` as an `Expr[T]` | |
| 127 | +| result(`T`, `Hole[T]`) | `T` as a `Type[T]` | |
| 128 | +| matches(`n: X1`, `n: bindHole[X2]`) | (`n` as a `Binding[X2]`) ++ result(`X1`, `X2`) | |
| 129 | +| result(`if (s1) s2 else s3`, `if (p1) p2 else p3`) | result(`s1`, `p1`) ++ result(`s2`, `p2`) ++ result(`s3`,`p3`) | |
| 130 | +| result(`while (s1) s2`, `while (p1) p2`) | result(`s1`, `p1`) ++ result(`s2`, `p2`) | |
0 commit comments