From d5aadc4c8e59832f753df11e5b0e5783bcf5bb71 Mon Sep 17 00:00:00 2001 From: Denys Shabalin Date: Wed, 2 Apr 2014 17:33:37 +0200 Subject: [PATCH 1/2] Add a new quasiquote guide --- overviews/macros/quasiquotes.md | 111 ---- overviews/quasiquotes/definition-details.md | 382 ++++++++++++ overviews/quasiquotes/expression-details.md | 630 ++++++++++++++++++++ overviews/quasiquotes/future.md | 17 + overviews/quasiquotes/hygiene.md | 126 ++++ overviews/quasiquotes/intro.md | 192 ++++++ overviews/quasiquotes/lifting.md | 158 +++++ overviews/quasiquotes/pattern-details.md | 114 ++++ overviews/quasiquotes/syntax-summary.md | 182 ++++++ overviews/quasiquotes/terminology.md | 22 + overviews/quasiquotes/type-details.md | 185 ++++++ overviews/quasiquotes/unlifting.md | 102 ++++ overviews/quasiquotes/usecases.md | 93 +++ 13 files changed, 2203 insertions(+), 111 deletions(-) delete mode 100644 overviews/macros/quasiquotes.md create mode 100644 overviews/quasiquotes/definition-details.md create mode 100644 overviews/quasiquotes/expression-details.md create mode 100644 overviews/quasiquotes/future.md create mode 100644 overviews/quasiquotes/hygiene.md create mode 100644 overviews/quasiquotes/intro.md create mode 100644 overviews/quasiquotes/lifting.md create mode 100644 overviews/quasiquotes/pattern-details.md create mode 100644 overviews/quasiquotes/syntax-summary.md create mode 100644 overviews/quasiquotes/terminology.md create mode 100644 overviews/quasiquotes/type-details.md create mode 100644 overviews/quasiquotes/unlifting.md create mode 100644 overviews/quasiquotes/usecases.md diff --git a/overviews/macros/quasiquotes.md b/overviews/macros/quasiquotes.md deleted file mode 100644 index 6036188691..0000000000 --- a/overviews/macros/quasiquotes.md +++ /dev/null @@ -1,111 +0,0 @@ ---- -layout: overview-large -title: Quasiquotes - -disqus: true - -partof: macros -num: 8 -languages: [ja] ---- -EXPERIMENTAL - -**Eugene Burmako** - -Quasiquotes are shipped with recent milestone builds of Scala 2.11, starting from 2.11.0-M4. They are also available in Scala 2.10 with the macro paradise plugin. Follow the instructions at the ["Macro Paradise"](/overviews/macros/paradise.html) page to download and use our compiler plugin. - -Note that both in 2.10.x and in 2.11, quasiquotes don't bring transitive dependencies on macro paradise, -which means that you can write macros using quasiquotes from macro paradise for 2.10.x, and people will be able -to use them with vanilla 2.10.x. -Neither your code that uses quasiquotes from macro paradise, nor the users of such code will need to have macro paradise -on their classpath at runtime. - -NEW There's a new, work-in-progress quasiquote guide for Scala 2.11.0 that lives at [http://den.sh/quasiquotes.html](http://den.sh/quasiquotes.html). Behavior of quasiquotes in macro paradise for 2.10.x might differ from what's described in the guide, but that's something that we plan to fix in paradise 2.0.0-final somewhen around the final release of Scala 2.11.0. - -OBSOLETE For the time being, while the aforementioned guide is work in progress, I'm also leaving the old quasiquote guide in place. Once Denys finishes the new guide, we will decide what to do. - -## Intuition - -Consider an `async` [macro annotation](/overviews/macros/annotations.html), which takes a class or an object and duplicates their methods with asynchronous counterparts wrapped in `future`. - - @async - class D { - def x = 2 - // def asyncX = future { 2 } - } - - val d = new D - d.asyncX onComplete { - case Success(x) => println(x) - case Failure(_) => println("failed") - } - -An implementation of such a macro might look as the code at the snippet below. This routine - acquire, destructure, wrap in generated code, restructure again - is quite familiar to macro writers. - - case ClassDef(_, _, _, Template(_, _, defs)) => - val defs1 = defs collect { - case DefDef(mods, name, tparams, vparamss, tpt, body) => - val tpt1 = if (tpt.isEmpty) tpt else AppliedTypeTree( - Ident(newTermName("Future")), List(tpt)) - val body1 = Apply( - Ident(newTermName("future")), List(body)) - val name1 = newTermName("async" + name.capitalize) - DefDef(mods, name1, tparams, vparamss, tpt1, body1) - } - Template(Nil, emptyValDef, defs ::: defs1) - -However even seasoned macro writers will admit that this code, even though it's quite simple, is exceedingly verbose, requiring one to understand the details internal representation of code snippets, e.g. the difference between `AppliedTypeTree` and `Apply`. Quasiquotes provide a neat domain-specific language that represents parameterized Scala snippets with Scala: - - val q"class $name extends Liftable { ..$body }" = tree - - val newdefs = body collect { - case q"def $name[..$tparams](...$vparamss): $tpt = $body" => - val tpt1 = if (tpt.isEmpty) tpt else tq"Future[$tpt]" - val name1 = newTermName("async" + name.capitalize) - q"def $name1[..$tparams](...$vparamss): $tpt1 = future { $body }" - } - - q"class $name extends AnyRef { ..${body ++ newdefs} }" - -At the moment quasiquotes suffer from [SI-6842](https://issues.scala-lang.org/browse/SI-6842), which doesn't let one write the code as concisely as mentioned above. A [series of casts](https://gist.github.com/7ab617d054f28d68901b) has to be applied to get things working. - -## Details - -Quasiquotes are implemented as a part of the `scala.reflect.api.Universe` cake, which means that it is enough to do `import c.universe._` to use quasiquotes in macros. Exposed API provides `q`, `tq`, `cq` and `pq` [string interpolators](/overviews/core/string-interpolation.html) (corresponding to term and type quasiquotes), which support both construction and deconstruction, i.e. can be used both in normal code and on the left-hand side of a pattern case. - -| Flavor | Works with | Construction | Deconstruction | -|--------|------------|-----------------------|-------------------------------| -| `q` | Term trees | `q"future{ $body }"` | `case q"future{ $body }" =>` | -| `tq` | Type trees | `tq"Future[$t]"` | `case tq"Future[$t]" =>` | -| `cq` | Cases | `cq"x => x"` | `case cq"$pat => ${_}" =>` | -| `pq` | Patterns | `pq"xs @ (hd :: tl)"` | `case pq"$id @ ${_}" =>` | - -Unlike regular string interpolators, quasiquotes support multiple flavors of splices in order to distinguish between inserting/extracting single trees, lists of trees and lists of lists of trees. Mismatching cardinalities of splicees and splice operators results in a compile-time error. - - scala> val name = TypeName("C") - name: reflect.runtime.universe.TypeName = C - - scala> val q"class $name1" = q"class $name" - name1: reflect.runtime.universe.Name = C - - scala> val args = List(Literal(Constant(2))) - args: List[reflect.runtime.universe.Literal] = List(2) - - scala> val q"foo(..$args1)" = q"foo(..$args)" - args1: List[reflect.runtime.universe.Tree] = List(2) - - scala> val argss = List(List(Literal(Constant(2))), List(Literal(Constant(3)))) - argss: List[List[reflect.runtime.universe.Literal]] = List(List(2), List(3)) - - scala> val q"foo(...$argss1)" = q"foo(...$argss)" - argss1: List[List[reflect.runtime.universe.Tree]] = List(List(2), List(3)) - -## Tips and tricks - -### Liftable - -To simplify splicing of non-trees, quasiquotes provide the `Liftable` type class, which defines how values are transformed into trees when spliced in. We provide instances of `Liftable` for primitives and strings, which wrap those in `Literal(Constant(...))`. You might want to define your own instances for simple case classes and lists. - - trait Liftable[T] { - def apply(universe: api.Universe, value: T): universe.Tree - } \ No newline at end of file diff --git a/overviews/quasiquotes/definition-details.md b/overviews/quasiquotes/definition-details.md new file mode 100644 index 0000000000..e6e18962f4 --- /dev/null +++ b/overviews/quasiquotes/definition-details.md @@ -0,0 +1,382 @@ +--- +layout: overview-large +title: Definition and import details + +disqus: true + +partof: quasiquotes +num: 10 +outof: 12 +--- +**Denys Shabalin** EXPERIMENTAL + +## Modifiers {:#modifiers} + +Every definition except packages and package objects have associtated modifiers object which contains following data: + +1. `FlagSet`, a set of bits that charactarizes given definition. +2. Private within name (e.g. `foo` in `private[foo] def f`) +3. List of annotations + +Quasiquotes let you easily work with those fields through native support for `Modifiers`, `FlagSet` and annotation unquoting: + + 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 + +All of those quasiquotes result into equivalent trees. It's also possible to combine unquoted flags with one provided inline in the source code but unquoted one should be used before inline ones: + + scala> q"$PRIVATE implicit def foo" + res10: universe.DefDef = implicit private def foo: scala.Unit + + scala> q"implicit $PRIVATE def foo" + :32: error: expected start of definition + q"implicit $PRIVATE def foo" + ^ + +To provide a definition annotation one need to unquote a new-shaped tree: + + 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 + +In deconstruction one can either extract `Modifiers` or annotations, but you can't extract flags separatly: + + scala> val q"$mods def f" = q"@foo implicit def f" + mods: universe.Modifiers = Modifiers( implicit, , Map()) + + scala> val q"@..$annots implicit def f" = q"@foo @bar implicit def f" + annots: List[universe.Tree] = List(new foo(), new bar()) + +Considering the fact that definitions might contain various low-level flags added to trees during typechecking it\'s recommended to always extract complete modifiers as otherwise your pattern might not be exhaustive. If you don't care about them just use a wildcard: + + scala> val q"$_ def f" = q"@foo @bar implicit def f" + +## Templates {:#templates} + +Templates are a common abstraction in definition trees that is used in new expressions, classes, traits, objects, package objects. Although there is no interpolator for it at the moment we can illustrate its structure on the example of new expression (similar handling will applly to all other template-bearing trees): + + q"new { ..$earlydefns } with ..$parents { $self => ..$stats }" + +So template consists of: + +1. Early definitions. A list of val or type definitions. Type definitions are still allowed by they are deprecated and will be removed in the future: + + 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. List of parents. A list of type identifiers with possibly an optional arguments to the first one in the list: + + scala> val q"new ..$parents" = q"new Foo(1) with Bar[T]" + parents: List[universe.Tree] = List(Foo(1), Bar[T]) + + First of the parents has a bit unusual shape that is a symbiosis of term and type trees: + + 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)) + + The others are just plain type trees: + + scala> val tq"$name[..$targs]" = parents.tail.head + name: universe.Tree = Bar + targs: List[universe.Tree] = List(T) + +3. Self type definition. A val definition that can be used to define an alias to this and provide a self-type via 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. List of body statements. + + 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 and Var Definitions {:#val-var} + +Vals and vars allow you to define immutable and mutable variables correspondingly. Additionally they are also used to represent [function](/overviews/quasiquotes/expression-details.html#function), [class](#class) and [method](#method) paremeters. + +Each val and var consistents of four components: modifiers, name, type tree and a right hand side: + + 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 = + rhs: universe.Tree = 2 + +If type of the val isn't explicitly specified by the user an [empty type](/overviews/quasiquotes/type-details.html#empty) is used as tpt. + +Vals and vars are disjoint (they don't match one another): + + 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 always have `MUTABLE` flag in their modifiers: + + scala> val q"$mods var $name: $tpt = $rhs" = q"var x = 2" + mods: universe.Modifiers = Modifiers(, , Map()) + name: universe.TermName = x + tpt: universe.Tree = + rhs: universe.Tree = 2 + +## Pattern Definitions {:#pattern} + +Pattern definitions allow to use scala pattern matching capabilities to define variables. Unlike +val and var definitions, pattern definitions are not first-class and they are get represented +through combination of regular vals and vars and pattern matching: + + scala> val patdef = q"val (x, y) = (1, 2)" + patdef: universe.Tree = + { + 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; + () + } + +This representation has a few side-effects on the usage of such definitions: + +1. Due to the fact that single definition often gets desugared into multiple lower-level + ones, one need to always use unquote splicing to unquote pattern definitions into other trees: + + scala> val tupsum = q"..$patdef; a + b" + tupsum: universe.Tree = + { + 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) + } + + Otherwise if a regular unquoting is used, the definitions will be nested in a block that will make + them invisible in the scope where they are meant to be used: + + scala> val wrongtupsum = q"$patdef; a + b" + wrongtupsum: universe.Tree = + { + { + 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. One can only construct pattern definitions, not deconstruct them. + +Generic form of pattern definition consists of modifiers, pattern, ascribed type and a right hand side: + + q"$mods val $pat: $tpt = $rhs" + +Simiarly one can also construct a mutable pattern definition: + + q"$mods var $pat: $tpt = $rhs" + +## Type Definition {:#type} + +Type definition have two possible shapes: abstract type definitions and alias type definitions. + +Abstract type definitions have the following shape: + + scala> val q"$mods type $name[..$tparams] >: $low <: $high" = + q"type Foo[T] <: List[T]" + mods: universe.Modifiers = Modifiers(, , Map()) + name: universe.TypeName = Foo + tparams: List[universe.TypeDef] = List(type T) + low: universe.Tree = + high: universe.Tree = List[T] + +Whenever one of the bounds isn\'t available it gets represented as [empty tree](/overviews/quasiquotes/expression-details.html#empty). Here each of the type arguments is a type definition iteself. + +Another form of type definition is a type alias: + + 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] + +Due to low level uniform representation of type aliases and abstract types one matches another: + + 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] + +Where `tpt` has a `TypeBoundsTree(low, high)` shape. + +## Method Definition {:#method} + +Each method consists of modifiers, name, type arguments, value arguments, return type and a body: + + 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 = + body: universe.Tree = 1 + +Type arguments are [type definitions](#type) and value arguments are [val definitions](#val-var). Inferred return type is represented as [empty type](/overviews/quasiquotes/type-details.html#empty). If body of the method is [empty expression](/overviews/quasiquotes/expression-details.html#empty) it means that method is abstract. + +Alternatively you can also deconstruct arguments separating implicit and non-implicit parameters: + + 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) + +This way of parameter handling will still work if method doesn\'t have any implicit parameters and `implparams` will get extracted as an empty list: + + 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) + +## Secondary Constructor Definition {:#ctor} + +Secondary constructors are special kinds of methods that have following shape: + + 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)) + +Due to low level underlying representation of trees secondary constructors are represented as special kind of method with `termNames.CONSTRUCTOR` name: + + scala> val q"$mods def $name[..$tparams](...$paramss): $tpt = $body" + = q"def this() = this(0)" + mods: universe.Modifiers = Modifiers(, , Map()) + name: universe.TermName = + tparams: List[universe.TypeDef] = List() + paramss: List[List[universe.ValDef]] = List(List()) + tpt: universe.Tree = + body: universe.Tree = (0) + +## Class Definition {:#class} + +Classes have a following structure: + + q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" + +As you probably already see the right part after extends is just a [template](#templates). Apart from it and modifiers classes +also have primary constructor which consists of constructor modifiers, type and value parameters which behave very much like +[method](#method) modifiers and parameters. + +## Trait Definition {:#trait} + +Syntactically traits are quite similar to [classes](#class) sans value parameters and constructor modifiers: + + q"$mods trait $tpname[..$tparams] extends { ..$earlydefns } with ..$parents { $self => ..$stats }" + +An important difference in handling is caused by [SI-8399](https://issues.scala-lang.org/browse/SI-8399?filter=12305): due to INTERFACE flag that is set for traits with only abstract +members trait pattern might not match: + + scala> val q"trait $name { ..$stats }" = q"trait X { def x: Int }" + scala.MatchError: ... + +A workaround it to always extract modifiers with wildcard pattern: + + scala> val q"$_ trait $name { ..$stats }" = q"trait X { def x: Int }" + name: universe.TypeName = X + stats: List[universe.Tree] = List(def x: Int) + +## Object Definition {:#object} + +Syntactically objects are quite similar [classes](#class) without constructors: + + q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$stats }" + +## Package Definition {:#package} + +Packages are a fundamental primitive to organize source code. You can express them in quasiquotes as: + + scala> val pack = q"package mycorp.myproj { class MyClass }" + pack: universe.PackageDef = + package mycorp.myproj { + class MyClass extends scala.AnyRef { + def () = { + super.(); + () + } + } + } + + scala> val q"package $ref { ..$body }" = pack + ref: universe.RefTree = mycorp.myproj + body: List[universe.Tree] = + List(class MyClass extends scala.AnyRef { + def () = { + super.(); + () + } + }) + +Quasiquotes don\'t support inline package definition syntax that are usually used in the +header of the source file (but it's equivalent to the supported one in terms of ASTs). + +## Package Object Definition{:#package-object} + +Package objects are a cross between packages and object: + + q"package object $tname extends { ..$earlydefns } with ..$parents { $self => ..$stats }" + +All of the handling properties are equivalent to those of objects apart from the fact that they don\'t have [modifiers](#modifiers). + +Even though package and regular objects seem to be quite similar syntactically they don't match one another: + + scala> val q"$mods object $name" = q"package object O" + scala.MatchError: ... + + scala> val q"package object $name" = q"object O" + scala.MatchError: ... + +Internally they get represtend as an object nested into package with given name: + + scala> val P = q"package object P" + P: universe.PackageDef = + package P { + object `package` extends scala.AnyRef { + def () = { + super.(); + () + } + } + } + +This also means that you can match package object as a package. diff --git a/overviews/quasiquotes/expression-details.md b/overviews/quasiquotes/expression-details.md new file mode 100644 index 0000000000..9db804abf4 --- /dev/null +++ b/overviews/quasiquotes/expression-details.md @@ -0,0 +1,630 @@ +--- +layout: overview-large +title: Expression details + +disqus: true + +partof: quasiquotes +num: 7 +outof: 12 +--- +**Denys Shabalin** EXPERIMENTAL + +## Empty {:#empty} + +`q""` is used to indicate that some part of the tree is not provided by the user: + +1. Vals, Vars and Defs without right-hand side have it set to `q""`. +2. Abstract type definitions without bounds have them set to `q""`. +3. Try expressions without finally clause have it set to `q""`. +4. Case clauses without guards have them set to `q""`. + +Default toString formats `q""` as ``. + +## Literal {:#literal} + +Scala has a number of default built-in literals: + + q"1", q"1L" // integer literals + q"1.0f", q"1.0", q"1.0d" // floating point literals + q"true", q"false" // boolean literals + q"'c'" // character literal + q""" "string" """ // string literal + q"'symbol" // symbol literal + q"null" // null literal + q"()" // unit literal + +All of those values have Literal type except symbols which have different representation: + + scala> val foo = q"'foo" + foo: universe.Tree = scala.Symbol("foo") + +Thanks to [lifting](/overviews/quasiquotes/lifting.html) you can also easily create literal trees directly from values of corresponding types: + + scala> val x = 1 + scala> val one = q"$x" + one: universe.Tree = 1 + +This would work the same way for all literal types (see [standard liftables](/overviews/quasiquotes/lifting.html#standard) except `Null`. Lifting of `null` value and `Null` type isn't supported, use `q"null"` if you really mean to create null literal: + + scala> val x = null + scala> q"$x" + :31: error: Can't unquote Null, bottom type values often indicate programmer mistake + q"$x" + ^ + +During deconstruction you can use [unlifting](/overviews/quasiquotes/unlifting.html) to extract values out of Literal trees: + + scala> val q"${x: Int}" = q"1" + x: Int = 1 + +Similarly it would work with all the literal types except `Null`. (see [standard unliftables](/overviews/quasiquotes/unlifting.html#standard)) + +## Identifier and Selection {:#ref} + +Identifiers and member selections are two fundamental primitives that let you refer to other definitions. Combination of two of them is also known `RefTree`. + +Each term identifier is defined by its name and by the fact of being backquoted or not: + + scala> val name = TermName("Foo") + name: universe.TermName = Foo + + scala> val foo = q"$name" + foo: universe.Ident = Foo + + scala> val backquoted = q"`$name`" + backquoted: universe.Ident = `Foo` + +Although backquoted and non-backquoted identifiers may refer to the same things they are not equivalent from synactical point of view: + + scala> val q"`Foo`" = q"Foo" + scala.MatchError: Foo (of class scala.reflect.internal.Trees$Ident) + ... 32 elided + +This is caused by the fact that backquoted identifiers have different semantics in pattern patching. + +Apart from matching on identifiers with given name you can also extract their name values with the help of [unlifting](/overviews/quasiquotes/unlifting.html): + + scala> val q"${name: TermName}" = q"Foo" + name: universe.TermName = Foo + +Name ascription is important here as without it you\'ll get pattern that is equivalent to regular pattern variable binding. + +Similarly you can create and extract member selections: + + scala> val member = TermName("bar") + member: universe.TermName = bar + + scala> val q"foo.$name" = selected + name: universe.TermName = bar + +## Super and This {:#super-this} + +One can use this and super to select precise members within inheritance chain. + +This tree supports following variations: + + scala> val q"$name.this" = q"this" + name: universe.TypeName = + + scala> val q"$name.this" = q"foo.this" + name: universe.TypeName = foov + +So plain `q"this"` is equivalent to `q"${tpnme.EMPTY}.this"`. + +Similarly for super we have: + + scala> val q"$name.super[$qual].$field" = q"super.foo" + name: universe.TypeName = + qual: universe.TypeName = + field: universe.Name = foo + + scala> val q"$name.super[$qual].$field" = q"super[T].foo" + name: universe.TypeName = + qual: universe.TypeName = T + field: universe.Name = foo + + scala> val q"$name.super[$qual].$field" = q"other.super[T].foo" + name: universe.TypeName = other + qual: universe.TypeName = T + field: universe.Name = foo + +## Application and Type Application {:#application} + +Value applications and type applications are two fundamental parts out of which one can construct calls to Scala functions and methods. Lets assume that we would like to handle function calls to the following method: + + def f[T](xs: T*): List[T] = xs.toList + +It can be handled in the following way: + + scala> val apps = List(q"f[Int](1, 2)", q"f('a, 'b)") + scala> apps.foreach { + case q"f[..$ts](..$args)" => + println(s"type arguments: $ts, value arguments: $args") + } + type arguments: List(Int), value arguments: List(1, 2) + type arguments: List(), value arguments: List(scala.Symbol("a"), scala.Symbol("b")) + +As you can see we were able to match both calls even though one has no explicit type applications an danother does have one. This happens because type application matcher extract empty list of type arguments if the tree is not an actual type application making it possible to uniformly handle situations with and without explicit type applications. + +It's recommended to always include type applications when you match on function with type arguments as they will be inserted by the compiler during typechecking even if the user didn't write them explicitly: + + scala> val q"$_; f[..$ts](..$args)" = toolbox.typecheck(q""" + def f[T](xs: T*): List[T] = xs.toList + f(1, 2, 3) + """) + ts: List[universe.Tree] = List(Int) + args: List[universe.Tree] = List(1, 2, 3) + +Another important feature of scala method calls is multiple argument lists and implicit arguments: + + def g(x: Int)(implicit y: Int) = x + y + +Here we might get either one or two subsequent value applications: + + scala> val apps = List(q"g(1)", q"g(1)(2)") + scala> apps.foreach { + case q"g(...$argss)" if argss.nonEmpty => + println(s"argss: $argss") + } + argss: List(List(1)) + argss: List(List(1), List(2)) + +`...$` in a pattern allows us to greedily all subsequent value applications. Similarly to type arguments matcher one needs to be careful due to the fact that it always matches even if there are no actual value applications: + + scala> val q"g(...$argss)" = q"g" + argss: List[List[universe.Tree]] = List() + +Therefore it's recommended to use more specific patterns that check that for example check that extracted argss is not empty. + +Similarly to type arguments, implicit value arguments are automatically inferred during typechecking: + + scala> val q"..$stats; g(...$argss)" = toolbox.typecheck(q""" + def g(x: Int)(implicit y: Int) = x + y + implicit val y = 3 + g(2) + """) + stats: List[universe.Tree] = List(def g(x: Int)(implicit y: Int): Int = x.+(y), implicit val y: Int = 3) + argss: List[List[universe.Tree]] = List(List(2), List(y)) + +## Assign and Update {:#assign-update} + +Assign and update are two related ways to explictly mutate a variable or collection: + + scala> val assign = q"x = 2" + assign: universe.Tree = x = 2 + + scala> val update = q"array(0) = 1" + update: universe.Tree = array.update(0, 1) + +As you can see update syntax is just a syntactic sugar that gets represented as update method call on given object. + +Nevertheless quasiquotes let you deconstruct both of them uniformly according to their user-facing syntax: + + scala> List(assign, update).foreach { + case q"$left = $right" => + println(s"left = $left, right = $right") + } + left = x, right = 2 + left = array(0), right = 1 + +Where `array(0)` has the same AST as function application. + +On the other hand if you want to treat this two cases separately it's also possible with following more specific patterns: + + scala> List(assign, update).foreach { + case q"${ref: RefTree} = $expr" => + println(s"assign $expr to $ref") + case q"$obj(..$args) = $expr" => + println(s"update $obj at $args with $expr") + } + assign 2 to x + update array at List(0) with 1 + + +## Return {:#return} + +Return expressions is used to perform early return from a function. + + scala> val ret = q"return 2 + 2" + ret: universe.Return = return 2.$plus(2) + + scala> val q"return $expr" = ret + expr: universe.Tree = 2.$plus(2) + +## Throw {:#throw} + +Throw expression is used to throw a throwable: + + scala> val thr = q"throw new Exception" + thr: universe.Throw = throw new Exception() + + scala> val q"throw $expr" = thr + expr: universe.Tree = new Exception() + +## Ascription {:#ascription} + +Ascriptions lets users to annotate type of intermidiate expression: + + scala> val ascribed = q"(1 + 1): Int" + ascribed: universe.Typed = (1.$plus(1): Int) + + scala> val q"$expr: $tpt" = ascribed + expr: universe.Tree = 1.$plus(1) + tpt: universe.Tree = Int + +## Annotation {:#annotated} + +Expressions can be annotated: + + scala> val annotated = q"(1 + 1): @positive" + annotated: universe.Annotated = 1.$plus(1): @positive + + scala> val q"$expr: @$annot" = annotated + expr: universe.Tree = 1.$plus(1) + annot: universe.Tree = positive + +It's important to mention that such pattern won't match if we combine annotation with ascription: + + scala> val q"$expr: @$annot" = q"(1 + 1): Int @positive" + scala.MatchError: (1.$plus(1): Int @positive) (of class scala.reflect.internal.Trees$Typed) + ... 32 elided + +In this case we need to deconstruct it as [ascription](#ascription) and then deconstruct `tpt` as [annotated type](/overviews/quasiquotes/type-details.html#annotated). + +## Tuple {:#tuple} + +Tuples are heteregeneous data structures with built-in user-friendly syntax. The syntax itself is just a sugar that maps onto `scala.TupleN` calls: + + scala> val tup = q"(a, b)" + tup: universe.Tree = scala.Tuple2(a, b) + +At the moment tuples are only supported up to 22 arity but this is just an implementation restriction that might be lifted in the future. To find out if given arity is supported use: + + scala> val `tuple 10 supported?` = definitions.TupleClass(10) != NoSymbol + tuple 10 supported?: Boolean = true + + scala> val `tuple 23 supported?` = definitions.TupleClass(23) != NoSymbol + tuple 23 supported?: Boolean = false + +Despited the fact that `Tuple1` class exists there is no built-in syntax for it. Single parens around expression do not change its meaning: + + scala> val inparens = q"(a)" + inparens: universe.Ident = a + +It is also common to treat `Unit` as nullary tuple: + + scala> val elems = List.empty[Tree] + scala> val nullary = q"(..$elems)" + nullary: universe.Tree = () + +Quasiquotes also support deconstruction of tuples of arbitrary arity: + + scala> val q"(..$elems)" = q"(a, b)" + elems: List[universe.Tree] = List(a, b) + +This pattern also matches expressions as single-element tuples: + + scala> val q"(..$elems)" = q"(a)" + elems: List[universe.Tree] = List(a) + +And unit as nullary tuple: + + scala> val q"(..$elems)" = q"()" + elems: List[universe.Tree] = List() + +## Block {:#block} + +Blocks are a fundamental primitive to express sequence of actions or bindings. `q"..."` interpolator is an equivalent of a block. It allows to express more than one expression seperated by semicolon or a newline: + + scala> val t = q"a; b; c" + t: universe.Tree = + { + a; + b; + c + } + +The only difference between `q"{...}"` and `q"..."` is handling of case when just a single element is present. `q"..."` always returns an element itself while a block still remains a block if a single element is not expression: + + scala> val t = q"val x = 2" + t: universe.ValDef = val x = 2 + + scala> val t = q"{ val x = 2 }" + t: universe.Tree = + { + val x = 2; + () + } + +Blocks can also be flattened into another blocks with `..$`: + + scala> val ab = q"a; b" + ab: universe.Tree = + { + a; + b + } + + scala> val abc = q"..$ab; c" + abc: universe.Tree = + { + a; + b; + c + } + +The same syntax can be used to deconstruct blocks: + + scala> val q"..$stats" = q"a; b; c" + stats: List[universe.Tree] = List(a, b, c) + +Deconstruction always returns just user-defined contents of a block: + + scala> val q"..$stats" = q"{ val x = 2 }" + stats: List[universe.Tree] = List(val x = 2) + +Due to automatic flattening of single-element blocks with expressions, expressions themselves are considered to be single-element blocks: + + scala> val q"..$stats" = q"foo" + stats: List[universe.Tree] = List(foo) + +Except for empty tree which is not considered to be a block: + + scala> val q"..$stats" = q"" + scala.MatchError: (of class scala.reflect.internal.Trees$EmptyTree$) + ... 32 elided + +Zero-element block is equivalent to synthetic unit (one that was inserted by the compiler rather than written by the user): + + scala> val q"..$stats" = q"{}" + stats: List[universe.Tree] = List() + + scala> val syntheticUnit = q"..$stats" + syntheticUnit: universe.Tree = () + +Such units are used in empty else branches of [ifs](#if) and empty bodies of [case clauses](#match) making it convenient to work with those cases as with zero-element blocks. + +## If {:#if} + +There are two varieties of if expressions: those with else clause and without it: + + scala> val q"if ($cond) $thenp else $elsep" = q"if (true) a else b" + cond: universe.Tree = true + thenp: universe.Tree = a + elsep: universe.Tree = b + + scala> val q"if ($cond) $thenp else $elsep" = q"if (true) a" + cond: universe.Tree = true + thenp: universe.Tree = a + elsep: universe.Tree = () + +No-else clause is equivalent to else clause that contains a synthetic unit literal ([empty block](#block)). + +## Pattern Match {:#match} + +Pattern matching is cornerstone feature of Scala that lets you deconstruct values into their components: + + q"$expr match { case ..$cases } " + +Where `expr` is some non-empty expression and each case is represented with a `cq"..."` quote: + + cq"$pat if $expr => $expr" + +Combination of the two forms allows to construct and deconstruct arbitrary pattern matches: + + scala> val q"$expr match { case ..$cases }" = + q"foo match { case _: Foo => 'foo case _ => 'notfoo }" + expr: universe.Tree = foo + cases: List[universe.CaseDef] = List(case (_: Foo) => scala.Symbol("foo"), case _ => scala.Symbol("notfoo")) + + scala> val cq"$pat1 => $body1" :: cq"$pat2 => $body2" :: Nil = cases + pat1: universe.Tree = (_: Foo) + body1: universe.Tree = scala.Symbol("foo") + pat2: universe.Tree = _ + body2: universe.Tree = scala.Symbol("notfoo") + +Case clause without body is equivalent to one holding synthetic unit literal ([empty block](#block)): + + scala> val cq"$pat if $expr1 => $expr2" = cq"_ =>" + pat: universe.Tree = _ + expr1: universe.Tree = + expr2: universe.Tree = () + +No-guard is represented with the help of [empty expression](#empty). + +## Try {:#try} + +Try expression is used to handle possible error conditions and ensure consistent state via finally. Both error handling cases and finally clause are optional. + + scala> val q"try $a catch { case ..$b } finally $c" = q"try t" + a: universe.Tree = t + b: List[universe.CaseDef] = List() + c: universe.Tree = + + scala> val q"try $a catch { case ..$b } finally $c" = + q"try t catch { case _: C => }" + a: universe.Tree = t + b: List[universe.CaseDef] = List(case (_: C) => ()) + c: universe.Tree = + + scala> val q"try $a catch { case ..$b } finally $c" = + q"try t finally f" + a: universe.Tree = t + b: List[universe.CaseDef] = List() + c: universe.Tree = f + +Similarly to [pattern matching](#match) cases can be further deconstructed with `cq"..."`. No-finally clause is represented with the help of [empty expression](#empty). + +## Function {:#function} + +There are three ways to create anonymous function: + + scala> val f1 = q"_ + 1" + anon1: universe.Function = ((x$4) => x$4.$plus(1)) + + scala> val f2 = q"(a => a + 1)" + anon2: universe.Function = ((a) => a.$plus(1)) + + scala> val f3 = q"(a: Int) => a + 1" + anon3: universe.Function = ((a: Int) => a.$plus(1)) + +First one uses placeholder syntax. Second one names function parameter but still relies +on type inference to infer its type. Last one explicitly defines function parameter. Due +to implementation restriction second notation can only be used in parenthesis or inside other +expression. If you leave them out you have to specify parameter types. + +Parameters are represented as [Vals](/overviews/quasiquotes/definition-details.html#val-var). If you want to programmatically create val that should have +its type inferred you need to use [empty type](/overviews/quasiquotes/type-details.html#empty): + + scala> val tpt = tq"" + tpt: universe.TypeTree = + + scala> val param = q"val x: $tpt" + param: universe.ValDef = val x + + scala> val fun = q"($param => x)" + fun: universe.Function = ((x) => x) + +All of the given forms are represented in the same way and could be uniformly matched upon: + + scala> List(f1, f2, f3).foreach { + case q"(..$params) => $body" => + println(s"params = $params, body = $body") + } + params = List( val x$5 = _), body = x$5.$plus(1) + params = List(val a = _), body = a.$plus(1) + params = List(val a: Int = _), body = a.$plus(1) + +You can also tear arguments further apart: + + scala> val q"(..$params) => $_" = f3 + params: List[universe.ValDef] = List(val a: Int = _) + + scala> val List(q"$_ val $name: $tpt") = params + name: universe.TermName = a + tpt: universe.Tree = Int + +It's recommended to use underscore pattern in place of [modifiers](/overviews/quasiquotes/definition-details.html#modifiers) even if you don't plan to work +with them as parameters may contains additional flags which might cause match errors. + +## Partial Function {:#partial-function} + +Partial functions are a neat syntax that lets you express functions with +limited domain with the help of pattern matching: + + scala> val pf = q"{ case i: Int if i > 0 => i * i }" + pf: universe.Match = + match { + case (i @ (_: Int)) if i.$greater(0) => i.$times(i) + } + + scala> val q"{ case ..$cases }" = pf + cases: List[universe.CaseDef] = List(case (i @ (_: Int)) if i.$greater(0) => i.$times(i)) + +Weird default pretty printed view on the tree represents the fact that they share similar data structure as +trees for match expressions. Despite this fact they do not match one another: + + scala> val q"$expr match { case ..$cases }" = pf + scala.MatchError: ... + +## While and Do-While Loops {:#while} + +While and do-while loops are low-level control structures that used when performance of iteration +is critical: + + scala> val `while` = q"while(x > 0) x -= 1" + while: universe.LabelDef = + while$6(){ + if (x.$greater(0)) + { + x.$minus$eq(1); + while$6() + } + else + () + } + + scala> val q"while($cond) $body" = `while` + cond: universe.Tree = x.$greater(0) + body: universe.Tree = x.$minus$eq(1) + + scala> val `do-while` = q"do x -= 1 while (x > 0)" + do-while: universe.LabelDef = + doWhile$2(){ + x.$minus$eq(1); + if (x.$greater(0)) + doWhile$2() + else + () + } + + scala> val q"do $body while($cond)" = `do-while` + body: universe.Tree = x.$minus$eq(1) + cond: universe.Tree = x.$greater(0) + +## For and For-Yield Loops {:#for} + +For and For-Yield expressions allow to write monadic style comprehensions that desugar into calls to `map`, `flatMap`, `foreach` and `withFilter` methods: + + scala> val `for-yield` = q"for (x <- xs; if x > 0; y = x * 2) yield x" + for-yield: universe.Tree = + xs.withFilter(((x) => x.$greater(0))).map(((x) => { + val y = x.$times(2); + scala.Tuple2(x, y) + })).map(((x$3) => x$3: @scala.unchecked match { + case scala.Tuple2((x @ _), (y @ _)) => x + })) + +Each enumerator in the comprehension can be expressed with `fq"..."` interpolator: + + scala> val enums = List(fq"x <- xs", fq"if x > 0", fq"y = x * 2") + enums: List[universe.Tree] = List(`<-`((x @ _), xs), `if`(x.$greater(0)), (y @ _) = x.$times(2)) + + scala> val `for-yield` = q"for (..$enums) yield y" + for-yield: universe.Tree + +Simiarly one can deconstruct for-yield back into a list of enumerators and body: + + scala> val q"for (..$enums) yield $body" = `for-yield` + enums: List[universe.Tree] = List(`<-`((x @ _), xs), `if`(x.$greater(0)), (y @ _) = x.$times(2)) + body: universe.Tree = x + +It's important to mention that For and For-Yield do not cross-match each other: + + scala> val q"for (..$enums) $body" = `for-yield` + scala.MatchError: ... + +## New {:#new} + +New expression lets you construct an instance of given type possibly refining it with other types or definitions: + + scala> val q"new ..$parents { ..$body }" = q"new Foo(1) with Bar { def baz = 2 }" + parents: List[universe.Tree] = List(Foo(1), Bar) + body: List[universe.Tree] = List(def baz = 2) + +See [templates](/overviews/quasiquotes/definition-details.html#templates) section for details. + +## Import {:#import} + +Import trees consist of reference and a list of selectors: + + scala> val q"import $ref.{..$sels}" = q"import foo.{bar, baz => boo, poison => _, _}" + ref: universe.Tree = foo + sels: List[universe.Tree] = List((bar @ _), $minus$greater((baz @ _), (boo @ _)), $minus$greater((poison @ _), _), _) + +Selectors are extracted as pattern trees which are syntactically similar to selectors: + +1. Simple identifier selectors are represented as pattern bindings: `pq"bar"` +2. Renaming selectors are represented as thin arrow patterns: `pq"baz -> boo"` +3. Unimport selectors are represented as thin arrows with wildcard right hand side: `pq"poison -> _"` +4. Wildcard selector is represented as wildcard pattern: `pq"_"` + +Similarly one construct imports back from a programmatically created list of selectors: + + scala> val ref = q"a.b" + scala> val sels = List(pq"foo -> _", pq"_") + scala> val imp = q"import $ref.{..$sels}" + imp: universe.Import = import a.b.{foo=>_, _} + + diff --git a/overviews/quasiquotes/future.md b/overviews/quasiquotes/future.md new file mode 100644 index 0000000000..4d36460e37 --- /dev/null +++ b/overviews/quasiquotes/future.md @@ -0,0 +1,17 @@ +--- +layout: overview-large +title: Future prospects + +disqus: true + +partof: quasiquotes +num: 12 +outof: 12 +--- +**Denys Shabalin** EXPERIMENTAL + +In [Project Palladium](www.scalareflect.org) we are working on the following features that target future versions of Scala: + +* Hygiene: [SI-7823](https://issues.scala-lang.org/browse/SI-7823) +* Alternative to Scheme's ellipsis: [SI-8164](https://issues.scala-lang.org/browse/SI-8164) +* Safety by construction diff --git a/overviews/quasiquotes/hygiene.md b/overviews/quasiquotes/hygiene.md new file mode 100644 index 0000000000..6e350cfc20 --- /dev/null +++ b/overviews/quasiquotes/hygiene.md @@ -0,0 +1,126 @@ +--- +layout: overview-large +title: Hygiene + +disqus: true + +partof: quasiquotes +num: 4 +outof: 12 +--- +**Denys Shabalin, Eugene Burmako** EXPERIMENTAL + +The notion of hygiene has been widely popularized by macro research in Scheme. A code generator is called hygienic if it ensures absense of name clashes between regular and generated code, preventing accidental capture of identifiers. As numerous experience reports show, hygiene is of great importance to code generation, because name binding problems are often very non-obvious and lack of hygiene might manifest itself in subtle ways. + +Sophisticated macro systems such as Racket's have mechanisms that make macros hygienic without any effort from macro writers. In Scala we don't have automatic hygiene yet - both of our codegen facilities (compile-time codegen with macros and runtime codegen with toolboxes) require programmers to handle hygiene manually. Fixing this is our number one priority for 2.12 (see [future prospects](/overviews/quasiquotes/future.html)), but in the meanwhile you need to know how to work around the absense of hygiene, which is what this section is about. + +Preventing name clashes between regular and generated code means two things. First, we must ensure that regardless of the context in which we put generated code, its meaning isn't going to change (*referential transparency*). Second, we must make certain that regardless of the context in which we splice regular code, its meaning isn't going to change (often called *hygiene in the narrow sense*). Let's see what can be done to this end on a series of examples. + +## Referential transparency {:#referential-transparency} + +What referential transparency means is that quasiquotes should remember the lexical context in which they are defined. For instance, if there are imports provided at the definition site of the quasiquote, then these imports should be used to resolve names in the quasiquote. Unfortunately, this is not the case at the moment, and here's an example: + + scala> import collection.mutable.Map + + scala> def typecheckType(tree: Tree): Type = + toolbox.typecheck(tree, toolbox.TYPEmode).tpe + + scala> typecheckType(tq"Map[_, _]") =:= typeOf[Map[_, _]] + false + + scala> typecheckType(tq"Map[_, _]") =:= typeOf[collection.immutable.Map[_, _]] + true + +Here we can see that plain reference to `Map` doesn\'t respect our custom import and resolves to default `collection.immutable.Map` instead. Similar problems can arise if references aren't fully qualified in macros. + + // ---- MyMacro.scala ---- + package example + + import reflect.macros.blackbox.Context + import language.experimental.macros + + object MyMacro { + def wrapper(x: Int) = { println(s"wrapped x = $x"); x } + def apply(x: Int): Int = macro impl + def impl(c: Context)(x: c.Tree) = { + import c.universe._ + q"wrapper($x)" + } + } + + // ---- Test.scala ---- + package example + + object Test extends App { + def wrapper(x: Int) = x + MyMacro(2) + } + +If we compile both macro and it's usage we'll see that `println` will not be called when application runs. This will happen because after macro expansion `Test.scala` will look like: + + // Expanded Test.scala + package example + + object Test extends App { + def wrapper(x: Int) = x + wrapper(2) + } + +And wrapper will be resolved to `example.Test.wrapper` rather than intended `example.MyMacro.wrapper`. To avoid referential transparency gotchas one can use two possible workarounds: + +1. Fully qualify all references. i.e. we can adapt our macros' implementation to: + + def impl(c: Context)(x: c.Tree) = { + import c.universe._ + q"_root_.example.MyMacro.wrapper($x)" + } + + It's important to start with `_root_` as otherwise there will still be a chance of name collision if `example` gets redefined at use-site of the macro. + +2. Unquote symbols instead of using plain identifiers. i.e. we can resolve reference to wrapper by hand: + + def impl(c: Context)(x: c.Tree) = { + import c.universe._ + val myMacro = symbolOf[MyMacro.type].asClass.module + val wrapper = myMacro.info.member(TermName("wrapper")) + q"$wrapper($x)" + } + +## Hygiene in the narrow sense {:#hygiene-in-the-narrow-sense} + +What hygiene in the narrow sense means is that quasiquotes shouldn't mess with the bindings of trees that are unquoted into them. For example, if a macro argument unquoted into a macro expansion was originally referring to some variable in enclosing lexical context, then this reference should remain in force after macro expansion, regardless of what code was generated for the macro expansion. Unfortunately, we don't have automatic facilities to ensure this, and that can lead to unexpected situations: + + scala> val originalTree = q"val x = 1; x" + originalTree: universe.Tree = ... + + scala> toolbox.eval(originalTree) + res1: Any = 1 + + scala> val q"$originalDefn; $originalRef" = originalTree + originalDefn: universe.Tree = val x = 1 + originalRef: universe.Tree = x + + scala> val generatedTree = q"$originalDefn; { val x = 2; println(x); $originalRef }" + generatedTree: universe.Tree = ... + + scala> toolbox.eval(generatedTree) + 2 + res2: Any = 2 + +In the example the definition of `val x = 2` shadows the binding from `x` to `val x = 1` established in the original tree, changing the semantics of `originalRef` in generated code. In this simple example, shadowing is quite easy to follow, however in elaborate macros it can easy get out of hand. + +To avoid these issues, there's a battle-tested workaround from the times of early Lisp - having a function that creates unique names to be used in generated code. In Lisp parlance it's called gensym, whereas in Scala we call it freshName. Quasiquotes are particularly nice here, because they allow unquoting of generated names directly into generated code. + +There's a bit of a mixup in our API, though. There is an internal API `internal.reificationSupport.freshTermName/freshTypeName` available in both compile-time and runtime universes, however only at compile-time there's a pretty public facade for it, called `c.freshName`. We plan to fix this in Scala 2.12. + + scala> val xfresh = universe.internal.reificationSupport.freshTermName("x$") + xfresh: universe.TermName = x$1 + + scala> val generatedTree = q"$originalDefn; { val $xfresh = 2; println($xfresh); $originalRef }" + generatedTree: universe.Tree = ... + + scala> toolbox.eval(generatedTree) + 2 + res2: Any = 1 + + diff --git a/overviews/quasiquotes/intro.md b/overviews/quasiquotes/intro.md new file mode 100644 index 0000000000..ecd138bd6c --- /dev/null +++ b/overviews/quasiquotes/intro.md @@ -0,0 +1,192 @@ +--- +layout: overview-large +title: Introduction + +disqus: true + +partof: quasiquotes +num: 1 +outof: 12 +--- +**Denys Shabalin** EXPERIMENTAL + +## Before you start {:#before-you-start} + +Before you start reading this guide it's recommended to start a Scala REPL with one extra line: + + scala> val universe = reflect.runtime.universe; import universe._ + +REPL is the best place to explore quasiquotes and this guide will use it extensively to demonstrate handling of trees. All of the examples will assume that import. + +Additionally some examples that use `ToolBox` API might need a few more lines to get things rolling: + + scala> import reflect.runtime.currentMirror + scala> import tools.reflect.ToolBox + scala> val toolbox = currentMirror.mkToolBox() + +Another tool you might want to be aware of is new and shiny `showCode` pretty printer (contributed by [@VladimirNik](https://github.com/VladimirNik)): + + scala> val C = q"class C" + C: universe.ClassDef = + class C extends scala.AnyRef { + def () = { + super.(); + () + } + } + + scala> println(showCode(C)) + class C + +Default pretty printer shows you contents of the tree in imaginary low-level Scala-like notation. `showCode` on the other hand will do its best to reconstruct actual source code equivalent to the given tree in proper Scala syntax. + +On the other side of spectrum there is also a `showRaw` pretty printer that shows direct internal organization of the tree: + + scala> println(showRaw(q"class C")) + ClassDef(Modifiers(), TypeName("C"), List(), Template(List(Select(Ident(scala), TypeName("AnyRef"))), noSelfType, List(DefDef(Modifiers(), termNames.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(pendingSuperCall), Literal(Constant(()))))))) + +## Basics {:#basics} + +Quasiquotes are a neat notation that lets you manipulate Scala syntax trees with ease: + + scala> val tree = q"i am { a quasiquote }" + tree: universe.Tree = i.am(a.quasiquote) + +Every time you wrap a snippet of code into `q"..."` quotation it would become a tree that represents given snippet. As you might have already noticed quotation syntax is in just another usage of extensible string interpolation introduced in 2.10. Although they look like strings they operate on syntactic trees under the hood. + +The same syntax can be used to match trees as patterns: + + scala> println(tree match { case q"i am { a quasiquote }" => "it worked!" }) + it worked! + +Whenever you match a tree with a quasiquote it would match whenever a *structure* of given tree is equivalent to the one you\'ve provided as a pattern. You can check for structural equality manually with the help of `equalsStructure` method: + + scala> println(q"foo + bar" equalsStructure q"foo.+(bar)") + true + +You can also put things into quasiquotation with the help of `$`: + + scala> val aquasiquote = q"a quasiquote" + aquasiquote: universe.Select = a.quasiquote + + scala> val tree = q"i am { $aquasiquote }" + tree: universe.Tree = i.am(a.quasiquote) + +This operation is also known as unquoting. Whenever you unquote an expression of `Tree` type in a quasiquote it will *structurally substitute* that tree into that location. Most of the time such substitution between quotes is equivalent to textual substitution of the source code. + +Similarly one can structurally deconstruct a tree using unquoting in pattern matching: + + scala> val q"i am $what" = q"i am { a quasiquote }" + what: universe.Tree = a.quasiquote + +## Interpolators {:#interpolators} + +Scala is the language with rich syntax that differs greatly depending on the syntactical context: + + scala> val x = q""" + val x: List[Int] = List(1, 2) match { + case List(a, b) => List(a + b) + } + """ + x: universe.ValDef = + val x: List[Int] = List(1, 2) match { + case List((a @ _), (b @ _)) => List(a.$plus(b)) + } + +In this example we see three primary contexts being used: + +1. `List(1, 2)` and `List(a + b)` are expressions +2. `List[Int]` is a type +3. `List(a, b)` is a pattern + +Each of this contexts is covered by separate interpolator: + +   | Used for +----|---------------- + q | [expressions](/overviews/quasiquotes/syntax-summary.html#exprs), [definitions](/overviews/quasiquotes/syntax-summary.html#defns) and [imports](http://localhost:4000/overviews/quasiquotes/expression-details.html#import) + tq | [types](/overviews/quasiquotes/syntax-summary.html#types) + pq | [patterns](/overviews/quasiquotes/syntax-summary.html#pats) + +Syntactical similiarity between different contexts doesn\'t imply similarity between underlying trees: + + scala> println(q"List[Int]" equalsStructure tq"List[Int]") + false + +If we peek under the hood we'll see that trees are indeed different: + + scala> println(showRaw(q"List[Int]")) + TypeApply(Ident(TermName("List")), List(Ident(TypeName("Int")))) + + scala> println(showRaw(tq"List[Int]")) + AppliedTypeTree(Ident(TypeName("List")), List(Ident(TypeName("Int")))) + +Similarly patterns and expressions are not equivalent either: + + scala> println(pq"List(a, b)" equalsStructure q"List(a, b)") + false + +So it's extremely important to use the right interpotor for the job to construct a valid syntax tree. + +Additionally there are two auxilary interpolators that let you work with minor areas of scala syntax: + +   | Used for +----|------------------------------------- + cq | [case clause](/overviews/quasiquotes/syntax-summary.html#aux) + fq | [for loop enumerator](/overviews/quasiquotes/syntax-summary.html#aux) + +See [syntax summary](/overviews/quasiquotes/syntax-summary.html) section for details. + +## Splicing {:#splicing} + +Unquote splicing is a way to unquote a variable number of elements: + + scala> val ab = List(q"a", q"b") + scala> val fab = q"f(..$ab)" + fab: universe.Tree = f(a, b) + +Dots near unquotee annotate degree of flattenning and are also called splicing rank. `..$` expects argument to be an `Iterable[Tree]` and `...$` expects `Iterable[Iterable[Tree]]`. + +Splicing can be easily combined with regular unquotation: + + scala> val c = q"c" + scala> val fabc = q"f(..$ab, $c)" + fabc: universe.Tree = f(a, b, c) + + scala> val fcab = q"f($c, ..$ab)" + fcab: universe.Tree = f(c, a, b) + + scala> val fabcab = q"f(..$ab, $c, ..$ab)" + fabcab: universe.Tree = f(a, b, c, a, b) + +If you want to abstract over applications even further you can use `...$`: + + scala> val argss = List(ab, List(c)) + arglists: List[List[universe.Ident]] = List(List(a, b), List(c)) + + scala> val fargss = q"f(...$argss)" + fargss: universe.Tree = f(a, b)(c) + +At the moment `...$` splicing is only supported for function applications and parameter lists in def and class definitions. + +Similarly to construction one can also use `..$` and `...$` to tear trees apart: + + scala> val q"f(..$args)" = q"f(a, b)" + args: List[universe.Tree] = List(a, b) + + scala> val q"f(...$argss)" = q"f(a, b)(c)" + argss: List[List[universe.Tree]] = List(List(a, b), List(c)) + +Although there are some limitations to the way to you can combine it with regular `$` variable extraction: + + case q"f($first, ..$rest)" => // ok + case q"f(..$init, $last)" => // ok + case q"f(..$a, ..$b)" => // not allowed + +So in general only one `..$` is allowed per given list. Similar restrictions also apply to `...$`: + + case q"f(..$first)(...$rest)" => // ok + case q"f(...$init)(..$first)" => // ok + case q"f(...$a)(...$b)" => // not allowed + +In this section we only worked with function arguments but the same splicing rules are true for all syntax forms with variable amount of elements. [Syntax summary](/overviews/quasiquotes/syntax-summary.html) and corresponding details sections demonstrate how you can splice into other syntactic forms. + diff --git a/overviews/quasiquotes/lifting.md b/overviews/quasiquotes/lifting.md new file mode 100644 index 0000000000..69ed4b2ff9 --- /dev/null +++ b/overviews/quasiquotes/lifting.md @@ -0,0 +1,158 @@ +--- +layout: overview-large +title: Lifting + +disqus: true + +partof: quasiquotes +num: 2 +outof: 12 +--- +**Denys Shabalin** EXPERIMENTAL + +Lifting is and extensible way to unquote custom data types in quasiquotes. Its primary use-case is support unquoting of [literal](/overviews/quasiquotes/expression-details.html#literal) values and a number of reflection primitives as trees: + + scala> val two = 1 + 1 + two: Int = 2 + + scala> val four = "$two + $two" + four: universe.Tree = 2.$plus(2) + +This code runs successfully because `Int` is considered to be `Liftable` by default. `Liftable` type is just a trait with a single absract method that defines a mapping of given type to tree: + + trait Liftable[T] { + def apply(value: T): Tree + } + +Whenever there is implicit value of `Liftable[T]` is available one can unquote `T` in quasiquote. This design pattern is known as a type class. You can read more about it in ["Type Classes as Objects and Implicits"](http://ropas.snu.ac.kr/~bruno/papers/TypeClasses.pdf). + +A number of data types that are supported natively by quasiquotes will never triger usage of `Liftable` representation even if it\'s available: subtypes of `Tree`, `Symbol`, `Name`, `Modifiers` and `FlagSet`. + +One can also combine lifting and unquote splicing: + + scala> val ints = List(1, 2, 3) + scala> val f123 = q"f(..$ints)" + f123: universe.Tree = f(1, 2, 3) + + scala> val intss = List(List(1, 2, 3), List(4, 5), List(6)) + scala> val f123456 = q"f(...$intss)" + f123456: universe.Tree = f(1, 2, 3)(4, 5)(6) + +In this case each element of the list will be lifted separately and the result will be spliced right in. + +## Bring your own {:#bring-your-own} + +To define tree representation for your own data type just provide an implicit instance of `Liftable` for it: + + package points + + import scala.universe._ + + case class Point(x: Int, y: Int) + object Point { + implicit val lift = Liftable[Point] { p => + q"_root_.points.Point(${p.x}, ${p.y})" + } + } + +This way whenever a value of Point type is unquoted in runtime quasiquote it will be automatically transformed +into a case class constructor call. In this example there two important points to take into account: + +0. Liftable companion contains helper `apply` method to simplifies creation of `Liftable` instances. + It takes a single type parameter `T` and a `T => Tree` function as a single value parameter and + returns a `Liftable[T]`. + +1. Here we only defined `Liftable` for runtime reflection. It won't be found if you try to + use it from a macro due to the fact that each universe contains its own `Liftable` which is not + compatible with the others. This problem is caused by path-dependant nature of current reflection + api. (see [sharing liftable implementation between universes](#reusing-impl)) + +2. Due to lack of [hygiene](/overviews/quasiquotes/hygiene.html), reference to point companion + has to be fully qualified to ensure correctness in of this tree in every possible context. Another + way to workaround reference issue is to use symbols to refer to things: + + val PointSym = symbolOf[Point].companionModule + implicit val lift = Liftable[Point] { p => + q"$PointSym(${p.x}, ${p.y})" + } + +## Standard Liftables {:#standard} + + Type | Value | Representation +--------------------------------|-----------------------|--------------- + `Byte`, `Short`, `Int`, `Long` | `0` | `q"0"` + `Float` | `0.0` | `q"0.0"` + `Double` | `0.0D` | `q"0.0D"` + `Boolean` | `true`, `false` | `q"true"`, `q"false"` + `Char` | `'c'` | `q"'c'"` + `Unit` | `()` | `q"()"` + `String` | `"string"` | `q""" "string" """` + `Symbol` | `'symbol` | `q"'symbol"` + `Array[T]` † | `Array(1, 2)` | `q"s.Array(1, 2)"` ‡ + `Option[T]` † | `Some(1)` | `q"s.Some(1)"` ‡ + `Vector[T]` † | `Vector(1, 2)` | `q"s.c.i.Vector(1, 2)"` ‡ + `List[T]` † | `List(1, 2)` | `q"s.c.i.List(1, 2)"` ‡ + `Map[K, V]` † | `Map(1 -> 2)` | `q"s.c.i.Map((1, 2))"` ‡ + `Set[T]` † | `Set(1, 2)` | `q"s.c.i.Set(1, 2)"` ‡ + `Either[L, R]` † | `Left(1)` | `q"s.u.Left(1)"` ‡ + `TupleN[...]` \* † | `(1, 2)` | `q"(1, 2)"` + `TermName` | `TermName("foo")` | `q"foo"` + `TypeName` | `TypeName("foo")` | `tq"foo"` + `Tree` | `tree` | `tree` + `Expr` | `expr` | `expr.tree` + `Type` | `typeOf[Int]` | `TypeTree(typeof[Int])` + `TypeTag` | `ttag` | `TypeTree(ttag.tpe)` + `Constant` | `const` | `Literal(const)` + + (\*) Liftable for tuples is defined for all `N` in `[2, 22]` range. + + (†) All type parameters have to be Liftable themselves. + + (‡) `s.` is shorthand for scala, `s.c.i.` for `scala.collection.immutable`, `s.u.` for `scala.util.` + +## Reusing Liftable implementation between universes {:#reusing-impl} + +Due to path dependent nature of current reflection API it isn't trivial to share the same Liftable definition between both macro and runtime universes. A possible way to do this is to define Liftable implementations in a trait and instantiate it for each universe separately: + + import reflect.api.Universe + import reflect.macros.blackbox.Context + + trait LiftableImpls { + val universe: Universe + import universe._ + + implicit val liftPoint = Liftable[points.Point] { p => + q"_root_.points.Point(${p.x}, ${p.y})" + } + } + + object RuntimeLiftableImpls extends LiftableImpls { + val universe: universe.type = universe + } + + trait MacroLiftableImpls extends LiftableImpls { + val c: Context + val universe: c.universe.type = c.universe + } + + // macro impls defined as a bundle + class MyMacro(val c: Context) extends MacroLiftableImpls { + // ... + } + +So in practice it's much easier to just define a liftable for given universe at hand: + + import reflect.macros.blackbox.Context + + // macro impls defined as a macro bundle + class MyMacros(c: Context) { + import c.universe._ + + implicit val liftPoint = Liftable[points.Point] { p => + q"_root_.points.Point(${p.x}, ${p.y})" + } + + // ... + } + + diff --git a/overviews/quasiquotes/pattern-details.md b/overviews/quasiquotes/pattern-details.md new file mode 100644 index 0000000000..aa897c6020 --- /dev/null +++ b/overviews/quasiquotes/pattern-details.md @@ -0,0 +1,114 @@ +--- +layout: overview-large +title: Pattern details + +disqus: true + +partof: quasiquotes +num: 9 +outof: 12 +--- +**Denys Shabalin** EXPERIMENTAL + +## Wildcard Pattern {:#wildcard} + +Wildcard pattern (`pq"_"`) is the simplest form of pattern that matches any input. + +## Literal Pattern {:#literal} + +Literal patterns are equivalent to literal expressions on AST level: + + scala> val equivalent = pq"1" equalsStructure q"1" + equivalent: Boolean = true + +See chapter on [literal expressions](/overviews/quasiquotes/expression-details.html#literal) for details. + +## Binding Pattern {:#binding} + +Binding pattern is a way to name pattern or one it's part as local variable: + + scala> val bindtup = pq"foo @ (1, 2)" + bindtup: universe.Bind = (foo @ scala.Tuple2(1, 2)) + + scala> val pq"$name @ $pat" = bindtup + name: universe.Name = foo + pat: universe.Tree = scala.Tuple2(1, 2) + +Binding without explicit pattern is equivalent to the one with wildcard pattern: + + scala> val pq"$name @ $pat" = pq"foo" + name: universe.Name = foo + pat: universe.Tree = _ + +See also [type pattern](#type) for an example of type variable binding. + +## Extractor Pattern {:#extractor} + +Extractors are a neat way to delegate a pattern matching to another object's unapply method: + + scala> val extractor = pq"Foo(1, 2, 3)" + extractor: universe.Tree = Foo(1, 2, 3) + + scala> val pq"$id(..$pats)" = extractor + id: universe.Tree = Foo + pats: List[universe.Tree] = List(1, 2, 3) + +## Type Pattern {:#type} + +Type patterns are a way to check type of a scrutinee: + + scala> val isT = pq"_: T" + isT: universe.Typed = (_: T) + + scala> val pq"_: $tpt" = isT + tpt: universe.Tree = T + +Combination of non-wildcard name and type pattern is represented as bind over wildcard type pattern: + + scala> val fooIsT = pq"foo: T" + fooIsT: universe.Bind = (foo @ (_: T)) + + scala> val pq"$name @ (_: $tpt)" = fooIsT + name: universe.Name = foo + tpt: universe.Tree = T + +Another important thing to mention is a type variable patterns: + + scala> val typevar = pq"_: F[t]" + typevar: universe.Typed = (_: F[(t @ )]) + +One can construct (and similarly deconstruct) such patterns by following steps: + + scala> val name = TypeName("t") + scala> val empty = q"" + scala> val t = pq"$name @ $empty" + scala> val tpt = tq"F[$t]" + scala> val typevar = pq"_: $tpt" + typevar: universe.Typed = (_: F[(t @ _)]) + +## Alternative Pattern {:#alternative} + +Pattern alternatives represent a pattern that matches whenever at least one of the branches matches: + + scala> val alt = pq"Foo() | Bar() | Baz()" + alt: universe.Alternative = (Foo()| Bar()| Baz()) + + scala> val pq"$first | ..$rest" = alt + head: universe.Tree = Foo() + tail: List[universe.Tree] = List(Bar(), Baz()) + + scala> val pq"..$init | $last" = alt + init: List[universe.Tree] = List(Foo(), Bar()) + last: universe.Tree = Baz() + +## Tuple Pattern {:#tuple} + +Similarly to [tuple expressions](/overviews/quasiquotes/expression-details.html#tuple) and [tuple types](/overviews/quasiquotes/type-details.html#tuple), tuple patterns are just a syntactic sugar that expands as `TupleN` extractor: + + scala> val tup2pat = pq"(a, b)" + tup2pat: universe.Tree = scala.Tuple2((a @ _), (b @ _)) + + scala> val pq"(..$pats)" = tup2pat + pats: List[universe.Tree] = List((a @ _), (b @ _)) + + diff --git a/overviews/quasiquotes/syntax-summary.md b/overviews/quasiquotes/syntax-summary.md new file mode 100644 index 0000000000..b659211e4b --- /dev/null +++ b/overviews/quasiquotes/syntax-summary.md @@ -0,0 +1,182 @@ +--- +layout: overview-large +title: Syntax summary + +disqus: true + +partof: quasiquotes +num: 6 +outof: 12 +--- +**Denys Shabalin** EXPERIMENTAL + +## Expressions {:#exprs} + + +   | Quasiquote | Type +-------------------------|------------------------------------------------------------------|------------------------- + [Empty][101] | `q""` | EmptyTree + [Literal][102] | `q"$value"` | Literal + [Identifier][103] | `q"$tname"` or `q"name"` | Ident + [Selection][103] | `q"$expr.$tname"` | Select + [Super Selection][104] | `q"$tpname.super[$tpname].$tname"` | Select + [This][104] | `q"$tpname.this"` | This + [Application][105] | `q"$expr(...$exprss)"` | Apply + [Type Application][105] | `q"$expr[..$tpts]"` | TypeApply + [Assign][106] | `q"$expr = $expr"` | Assign, AssignOrNamedArg + [Update][106] | `q"$expr(..$exprs) = $expr"` | Tree + [Return][107] | `q"return $expr"` | Return + [Throw][108] | `q"throw $expr"` | Throw + [Ascription][109] | `q"$expr: $tpt"` | Typed + [Annotated][110] | `q"$expr: @$annot"` | Annotated + [Tuple][111] | `q"(..$exprs)"` | Tree + [Block][112] | `q"{ ..$stats }"` | Block + [If][113] | `q"if ($expr) $expr else $expr"` | If + [Pattern Match][114] | `q"$expr match { case ..$cases }"` | Match + [Try][115] | `q"try $expr catch { case ..$cases } finally $expr"` | Try + [Function][116] | `q"(..$params) => $expr"` | Function + [Partial Function][117] | `q"{ case ..$cases }"` | Match + [While Loop][118] | `q"while ($expr) $expr"` | LabelDef + [Do-While Loop][118] | `q"do $expr while ($expr)"` | LabelDef + [For Loop][119] | `q"for (..$enums) $expr"` | Tree + [For-Yield Loop][119] | `q"for (..$enums) yield $expr"` | Tree + [New][120] | `q"new { ..$earlydefns } with ..$parents { $self => ..$stats }"` | Tree + XML Literal | Not natively supported | Tree + + +[101]: /overviews/quasiquotes/expression-details.html#empty +[102]: /overviews/quasiquotes/expression-details.html#literal +[103]: /overviews/quasiquotes/expression-details.html#ref +[104]: /overviews/quasiquotes/expression-details.html#super-this +[105]: /overviews/quasiquotes/expression-details.html#application +[106]: /overviews/quasiquotes/expression-details.html#assign-update +[107]: /overviews/quasiquotes/expression-details.html#return +[108]: /overviews/quasiquotes/expression-details.html#throw +[109]: /overviews/quasiquotes/expression-details.html#ascription +[110]: /overviews/quasiquotes/expression-details.html#annotated +[111]: /overviews/quasiquotes/expression-details.html#tuple +[112]: /overviews/quasiquotes/expression-details.html#block +[113]: /overviews/quasiquotes/expression-details.html#if +[114]: /overviews/quasiquotes/expression-details.html#match +[115]: /overviews/quasiquotes/expression-details.html#try +[116]: /overviews/quasiquotes/expression-details.html#function +[117]: /overviews/quasiquotes/expression-details.html#partial-function +[118]: /overviews/quasiquotes/expression-details.html#while +[119]: /overviews/quasiquotes/expression-details.html#for +[120]: /overviews/quasiquotes/expression-details.html#new + +## Types {:#types} + +   | Quasiquote | Type +-----------------------------|---------------------------------------|--------------------- + [Empty Type][201] | `tq""` | TypeTree + [Type Identifier][202] | `tq"$tpname"` or `tq"Name"` | Ident + [Singleton Type][203] | `tq"$ref.type"` | SingletonType + [Type Projection][204] | `tq"$tpt#$tpname"` | SelectFromTypeTree + [Type Selection][204] | `tq"$ref.$tpname"` | Select + [Super Type Selection][204] | `tq"$tpname.super[$tpname].$tpname"` | Select + [This Type Selection][204] | `tq"this.$tpname"` | Select + [Applied Type][205] | `tq"$tpt[..$tpts]"` | AppliedTypeTree + [Annotated Type][206] | `tq"$tpt @$annots"` | Annotated + [Compound Type][207] | `tq"..$parents { ..$defns }"` | CompoundTypeTree + [Existential Type][208] | `tq"$tpt forSome { ..$defns }"` | ExistentialTypeTree + [Tuple Type][209] | `tq"(..$tpts)"` | Tree + [Function Type][210] | `tq"(..$tpts) => $tpt"` | Tree + +[201]: /overviews/quasiquotes/type-details.html#empty +[202]: /overviews/quasiquotes/type-details.html#ident +[203]: /overviews/quasiquotes/type-details.html#singleton +[204]: /overviews/quasiquotes/type-details.html#projection +[205]: /overviews/quasiquotes/type-details.html#applied +[206]: /overviews/quasiquotes/type-details.html#annotated +[207]: /overviews/quasiquotes/type-details.html#compound +[208]: /overviews/quasiquotes/type-details.html#existential +[209]: /overviews/quasiquotes/type-details.html#tuple +[210]: /overviews/quasiquotes/type-details.html#function + +## Patterns {:#pats} + +   | Quasiquote | Type +----------------------------|------------------------|------------------- + [Wildcard Pattern][301] | `pq"_"` | Ident + [Literal Pattern][302] | `pq"$value"` | Literal + [Binding Pattern][303] | `pq"$name @ $pat"` | Bind + [Extractor Pattern][304] | `pq"$ref(..$pats)"` | Apply, UnApply + [Type Pattern][305] | `pq"_: $tpt"` | Typed + [Alternative Pattern][306] | `pq"$first │ ..$rest"` | Alternative + [Tuple Pattern][307] | `pq"(..$pats)"` | Apply, UnApply + XML Pattern | Not natively supported | Tree + +[301]: /overviews/quasiquotes/pattern-details.html#wildcard +[302]: /overviews/quasiquotes/pattern-details.html#literal +[303]: /overviews/quasiquotes/pattern-details.html#binding +[304]: /overviews/quasiquotes/pattern-details.html#extractor +[305]: /overviews/quasiquotes/pattern-details.html#type +[306]: /overviews/quasiquotes/pattern-details.html#alternative +[307]: /overviews/quasiquotes/pattern-details.html#tuple + +## Definitions {:#defns} + +   | Quasiquote | Type +------------------------------|-----------------------------------------------------------------------------------------------------------------------------|----------- + [Val][401] | `q"$mods val $tname: $tpt = $expr"` or `q"$mods val $pat = $expr"` | ValDef + [Var][401] | `q"$mods var $tname: $tpt = $expr"` or `q"$mods val $pat = $expr"` | ValDef + [Val Pattern][403] | `q"$mods val $pat: $tpt = $expr"` | Tree + [Var Pattern][404] | `q"$mods var $pat: $tpt = $expr"` | Tree + [Method][403] | `q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr"` | DefDef + [Secondary Constructor][404] | `q"$mods def this(...$paramss) = this(..$argss)"` | DefDef + [Type][405] | `q"$mods type $tpname[..$tparams] = $tpt"` | TypeDef + [Class][406] | `q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }"` | ClassDef + [Trait][407] | `q"$mods trait $tpname[..$tparams] extends { ..$earlydefns } with ..$parents { $self => ..$stats }"` | TraitDef + [Object][408] | `q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body }"` | ModuleDef + [Package][409] | `q"package $ref { ..$topstats }"` | PackageDef + [Package Object][410] | `q"package object $tname extends { ..$earlydefns } with ..$parents { $self => ..$stats }"` | PackageDef + +[401]: /overviews/quasiquotes/definition-details.html#val-var +[402]: /overviews/quasiquotes/definition-details.html#pattern +[403]: /overviews/quasiquotes/definition-details.html#method +[404]: /overviews/quasiquotes/definition-details.html#ctor +[405]: /overviews/quasiquotes/definition-details.html#type +[406]: /overviews/quasiquotes/definition-details.html#class +[407]: /overviews/quasiquotes/definition-details.html#trait +[408]: /overviews/quasiquotes/definition-details.html#object +[409]: /overviews/quasiquotes/definition-details.html#package +[410]: /overviews/quasiquotes/definition-details.html#package-object + +## Auxiliary {:#aux} + +   | Quasiquote | Type +------------------------------------|-----------------------------|-------- + [Import][501] | `q"import $ref.{..$sels}"` | Import + [Case Clause][502] | `cq"$pat if $expr => expr"` | CaseDef + [Generator Enumerator][503] | `fq"$pat <- $expr"` | Tree + [Value Definition Enumerator][503] | `fq"$pat = $expr"` | Tree + [Guard Enumerator][503] | `fq"if $expr"` | Tree + + +[501]: /overviews/quasiquotes/expression-details.html#import +[502]: /overviews/quasiquotes/expression-details.html#match +[503]: /overviews/quasiquotes/expression-details.html#for + +## Abbreviations {:#abbrev} + +Prefixes of unquotees imply the following: + +* `name: Name`, `tname: TermName`, `tpname: TypeName` +* `value: T` where `T` is value type that corresponds to given literal (e.g. `Int`, `Char`, `Float` etc) +* `expr: Tree` an [expression tree](#exprs) +* `tpt: Tree` a [type tree](#types) +* `pat: Tree` a [pattern tree](#pats) +* `defn: Tree` a [definition tree](#defns) +* `earlydefn: Tree` an early definion tree ([val](/overviews/quasiquotes/definition-details.html#val-var) or [type definition](/overviews/quasiquotes/definition-details.html#type)) +* `self: Tree` a self definition tree (i.e. [val definition](/overviews/quasiquotes/definition-details.html#val-var)) +* `stat: Tree` a statement tree ([definition](#defns), [expression](#exprs) or an [import](/overviews/quasiquotes/expression-details.html#import)) +* `topstat: Tree` a top-level statement tree ([class](/overviews/quasiquotes/definition-details.html#class), [trait](/overviews/quasiquotes/definition-details.html#trait), [package](/overviews/quasiquotes/definition-details.html#package), [package object](/overviews/quasiquotes/definition-details.html#package-object) or [import](/overviews/quasiquotes/expression-details.html#import)) +* `enum: Tree` a [for loop](/overviews/quasiquotes/expression-details.html#for) enumerator +* `param: Tree` a value parameter tree (i.e. [val definition](/overviews/quasiquotes/definition-details.html#val-var)) +* `tparam: Tree` a type paremeter tree (i.e. [type definition](/overviews/quasiquotes/definition-details.html#type)) +* `parent: Tree` a [template](/overviews/quasiquotes/definition-details.html#templates) parent +* `sel: Tree` an [import](/overviews/quasiquotes/expression-details.html#import) selector tree + +Whenever a name has suffix `s` it means that it is a List of something. `ss` means List of Lists. So for example `exprss` means a List of Lists of expressions. + diff --git a/overviews/quasiquotes/terminology.md b/overviews/quasiquotes/terminology.md new file mode 100644 index 0000000000..c06bb5fcfd --- /dev/null +++ b/overviews/quasiquotes/terminology.md @@ -0,0 +1,22 @@ +--- +layout: overview-large +title: Terminology summary + +disqus: true + +partof: quasiquotes +num: 11 +outof: 12 +--- +EXPERIMENTAL + +* **Quasiquote** (not quasi-quote) can refer to either quasiquote library or any usage of one it's [interpolators](/overviews/quasiquotes/intro.html#interpolators). The name is not hyphenated for sake of consistency with implementations of the same concept in other languages (e.g. [Scheme and Racket](http://docs.racket-lang.org/reference/quasiquote.html), [Haskell](http://www.haskell.org/haskellwiki/Quasiquotation)) +* **Tree** or **AST** (Abstract Syntax Tree) is representation of Scala program or a part of it through means of Scala reflection API's Tree type. +* **Tree construction** refers to usages of quasiquotes as expressions to represent creation of new tree values. +* **Tree deconstruction** refers to usages of quasiquotes as patterns to structurally tear trees apart. +* **Unquoting** is a way of either putting thing in or extracting things out of quasiquote. Can be performed with `$` syntax within a quasiquote. +* **Unquote splicing** (or just splicing) is another form of unquoting that flattens contents of the unquotee into a tree. Can be performed with either `..$` or `...$` syntax. +* **Rank** is a degree of flattenning of unquotee: `rank($) == 0`, `rank(..$) == 1`, `rank(...$) == 2`. +* [**Lifting**](/overviews/quasiquotes/lifting.html) is a way to unquote non-tree values and transform them into trees with the help of Liftable typeclass. +* [**Unlifting**](/overviews/quasiquotes/unlifting.html) is a way to unquote non-tree values out of quasiquote patterns with the help of Unliftable typeclass. + diff --git a/overviews/quasiquotes/type-details.md b/overviews/quasiquotes/type-details.md new file mode 100644 index 0000000000..112629a216 --- /dev/null +++ b/overviews/quasiquotes/type-details.md @@ -0,0 +1,185 @@ +--- +layout: overview-large +title: Type details + +disqus: true + +partof: quasiquotes +num: 8 +outof: 12 +--- +**Denys Shabalin** EXPERIMENTAL + +## Empty Type {:#empty} + +Empty type (`tq""`) is a canonical way to say that type at given location isn't given by the user and should be inferred by the compiler: + +1. [Method](/overviews/quasiquotes/definition-details.html#method) with unknown return type +2. [Val or Var](/overviews/quasiquotes/definition-details.html#val-var) with unknown type +3. [Anonymous function](/overviews/quasiquotes/expression-details.html#function) with unknown argument type + +## Type Identifier {:#ident} + +Similarly to [term identifiers](/overviews/quasiquotes/expression-details.html#ref) one can construct a type identifier based on a name: + + scala> val name = TypeName("Foo") + name: universe.TypeName = Foo + + scala> val foo = tq"$name" + foo: universe.Ident = Foo + +And deconstruct it back through [unlifting](/overviews/quasiquotes/unlifting.html): + + scala> val tq"${name: TypeName}" = tq"Foo" + name: universe.TypeName = Foo + +It's recommended to always ascribe name as `TypeName` when you work with type identifiers. Non-ascribed pattern is equivalent to just a pattern variable binding. + +## Singleton Type {:#singleton} + +A singleton type is a way to express a type of a term definition that is being referenced: + + scala> val singleton = tq"foo.bar.type".sr + singleton: String = SingletonTypeTree(Select(Ident(TermName("foo")), TermName("bar"))) + + scala> val tq"$ref.type" = tq"foo.bar.type" + ref: universe.Tree = foo.bar + +## Type Projection {:#projection} + +Type projection is a fundamental way to select types as members of other types: + + scala> val proj = tq"Foo#Bar" + proj: universe.SelectFromTypeTree = Foo#Bar + + scala> val tq"$foo#$bar" = proj + foo: universe.Tree = Foo + bar: universe.TypeName = Bar + +Similarly to identifiers it\'s recommended to always ascribe name as `TypeName`. Non-ascribed matching behaviour might change in the future. + +As a convenience one can also select type members of terms: + + scala> val int = tq"scala.Int" + int: universe.Select = scala.Int + + scala> val tq"scala.$name" = int + name: universe.TypeName = Int + +But semantically such selections are just a shortcut for a combination of singleton types and type projections: + + scala> val projected = tq"scala.type#Int" + projected: universe.SelectFromTypeTree = scala.type#Int + +Lastly [similarly to expressions](/overviews/quasiquotes/expression-details.html#super-this) one can select members through super and this: + + scala> val superbar = tq"super.Bar" + superbar: universe.Select = super.Bar + + scala> val tq"$pre.super[$parent].$field" = superbar + pre: universe.TypeName = + parent: universe.TypeName = + field: universe.Name = Bar + + scala> val thisfoo = tq"this.Foo" + thisfoo: universe.Select = this.Foo + + scala> val tq"this.${tpname: TypeName}" = thisfoo + tpname: universe.TypeName = Foo + +## Applied Type {:#applied} + +Instantiations of parametized types can be expressed with the help of applied types (type-level equivalent of type application): + + scala> val applied = tq"Foo[A, B]" + applied: universe.Tree = Foo[A, B] + + scala> val tq"Foo[..$targs]" = applied + targs: List[universe.Tree] = List(A, B) + +Deconstruction of non-applied types will cause `targs` begin extracted as empty list: + + scala> val tq"Foo[..$targs]" = tq"Foo" + targs: List[universe.Tree] = List() + +## Annotated Type {:#annotated} + +Similarly to expressions types can be annotated: + + scala> val annotated = tq"T @Fooable" + annotated: universe.Annotated = T @Fooable + + scala> val tq"$tpt @$annot" = annotated + tpt: universe.Tree = T + annot: universe.Tree = Fooable + +## Compound Type {:#compound} + +Compound type lets users to express a combination of a number of types with optional refined member list: + + scala> val compound = tq"A with B with C" + compound: universe.CompoundTypeTree = A with B with C + + scala> val tq"..$parents { }" = compound + parents: List[universe.Tree] = List(A, B, C) + defns: List[universe.Tree] = List() + +Braces after parents are required to signal that this type is a compound type even if there are no refinements and we just want to extract a sequence of types combined with `with` keyword. + +On the other side of the spectrum are pure refinements without explicit parents (a.k.a. structural types): + + scala> val structural = tq"{ val x: Int; val y: Int }" + structural: universe.CompoundTypeTree = + scala.AnyRef { + val x: Int; + val y: Int + } + + scala> val tq"{ ..$defns }" = structural + defns: List[universe.Tree] = List(val x: Int, val y: Int) + +Here we can see that AnyRef is a parent that is inserted implicitly if we don't provide any. + +## Existential Type {:#existential} + +Existential types consist of a type tree and a list of definitions: + + scala> val tq"$tpt forSome { ..$defns }" = tq"List[T] forSome { type T }" + tpt: universe.Tree = List[T] + defns: List[universe.MemberDef] = List(type T) + +Alternatively there is also an underscrore notation: + + scala> val tq"$tpt forSome { ..$defns }" = tq"List[_]" + tpt: universe.Tree = List[_$1] + defns: List[universe.MemberDef] = List( type _$1) + +## Tuple Type {:#tuple} + +[Similarly to expressions](/overviews/quasiquotes/expression-details.html#tuple), tuple types are just a syntactic sugar over `TupleN` classes: + + scala> val tup2 = tq"(A, B)" + tup2: universe.Tree = scala.Tuple2[A, B] + + scala> val tq"(..$tpts)" = tup2 + tpts: List[universe.Tree] = List(A, B) + +Analagously `Unit` type is considered to be nullary tuple: + + scala> val tq"(..$tpts)" = tq"_root_.scala.Unit" + tpts: List[universe.Tree] = List() + +It's important to mention that pattern matching of reference to `Unit` is limited to either fully qualified path or a reference that contains symbols. (see [hygiene](/overviews/quasiquotes/hygiene.html)) + +## Function Type {:#function} + +Similarly to tuples, function types are a syntactic sugar over `FunctionN` classes: + + scala> val funtype = tq"(A, B) => C" + funtype: universe.Tree = _root_.scala.Function2[A, B, C] + + scala> val tq"..$foo => $bar" = funtype + foo: List[universe.Tree] = List(A, B) + bar: universe.Tree = C + + diff --git a/overviews/quasiquotes/unlifting.md b/overviews/quasiquotes/unlifting.md new file mode 100644 index 0000000000..469af83bdd --- /dev/null +++ b/overviews/quasiquotes/unlifting.md @@ -0,0 +1,102 @@ +--- +layout: overview-large +title: Unlifting + +disqus: true + +partof: quasiquotes +num: 3 +outof: 12 +--- +**Denys Shabalin** EXPERIMENTAL + +Unlifting is the reverse operation to [lifting](/overviews/quasiquotes/lifting.html): it takes a tree and recovers value from it: + + trait Unliftable[T] { + def unapply(tree: Tree): Option[T] + } + +Due to the fact that tree might not be a represention of our data type, the return type of unapply is `Option[T]` rather than just `T`. Such signature also makes it easy to use `Unliftable` instances as extractors. + +Whenever implicit instance of `Unliftable` is available for given data type you can use it for pattern matching with the help of 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` is extracted by default: + + scala> val q"foo.${bar: Int}" = q"foo.bar" + :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 it would unlift arguments of the function elementwise and wrap the result into a list. + +## Bring your own {:#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 needs to pay attention to a few nuances: + +0. Similarly to `Liftable`, `Unliftable` defines a helper `apply` function in companion + to simplify creation of `Unliftable` instances which takes a type parameter `T` and + a partial function `PartialFunction[Tree, T]` and returns `Unliftable[T]`. At all + inputs where partial function is defined it's expected to unconditionally return + instance of `T`. + +1. We only define `Unliftable` for runtime universe, it won't be available in macros. + (see [sharing liftable implementations](/overviews/quasiquotes/lifting.html#reusing-impl)) + +2. Pattern used in this unliftable will only match fully qualified reference to Point that + starts with `_root_`. It won't match other possible shapes of the reference and they have + to be specified by hand. This problem is caused by lack of [hygiene](/overviews/quasiquotes/hygiene.html). + +3. The pattern will also only match trees that have literal `Int` arguments. + It won't work for other expressions that might evaluate to `Int`. + +## Standard Unliftables {:#standard} + + 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. + + diff --git a/overviews/quasiquotes/usecases.md b/overviews/quasiquotes/usecases.md new file mode 100644 index 0000000000..70dd566893 --- /dev/null +++ b/overviews/quasiquotes/usecases.md @@ -0,0 +1,93 @@ +--- +layout: overview-large +title: Use cases + +disqus: true + +partof: quasiquotes +num: 5 +outof: 12 +--- +**Denys Shabalin** EXPERIMENTAL + +## AST manipulation in macros and compiler plugins {:#ast-manipulation} + +Quasiquotes were designed primary as tool for ast manipulation in macros. Common workflow is to deconstruct arguments with quasiquotes patterns and construct rewritten result with another quasiquote: + + // macro that prints expression code before executing it + object debug { + def apply[T](x: =>T): T = macro impl + def impl(c: Context)(x: c.Tree) = { import c.universe._ + val q"..$stats" = x + val loggedStats = stats.flatMap { stat => + val msg = "executing " + showCode(stat) + List(q"println($msg)", stat) + } + q"..$loggedStats" + } + } + + // usage + object Test extends App { + def faulty: Int = throw new Exception + debug { + val x = 1 + val y = x + faulty + x + y + } + } + + // output + executing val x: Int = 1 + executing val y: Int = x.+(Test.this.faulty) + java.lang.Exception + ... + +To simplify integration with macros we've also made it easier to just use trees in macro implementations instead of previous reify-centric `Expr` api: + + // 2.10 + object Macro { + def apply(x: Int): Int = macro impl + def impl(c: Context)(x: c.Expr[Int]): c.Expr[Int] = { import c.universe._ + c.Expr(q"$x + 1") + } + } + + // in 2.11 you can also do it like that + object Macro { + def apply(x: Int): Int = macro impl + def impl(c: Context)(x: c.Tree) = { import c.universe._ + q"$x + 1" + } + } + +You don't have to manually wrap return value of a macro into `c.Expr` or specify argument types twice any more. Return type in `impl` is optional too. + +Quasiquotes can also be used as is in compiler plugins as reflection api is strict subset of compiler's `Global` api. + +## Just in time compilation + +Thanks to `ToolBox` api one can generate, compile and run Scala code at runtime: + + scala> val code = q"""println("compiled and run at runtime!")""" + scala> val compiledCode = toolbox.compile(code) + scala> val result = compiledCode() + compiled and run at runtime! + result: Any = () + +## Offline code generation + +Thanks to new `showCode` pretty printer one can implement offline code generator that does AST manipulation with the help of quasiquotes and then serializes into actual source right before writing them to disk: + + object OfflineCodeGen extends App { + def generateCode() = + q"package mypackage { class MyClass }" + def saveToFile(path: String, code: Tree) = { + val writer = new java.io.PrintWriter(path) + try writer.write(showCode(code)) + finally writer.close() + } + saveToFile("myfile.scala", generateCode()) + } + + From fb638803b1b0b73d2d53e39c400796c129e0d52e Mon Sep 17 00:00:00 2001 From: Denys Shabalin Date: Mon, 21 Apr 2014 19:33:14 +0200 Subject: [PATCH 2/2] Add dependencies and setup section plus a redirect --- overviews/macros/quasiquotes.html | 11 ++++ overviews/quasiquotes/definition-details.md | 4 +- overviews/quasiquotes/expression-details.md | 4 +- overviews/quasiquotes/future.md | 4 +- overviews/quasiquotes/hygiene.md | 4 +- overviews/quasiquotes/intro.md | 41 +----------- overviews/quasiquotes/lifting.md | 4 +- overviews/quasiquotes/pattern-details.md | 4 +- overviews/quasiquotes/setup.md | 73 +++++++++++++++++++++ overviews/quasiquotes/syntax-summary.md | 4 +- overviews/quasiquotes/terminology.md | 4 +- overviews/quasiquotes/type-details.md | 4 +- overviews/quasiquotes/unlifting.md | 4 +- overviews/quasiquotes/usecases.md | 4 +- 14 files changed, 108 insertions(+), 61 deletions(-) create mode 100644 overviews/macros/quasiquotes.html create mode 100644 overviews/quasiquotes/setup.md diff --git a/overviews/macros/quasiquotes.html b/overviews/macros/quasiquotes.html new file mode 100644 index 0000000000..ce09a3793e --- /dev/null +++ b/overviews/macros/quasiquotes.html @@ -0,0 +1,11 @@ + + + + Redirecting you to the quasiquotes guide... + + +

Redirecting you to the quasiquotes guide...

+ + diff --git a/overviews/quasiquotes/definition-details.md b/overviews/quasiquotes/definition-details.md index e6e18962f4..9b9e3a6b40 100644 --- a/overviews/quasiquotes/definition-details.md +++ b/overviews/quasiquotes/definition-details.md @@ -5,8 +5,8 @@ title: Definition and import details disqus: true partof: quasiquotes -num: 10 -outof: 12 +num: 11 +outof: 13 --- **Denys Shabalin** EXPERIMENTAL diff --git a/overviews/quasiquotes/expression-details.md b/overviews/quasiquotes/expression-details.md index 9db804abf4..daf8e019f0 100644 --- a/overviews/quasiquotes/expression-details.md +++ b/overviews/quasiquotes/expression-details.md @@ -5,8 +5,8 @@ title: Expression details disqus: true partof: quasiquotes -num: 7 -outof: 12 +num: 8 +outof: 13 --- **Denys Shabalin** EXPERIMENTAL diff --git a/overviews/quasiquotes/future.md b/overviews/quasiquotes/future.md index 4d36460e37..90437cd2f3 100644 --- a/overviews/quasiquotes/future.md +++ b/overviews/quasiquotes/future.md @@ -5,8 +5,8 @@ title: Future prospects disqus: true partof: quasiquotes -num: 12 -outof: 12 +num: 13 +outof: 13 --- **Denys Shabalin** EXPERIMENTAL diff --git a/overviews/quasiquotes/hygiene.md b/overviews/quasiquotes/hygiene.md index 6e350cfc20..4924d9b937 100644 --- a/overviews/quasiquotes/hygiene.md +++ b/overviews/quasiquotes/hygiene.md @@ -5,8 +5,8 @@ title: Hygiene disqus: true partof: quasiquotes -num: 4 -outof: 12 +num: 5 +outof: 13 --- **Denys Shabalin, Eugene Burmako** EXPERIMENTAL diff --git a/overviews/quasiquotes/intro.md b/overviews/quasiquotes/intro.md index ecd138bd6c..153c66c483 100644 --- a/overviews/quasiquotes/intro.md +++ b/overviews/quasiquotes/intro.md @@ -5,48 +5,11 @@ title: Introduction disqus: true partof: quasiquotes -num: 1 -outof: 12 +num: 2 +outof: 13 --- **Denys Shabalin** EXPERIMENTAL -## Before you start {:#before-you-start} - -Before you start reading this guide it's recommended to start a Scala REPL with one extra line: - - scala> val universe = reflect.runtime.universe; import universe._ - -REPL is the best place to explore quasiquotes and this guide will use it extensively to demonstrate handling of trees. All of the examples will assume that import. - -Additionally some examples that use `ToolBox` API might need a few more lines to get things rolling: - - scala> import reflect.runtime.currentMirror - scala> import tools.reflect.ToolBox - scala> val toolbox = currentMirror.mkToolBox() - -Another tool you might want to be aware of is new and shiny `showCode` pretty printer (contributed by [@VladimirNik](https://github.com/VladimirNik)): - - scala> val C = q"class C" - C: universe.ClassDef = - class C extends scala.AnyRef { - def () = { - super.(); - () - } - } - - scala> println(showCode(C)) - class C - -Default pretty printer shows you contents of the tree in imaginary low-level Scala-like notation. `showCode` on the other hand will do its best to reconstruct actual source code equivalent to the given tree in proper Scala syntax. - -On the other side of spectrum there is also a `showRaw` pretty printer that shows direct internal organization of the tree: - - scala> println(showRaw(q"class C")) - ClassDef(Modifiers(), TypeName("C"), List(), Template(List(Select(Ident(scala), TypeName("AnyRef"))), noSelfType, List(DefDef(Modifiers(), termNames.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(pendingSuperCall), Literal(Constant(()))))))) - -## Basics {:#basics} - Quasiquotes are a neat notation that lets you manipulate Scala syntax trees with ease: scala> val tree = q"i am { a quasiquote }" diff --git a/overviews/quasiquotes/lifting.md b/overviews/quasiquotes/lifting.md index 69ed4b2ff9..170741af72 100644 --- a/overviews/quasiquotes/lifting.md +++ b/overviews/quasiquotes/lifting.md @@ -5,8 +5,8 @@ title: Lifting disqus: true partof: quasiquotes -num: 2 -outof: 12 +num: 3 +outof: 13 --- **Denys Shabalin** EXPERIMENTAL diff --git a/overviews/quasiquotes/pattern-details.md b/overviews/quasiquotes/pattern-details.md index aa897c6020..79157b4123 100644 --- a/overviews/quasiquotes/pattern-details.md +++ b/overviews/quasiquotes/pattern-details.md @@ -5,8 +5,8 @@ title: Pattern details disqus: true partof: quasiquotes -num: 9 -outof: 12 +num: 10 +outof: 13 --- **Denys Shabalin** EXPERIMENTAL diff --git a/overviews/quasiquotes/setup.md b/overviews/quasiquotes/setup.md new file mode 100644 index 0000000000..b1cc74c034 --- /dev/null +++ b/overviews/quasiquotes/setup.md @@ -0,0 +1,73 @@ +--- +layout: overview-large +title: Dependencies and setup + +disqus: true + +partof: quasiquotes +num: 1 +outof: 13 +--- + +## Scala 2.11 {:#211} + +In Scala 2.11, quasiquotes are shipped in the official Scala distribution as part of `scala-reflect.jar`, so you don't need to do anything special to use them - just don't forget to add a dependency on `scala-reflect`. + +All examples and code snippets in this guide are run under in 2.11 REPL with one extra line: + + scala> val universe = reflect.runtime.universe; import universe._ + +A wildcard import from a universe (be it a runtime reflection universe like here or a compile-time universe provided in macros) is all that's needed to use quasiquotes. All of the examples will assume that import. + +Additionally some examples that use `ToolBox` API will need a few more lines to get things rolling: + + scala> import reflect.runtime.currentMirror + scala> import tools.reflect.ToolBox + scala> val toolbox = currentMirror.mkToolBox() + +Another tool you might want to be aware of is new and shiny `showCode` pretty printer (contributed by [@VladimirNik](https://github.com/VladimirNik)): + + scala> val C = q"class C" + C: universe.ClassDef = + class C extends scala.AnyRef { + def () = { + super.(); + () + } + } + + scala> println(showCode(C)) + class C + +Default pretty printer shows you contents of the tree in imaginary low-level Scala-like notation. `showCode` on the other hand will do its best to reconstruct actual source code equivalent to the given tree in proper Scala syntax. + +On the other side of spectrum there is also a `showRaw` pretty printer that shows direct internal organization of the tree: + + scala> println(showRaw(q"class C")) + ClassDef(Modifiers(), TypeName("C"), List(), Template(List(Select(Ident(scala), TypeName("AnyRef"))), noSelfType, List(DefDef(Modifiers(), termNames.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(pendingSuperCall), Literal(Constant(()))))))) + +## Scala 2.10 {:#210} + +In Scala 2.10, quasiquotes are only available via the [macro paradise compiler plugin](http://docs.scala-lang.org/overviews/macros/paradise.html). + +In short, using quasiquotes in 2.10 is as simple as adding a single `addCompilerPlugin` line to your SBT build for the macro paradise plugin that enables quasiquotes and an additional `libraryDependencies` line for the supporting library that is necessary for quasiquotes to function in Scala 2.10. A full example is provided at [https://github.com/scalamacros/sbt-example-paradise](https://github.com/scalamacros/sbt-example-paradise). + +New `showCode` pretty printer is not available under 2.10. + +## SBT cross-compile {:#sbt} + +Here's a neat SBT snippet taken from [Spire](https://github.com/non/spire) that allows you to use quasiquotes and cross-compile against both Scala 2.10 and 2.11: + + libraryDependencies := { + CrossVersion.partialVersion(scalaVersion.value) match { + // if scala 2.11+ is used, quasiquotes are merged into scala-reflect + case Some((2, scalaMajor)) if scalaMajor >= 11 => + libraryDependencies.value + // in Scala 2.10, quasiquotes are provided by macro paradise + case Some((2, 10)) => + libraryDependencies.value ++ Seq( + compilerPlugin("org.scalamacros" % "paradise" % "2.0.0" cross CrossVersion.full), + "org.scalamacros" %% "quasiquotes" % "2.0.0" cross CrossVersion.binary) + } + } + diff --git a/overviews/quasiquotes/syntax-summary.md b/overviews/quasiquotes/syntax-summary.md index b659211e4b..56310de294 100644 --- a/overviews/quasiquotes/syntax-summary.md +++ b/overviews/quasiquotes/syntax-summary.md @@ -5,8 +5,8 @@ title: Syntax summary disqus: true partof: quasiquotes -num: 6 -outof: 12 +num: 7 +outof: 13 --- **Denys Shabalin** EXPERIMENTAL diff --git a/overviews/quasiquotes/terminology.md b/overviews/quasiquotes/terminology.md index c06bb5fcfd..96c5a77bef 100644 --- a/overviews/quasiquotes/terminology.md +++ b/overviews/quasiquotes/terminology.md @@ -5,8 +5,8 @@ title: Terminology summary disqus: true partof: quasiquotes -num: 11 -outof: 12 +num: 12 +outof: 13 --- EXPERIMENTAL diff --git a/overviews/quasiquotes/type-details.md b/overviews/quasiquotes/type-details.md index 112629a216..0cc192f778 100644 --- a/overviews/quasiquotes/type-details.md +++ b/overviews/quasiquotes/type-details.md @@ -5,8 +5,8 @@ title: Type details disqus: true partof: quasiquotes -num: 8 -outof: 12 +num: 9 +outof: 13 --- **Denys Shabalin** EXPERIMENTAL diff --git a/overviews/quasiquotes/unlifting.md b/overviews/quasiquotes/unlifting.md index 469af83bdd..d97412135e 100644 --- a/overviews/quasiquotes/unlifting.md +++ b/overviews/quasiquotes/unlifting.md @@ -5,8 +5,8 @@ title: Unlifting disqus: true partof: quasiquotes -num: 3 -outof: 12 +num: 4 +outof: 13 --- **Denys Shabalin** EXPERIMENTAL diff --git a/overviews/quasiquotes/usecases.md b/overviews/quasiquotes/usecases.md index 70dd566893..befc104113 100644 --- a/overviews/quasiquotes/usecases.md +++ b/overviews/quasiquotes/usecases.md @@ -5,8 +5,8 @@ title: Use cases disqus: true partof: quasiquotes -num: 5 -outof: 12 +num: 6 +outof: 13 --- **Denys Shabalin** EXPERIMENTAL