diff --git a/overviews/quasiquotes/definition-details.md b/overviews/quasiquotes/definition-details.md index 6013fc79f9..2332de9279 100644 --- a/overviews/quasiquotes/definition-details.md +++ b/overviews/quasiquotes/definition-details.md @@ -12,13 +12,13 @@ outof: 13 ## Modifiers -Every definition except packages and package objects have associated modifiers object which contains following data: +Every definition except packages and package objects have associated a modifiers object that contains the following data: -1. `FlagSet`, a set of bits that characterizes given definition. -2. Private within name (e.g. `foo` in `private[foo] def f`) -3. List of annotations +1. `FlagSet`, a set of bits that characterizes the given definition. +2. Private within name (e.g. `foo` in `private[foo] def f`). +3. A list of annotations. -Quasiquotes let you easily work with those fields through native support for `Modifiers`, `FlagSet` and annotation unquoting: +Quasiquotes let you work easily 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 @@ -29,7 +29,7 @@ Quasiquotes let you easily work with those fields through native support for `Mo 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: +All of those quasiquotes result in equivalent trees. It's also possible to combine unquoted flags with one provided inline in the source code, but an unquoted one should be used before inline ones: scala> q"$PRIVATE implicit def foo" res10: universe.DefDef = implicit private def foo: scala.Unit @@ -39,7 +39,7 @@ All of those quasiquotes result into equivalent trees. It's also possible to com q"implicit $PRIVATE def foo" ^ -To provide a definition annotation one need to unquote a new-shaped tree: +To provide a definition annotation one must unquote a new-shaped tree: scala> val annot = q"new foo(1)" annot: universe.Tree = new Foo(1) @@ -58,19 +58,19 @@ In deconstruction one can either extract `Modifiers` or annotations, but you can 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: +Considering that definitions might contain various low-level flags added to trees during typechecking, it\'s recommended to always extract complete modifiers, or 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 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 apply to all other template-bearing trees): +Templates are a common abstraction in definition trees that are used in new expressions, classes, traits, objects, and package objects. Although there is no interpolator for it at the moment we can illustrate its structure using the example of a new expression (similar handling will apply to all other template-bearing trees): q"new { ..$earlydefns } with ..$parents { $self => ..$stats }" -So template consists of: +The 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: +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 = ... @@ -78,12 +78,12 @@ So template consists of: 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: +2. List of parents. A list of type identifiers where only the first one in the list may have optional type and value arguments. This is because the first parent must be a class and subsequent parents are just traits that don't yet accept arguments: 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: + The first of the parents has an unusual shape that is a combination of term and type trees: scala> val q"${tq"$name[..$targs]"}(...$argss)" = parents.head name: universe.Tree = Foo @@ -96,7 +96,7 @@ So template consists of: 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: +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 = _ @@ -113,9 +113,9 @@ So template consists of: ## Val and Var Definitions -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-definition) and [method](#method-definition) parameters. +`val`s and `var`s allow you to define immutable values and mutable variables respectively. Additionally they can be used to represent [function](/overviews/quasiquotes/expression-details.html#function), [class](#class-definition) and [method](#method-definition) parameters. -Each val and var consistents of four components: modifiers, name, type tree and a right hand side: +Each `val` and `var` consistents of four components: modifiers, a name, a type tree and a right hand side: scala> val valx = q"val x = 2" valx: universe.ValDef = val x = 2 @@ -126,15 +126,15 @@ Each val and var consistents of four components: modifiers, name, type tree and 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-type) is used as tpt. +If the type of the `val` isn't explicitly specified by the user an [empty type](/overviews/quasiquotes/type-details.html#empty-type) is used as `tpt`. -Vals and vars are disjoint (they don't match one another): +`val`s and `var`s 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: +Vars always have the `MUTABLE` flag in their modifiers: scala> val q"$mods var $name: $tpt = $rhs" = q"var x = 2" mods: universe.Modifiers = Modifiers(, , Map()) @@ -144,9 +144,7 @@ Vars always have `MUTABLE` flag in their modifiers: ## Pattern Definitions -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: +Pattern definitions allow you to use Scala pattern matching capabilities to define variables. Unlike `val` and `var` definitions, pattern definitions are not first-class and they are represented through a combination of regular `val`s, `var`s and pattern matching: scala> val patdef = q"val (x, y) = (1, 2)" patdef: universe.Tree = @@ -161,8 +159,8 @@ through combination of regular vals and vars and pattern matching: 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: +1. Due to the fact that a single definition often gets desugared into multiple lower-level + ones, you must always use unquote splicing to unquote pattern definitions into other trees: scala> val tupsum = q"..$patdef; a + b" tupsum: universe.Tree = @@ -175,7 +173,7 @@ This representation has a few side-effects on the usage of such definitions: a.$plus(b) } - Otherwise if a regular unquoting is used, the definitions will be nested in a block that will make + 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" @@ -194,17 +192,17 @@ This representation has a few side-effects on the usage of such definitions: 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: +A generic form of pattern definition consists of modifiers, pattern, ascribed type and a right hand side: q"$mods val $pat: $tpt = $rhs" -Similarly one can also construct a mutable pattern definition: +Similarly, one can also construct a mutable pattern definition: q"$mods var $pat: $tpt = $rhs" ## Type Definition -Type definition have two possible shapes: abstract type definitions and alias type definitions. +Type definitions have two possible shapes: abstract type definitions and alias type definitions. Abstract type definitions have the following shape: @@ -216,7 +214,7 @@ Abstract type definitions have the following shape: 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. +Whenever one of the bounds isn\'t available, it gets represented as an [empty tree](/overviews/quasiquotes/expression-details.html#empty). Here each of the type arguments is a type definition itself. Another form of type definition is a type alias: @@ -227,7 +225,7 @@ Another form of type definition is a type alias: 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: +Due to the 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()) @@ -239,7 +237,7 @@ Where `tpt` has a `TypeBoundsTree(low, high)` shape. ## Method Definition -Each method consists of modifiers, name, type arguments, value arguments, return type and a body: +Each method consists of modifiers, a 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()) @@ -249,9 +247,9 @@ Each method consists of modifiers, name, type arguments, value arguments, return tpt: universe.Tree = body: universe.Tree = 1 -Type arguments are [type definitions](#type-definition) and value arguments are [val definitions](#val-and-var-definitions). Inferred return type is represented as [empty type](/overviews/quasiquotes/type-details.html#empty-type). If body of the method is [empty expression](/overviews/quasiquotes/expression-details.html#empty) it means that method is abstract. +Type arguments are [type definitions](#type-definition) and value arguments are [val definitions](#val-and-var-definitions). The inferred return type is represented as an [empty type](/overviews/quasiquotes/type-details.html#empty-type). If the body of the method is an [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: +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" @@ -259,7 +257,7 @@ Alternatively you can also deconstruct arguments separating implicit and non-imp 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: +This way of handling parameters will still work if the 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" @@ -269,7 +267,7 @@ This way of parameter handling will still work if method doesn\'t have any impli ## Secondary Constructor Definition -Secondary constructors are special kinds of methods that have following shape: +Secondary constructors are special kinds of methods that have the following shape: scala> val q"$mods def this(...$paramss) = this(...$argss)" = q"def this() = this(0)" @@ -277,7 +275,7 @@ Secondary constructors are special kinds of methods that have following shape: 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: +Due to the low level underlying representation of trees, secondary constructors are represented as a special kind of method with `termNames.CONSTRUCTOR` name: scala> val q"$mods def $name[..$tparams](...$paramss): $tpt = $body" = q"def this() = this(0)" @@ -290,27 +288,24 @@ Due to low level underlying representation of trees secondary constructors are r ## Class Definition -Classes have a following structure: +Classes have the 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-definition) modifiers and parameters. +As you can may already see, the right part after the `extends` is just a [template](#templates). Apart from it and the modifiers, classes also have a primary constructor that consists of constructor modifiers, type and value parameters that behave very much like [method](#method-definition) modifiers and parameters. ## Trait Definition -Syntactically traits are quite similar to [classes](#class-definition) sans value parameters and constructor modifiers: +Syntactically, traits are quite similar to [classes](#class-definition) minus 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: +An important difference in handling is caused by [SI-8399](https://issues.scala-lang.org/browse/SI-8399?filter=12305) due to the INTERFACE flag that is set for traits. Wwith only abstract members, trait patterns 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: +A workaround is to always extract modifiers using wildcard pattern: scala> val q"$_ trait $name { ..$stats }" = q"trait X { def x: Int }" name: universe.TypeName = X @@ -318,7 +313,7 @@ A workaround it to always extract modifiers with wildcard pattern: ## Object Definition -Syntactically objects are quite similar [classes](#class-definition) without constructors: +Syntactically, objects are quite similar to [classes](#class-definition) without constructors: q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$stats }" @@ -347,8 +342,7 @@ Packages are a fundamental primitive to organize source code. You can express th } }) -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). +Quasiquotes don\'t support the inline package definition syntax that is usually used in the header of the source file (but it's equivalent to the supported one in terms of ASTs). ## Package Object Definition @@ -358,7 +352,7 @@ Package objects are a cross between packages and object: 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: +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: ... @@ -366,7 +360,7 @@ Even though package and regular objects seem to be quite similar syntactically t scala> val q"package object $name" = q"object O" scala.MatchError: ... -Internally they get represtend as an object nested into package with given name: +Internally, they get represented as an object nested in a package with a given name: scala> val P = q"package object P" P: universe.PackageDef = @@ -379,4 +373,4 @@ Internally they get represtend as an object nested into package with given name: } } -This also means that you can match package object as a package. +This also means that you can match a package object as a package. diff --git a/overviews/quasiquotes/expression-details.md b/overviews/quasiquotes/expression-details.md index 015ad1fa44..0efeff3a18 100644 --- a/overviews/quasiquotes/expression-details.md +++ b/overviews/quasiquotes/expression-details.md @@ -14,12 +14,12 @@ outof: 13 `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""`. +1. `Val`s, `Var`s and `Def`s without the 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""`. +3. `Try` expressions without a finally clause have it set to `q""`. +4. `Case` clauses without guards have them set to `q""`. -Default toString formats `q""` as ``. +The default `toString` formats `q""` as ``. ## Literal @@ -34,18 +34,18 @@ Scala has a number of default built-in literals: q"null" // null literal q"()" // unit literal -All of those values have Literal type except symbols which have different representation: +All of those values are of type `Literal` except symbols, which have a 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: +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-liftables) except `Null`. Lifting of `null` value and `Null` type isn't supported, use `q"null"` if you really mean to create null literal: +This would work the same way for all literal types (see [standard liftables](/overviews/quasiquotes/lifting.html#standard-liftables) except `Null`. Lifting of the `null` value into the `Null` type isn't supported; use `q"null"` if you really want to create a `null` literal: scala> val x = null scala> q"$x" @@ -53,7 +53,7 @@ This would work the same way for all literal types (see [standard liftables](/ov q"$x" ^ -During deconstruction you can use [unlifting](/overviews/quasiquotes/unlifting.html) to extract values out of Literal trees: +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 @@ -62,9 +62,9 @@ Similarly it would work with all the literal types except `Null`. (see [standard ## Identifier and Selection -Identifiers and member selections are two fundamental primitives that let you refer to other definitions. Combination of two of them is also known `RefTree`. +Identifiers and member selections are two fundamental primitives that let you refer to other definitions. A combination of two of them is also known as a `RefTree`. -Each term identifier is defined by its name and by the fact of being backquoted or not: +Each term identifier is defined by its name and whether or not it is backquoted: scala> val name = TermName("Foo") name: universe.TermName = Foo @@ -75,20 +75,20 @@ Each term identifier is defined by its name and by the fact of being backquoted 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 syntactical point of view: +Although backquoted and non-backquoted identifiers may refer to the same thing they are not syntactically equivalent: 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. +This is because 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): +Apart from matching on identifiers with a 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. +Name ascription is important here because without it you'll get a pattern that is equivalent to regular pattern variable binding. Similarly you can create and extract member selections: @@ -100,7 +100,7 @@ Similarly you can create and extract member selections: ## Super and This -One can use this and super to select precise members within inheritance chain. +One can use `this` and `super` to select precise members within an inheritance chain. This tree supports following variations: @@ -110,9 +110,9 @@ This tree supports following variations: scala> val q"$name.this" = q"foo.this" name: universe.TypeName = foo -So plain `q"this"` is equivalent to `q"${tpnme.EMPTY}.this"`. +So an unqualified `q"this"` is equivalent to `q"${tpnme.EMPTY}.this"`. -Similarly for super we have: +Similarly for `super` we have: scala> val q"$name.super[$qual].$field" = q"super.foo" name: universe.TypeName = @@ -131,11 +131,11 @@ Similarly for super we have: ## Application and Type 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: +Value applications and type applications are two fundamental parts from which one can construct calls to Scala functions and methods. Let's 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: +This can be accomplished with the following: scala> val apps = List(q"f[Int](1, 2)", q"f('a, 'b)") scala> apps.foreach { @@ -145,9 +145,9 @@ It can be handled in the following way: 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 and another 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. +As you can see, we were able to match both calls regardless as to whether or not a specific type application exists. This happens because the type application matcher extracts the empty list of type arguments if the tree is not an actual type application, making it possible to handle both situations uniformly. -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: +It is recommended to always include type applications when you match on a function with type arguments, as they will be inserted by the compiler during type checking, 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 @@ -156,11 +156,11 @@ It's recommended to always include type applications when you match on function 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: +Other important features of Scala method calls are 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: +Here we might get one, or two subsequent value applications: scala> val apps = List(q"g(1)", q"g(1)(2)") scala> apps.foreach { @@ -170,14 +170,14 @@ Here we might get either one or two subsequent value applications: 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: +`...$`, in a pattern, allows us to greedily match all subsequent value applications. Similarly to the type arguments matcher, one needs to be careful because it always matches even in the case where no actual value applications exist: 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. +Therefore it's recommended to use more specific patterns that check that ensure the extracted `argss` is not empty. -Similarly to type arguments, implicit value arguments are automatically inferred during typechecking: +Similarly to type arguments, implicit value arguments are automatically inferred during type checking: scala> val q"..$stats; g(...$argss)" = toolbox.typecheck(q""" def g(x: Int)(implicit y: Int) = x + y @@ -197,9 +197,9 @@ Assign and update are two related ways to explicitly mutate a variable or collec 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. +As you can see, the update syntax is just syntactic sugar that gets represented as an update method call on given object. -Nevertheless quasiquotes let you deconstruct both of them uniformly according to their user-facing syntax: +Nevertheless, quasiquotes let you deconstruct both of them uniformly according to their user-facing syntax: scala> List(assign, update).foreach { case q"$left = $right" => @@ -210,7 +210,7 @@ Nevertheless quasiquotes let you deconstruct both of them uniformly according to 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: +On the other hand if you want to treat this two cases separately, it's possible with the following, more specific pattern: scala> List(assign, update).foreach { case q"${ref: RefTree} = $expr" => @@ -224,7 +224,7 @@ On the other hand if you want to treat this two cases separately it's also possi ## Return -Return expressions is used to perform early return from a function. +The *return* expression is used to perform an early return from a function. scala> val ret = q"return 2 + 2" ret: universe.Return = return 2.$plus(2) @@ -234,7 +234,7 @@ Return expressions is used to perform early return from a function. ## Throw -Throw expression is used to throw a throwable: +The *throw* expression is used to throw a throwable: scala> val thr = q"throw new Exception" thr: universe.Throw = throw new Exception() @@ -244,7 +244,7 @@ Throw expression is used to throw a throwable: ## Ascription -Ascriptions lets users to annotate type of intermediate expression: +Ascriptions let users annotate the type of an intermediate expression: scala> val ascribed = q"(1 + 1): Int" ascribed: universe.Typed = (1.$plus(1): Int) @@ -264,22 +264,22 @@ Expressions can be 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: +It's important to mention that such a 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-type). +In this case we need to deconstruct it as an [ascription](#ascription) and then deconstruct `tpt` as an [annotated type](/overviews/quasiquotes/type-details.html#annotated-type). ## 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: +Tuples are heteregeneous data structures with built-in user-friendly syntax. The syntax itself is just syntactic 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: +At the moment, tuples are only supported up to an arity of 22, but this is just an implementation restriction that might be lifted in the future. To find out if a given arity is supported use: scala> val `tuple 10 supported?` = definitions.TupleClass(10) != NoSymbol tuple 10 supported?: Boolean = true @@ -292,7 +292,7 @@ Despited the fact that `Tuple1` class exists there is no built-in syntax for it. scala> val inparens = q"(a)" inparens: universe.Ident = a -It is also common to treat `Unit` as nullary tuple: +It is also common to treat `Unit` as a nullary tuple: scala> val elems = List.empty[Tree] scala> val nullary = q"(..$elems)" @@ -308,14 +308,14 @@ 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: +And `Unit` as a nullary tuple: scala> val q"(..$elems)" = q"()" elems: List[universe.Tree] = List() ## 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 separated by semicolon or a newline: +Blocks are a fundamental primitive used to express a sequence of actions or bindings. The `q"..."` interpolator is an equivalent of a block. It allows you to convey more than one expression, separated by a semicolon or a newline: scala> val t = q"a; b; c" t: universe.Tree = @@ -325,7 +325,7 @@ Blocks are a fundamental primitive to express sequence of actions or bindings. ` 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: +The only difference between `q"{...}"` and `q"..."` is how they handle the of case just a single element. `q"..."` always returns an element itself while a block still remains a block if a single element is not an expression: scala> val t = q"val x = 2" t: universe.ValDef = val x = 2 @@ -337,7 +337,7 @@ The only difference between `q"{...}"` and `q"..."` is handling of case when jus () } -Blocks can also be flattened into another blocks with `..$`: +Blocks can also be flattened into other blocks with `..$`: scala> val ab = q"a; b" ab: universe.Tree = @@ -359,7 +359,7 @@ 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: +Deconstruction always returns the user-defined contents of a block: scala> val q"..$stats" = q"{ val x = 2 }" stats: List[universe.Tree] = List(val x = 2) @@ -375,7 +375,7 @@ Except for empty tree which is not considered to be a block: 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): +A zero-element block is equivalent to a 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() @@ -383,11 +383,11 @@ Zero-element block is equivalent to synthetic unit (one that was inserted by the 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](#pattern-match) making it convenient to work with those cases as with zero-element blocks. +Such units are used in empty `else` branches of [ifs](#if) and empty bodies of [case clauses](#pattern-match), making it as convenient to work with those cases as with zero-element blocks. ## If -There are two varieties of if expressions: those with else clause and without it: +There are two varieties of if expressions: those with an `else` clause and without it: scala> val q"if ($cond) $thenp else $elsep" = q"if (true) a else b" cond: universe.Tree = true @@ -399,11 +399,11 @@ There are two varieties of if expressions: those with else clause and without it thenp: universe.Tree = a elsep: universe.Tree = () -No-else clause is equivalent to else clause that contains a synthetic unit literal ([empty block](#block)). +A missing `else` clause is equivalent to an `else` clause that contains a synthetic unit literal ([empty block](#block)). ## Pattern Match -Pattern matching is cornerstone feature of Scala that lets you deconstruct values into their components: +Pattern matching is a cornerstone feature of Scala that lets you deconstruct values into their components: q"$expr match { case ..$cases } " @@ -411,7 +411,7 @@ Where `expr` is some non-empty expression and each case is represented with a `c cq"$pat if $expr => $expr" -Combination of the two forms allows to construct and deconstruct arbitrary pattern matches: +A combination of the two forms allows you to construct and deconstruct arbitrary pattern matches: scala> val q"$expr match { case ..$cases }" = q"foo match { case _: Foo => 'foo case _ => 'notfoo }" @@ -424,18 +424,18 @@ Combination of the two forms allows to construct and deconstruct arbitrary patte pat2: universe.Tree = _ body2: universe.Tree = scala.Symbol("notfoo") -Case clause without body is equivalent to one holding synthetic unit literal ([empty block](#block)): +A case clause without a body is equivalent to one holding a 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). +The lack of a guard is represented with the help of an [empty expression](#empty). ## 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. +A `try` expression is used to handle possible error conditions and to ensure a consistent state via `finally`. Both error handling cases and the `finally` clause are optional. scala> val q"try $a catch { case ..$b } finally $c" = q"try t" a: universe.Tree = t @@ -454,7 +454,7 @@ Try expression is used to handle possible error conditions and ensure consistent b: List[universe.CaseDef] = List() c: universe.Tree = f -Similarly to [pattern matching](#pattern-match) cases can be further deconstructed with `cq"..."`. No-finally clause is represented with the help of [empty expression](#empty). +Similar to [pattern matching](#pattern-match), cases can be further deconstructed with `cq"..."`. The lack of a `finally` clause is represented with the help of an [empty expression](#empty). ## Function @@ -469,13 +469,9 @@ There are three ways to create anonymous function: 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. +The first one uses the placeholder syntax. The second one names the function parameter but still relies on type inference to infer its type. An the last one explicitly defines the function parameter. Due to an implementation restriction, the second notation can only be used in parentheses or inside another expression. If you leave them out the you must specify the parameter types. -Parameters are represented as [Vals](/overviews/quasiquotes/definition-details.html#val-and-var-definitions). 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-type): +Parameters are represented as [Vals](/overviews/quasiquotes/definition-details.html#val-and-var-definitions). If you want to programmatically create a `val` that should have its type inferred you need to use the [empty type](/overviews/quasiquotes/type-details.html#empty-type): scala> val tpt = tq"" tpt: universe.TypeTree = @@ -486,7 +482,7 @@ its type inferred you need to use [empty type](/overviews/quasiquotes/type-detai 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: +All of the given forms are represented in the same way and may be matched uniformly: scala> List(f1, f2, f3).foreach { case q"(..$params) => $body" => @@ -496,7 +492,7 @@ All of the given forms are represented in the same way and could be uniformly ma params = List(val a = _), body = a.$plus(1) params = List(val a: Int = _), body = a.$plus(1) -You can also tear arguments further apart: +You can also tear arguments apart even further: scala> val q"(..$params) => $_" = f3 params: List[universe.ValDef] = List(val a: Int = _) @@ -505,13 +501,11 @@ You can also tear arguments further apart: 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. +It is recommended that you use the underscore pattern in place of [modifiers](/overviews/quasiquotes/definition-details.html#modifiers), even if you don't plan to work with them as parameters, they may contain additional flags which might cause match failures. ## Partial Function -Partial functions are a neat syntax that lets you express functions with -limited domain with the help of pattern matching: +Partial functions are a neat syntax that let you express functions with a limited domain by using pattern matching: scala> val pf = q"{ case i: Int if i > 0 => i * i }" pf: universe.Match = @@ -522,16 +516,14 @@ limited domain with the help of pattern matching: 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: +A weird default for the "pretty printed" view on the tree represents the fact that they share a similar data structure as do 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 and do-while loops are low-level control structures that used when performance of iteration -is critical: +While and do-while loops are low-level control structures that can be used when performance of a particular iteration is critical: scala> val `while` = q"while(x > 0) x -= 1" while: universe.LabelDef = @@ -565,7 +557,7 @@ is critical: ## For and For-Yield Loops -For and For-Yield expressions allow to write monadic style comprehensions that desugar into calls to `map`, `flatMap`, `foreach` and `withFilter` methods: +`for` and `for-yield` expressions allow us to write a monadic style comprehension 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 = @@ -576,7 +568,7 @@ For and For-Yield expressions allow to write monadic style comprehensions that d case scala.Tuple2((x @ _), (y @ _)) => x })) -Each enumerator in the comprehension can be expressed with `fq"..."` interpolator: +Each enumerator in the comprehension can be expressed with the `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)) @@ -584,41 +576,41 @@ Each enumerator in the comprehension can be expressed with `fq"..."` interpolato scala> val `for-yield` = q"for (..$enums) yield y" for-yield: universe.Tree -Similarly one can deconstruct for-yield back into a list of enumerators and body: +Similarly one can deconstruct the `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: +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 expression lets you construct an instance of given type possibly refining it with other types or definitions: +New expressions let 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. +See the [templates](/overviews/quasiquotes/definition-details.html#templates) section for details. ## Import -Import trees consist of reference and a list of selectors: +Import trees consist of a 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: +Selectors are extracted as pattern trees that 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"_"` +3. Unimport selectors are represented as thin arrows with a wildcard right hand side: `pq"poison -> _"` +4. The wildcard selector is represented as a wildcard pattern: `pq"_"` Similarly one construct imports back from a programmatically created list of selectors: @@ -626,5 +618,3 @@ Similarly one construct imports back from a programmatically created list of sel 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/hygiene.md b/overviews/quasiquotes/hygiene.md index a232314d65..607f86cddf 100644 --- a/overviews/quasiquotes/hygiene.md +++ b/overviews/quasiquotes/hygiene.md @@ -10,11 +10,11 @@ outof: 13 --- **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 absence 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. +The notion of hygiene has been widely popularized by macro research in Scheme. A code generator is called hygienic if it ensures the absence 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 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 absence of hygiene, which is what this section is about. +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 - both of our codegen facilities (compile-time codegen with macros and runtime codegen with toolboxes) require programmers to handle hygiene manually. You must know how to work around the absence 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. +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 will not change (*referential transparency*). Second, we must make certain that regardless of the context in which we splice regular code, its meaning will not 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 @@ -31,7 +31,7 @@ What referential transparency means is that quasiquotes should remember the lexi 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. +Here we can see that the unqualified reference to `Map` does not 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 @@ -56,7 +56,7 @@ Here we can see that plain reference to `Map` doesn\'t respect our custom import 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: +If we compile both the macro and it's usage, we'll see that `println` will not be called when the application runs. This will happen because, after macro expansion, `Test.scala` will look like: // Expanded Test.scala package example @@ -66,18 +66,18 @@ If we compile both macro and it's usage we'll see that `println` will not be cal 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: +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: +1. Fully qualify all references. i.e. we can adapt our macro's 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. + It's important to start with `_root_` as otherwise there will still be a chance of name collision if `example` gets redefined at the use-site of the macro. -2. Unquote symbols instead of using plain identifiers. i.e. we can resolve reference to wrapper by hand: +2. Unquote symbols instead of using plain identifiers. i.e. we can resolve the reference to `wrapper` by hand: def impl(c: Context)(x: c.Tree) = { import c.universe._ @@ -88,7 +88,7 @@ And wrapper will be resolved to `example.Test.wrapper` rather than intended `exa ## 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: +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 that unquoted into a macro expansion was originally referring to some variable in the enclosing lexical context, then this reference should remain in force after macro expansion, regardless of what code was generated for that 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 = ... @@ -107,11 +107,11 @@ What hygiene in the narrow sense means is that quasiquotes shouldn't mess with t 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. +In that 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 get out of hand quite easily. -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. +To avoid these situations, there is a battle-tested workaround from the early days of Lisp; having a function that creates unique names that are 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. +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 is there a nice 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 @@ -122,5 +122,3 @@ There's a bit of a mixup in our API, though. There is an internal API `internal. scala> toolbox.eval(generatedTree) 2 res2: Any = 1 - - diff --git a/overviews/quasiquotes/intro.md b/overviews/quasiquotes/intro.md index 0d131c753f..3242f86551 100644 --- a/overviews/quasiquotes/intro.md +++ b/overviews/quasiquotes/intro.md @@ -15,14 +15,14 @@ Quasiquotes are a neat notation that lets you manipulate Scala syntax trees with 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. +Every time you wrap a snippet of code in `q"..."` it will become a tree that represents a given snippet. As you might have already noticed, quotation syntax is 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: +Whenever you match a tree with a quasiquote it will match whenever the *structure* of a 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 @@ -35,9 +35,9 @@ You can also put things into quasiquotation with the help of `$`: 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. +This operation is also known as *unquoting*. Whenever you unquote an expression of type `Tree` in a quasiquote it will *structurally substitute* that tree into that location. Most of the time such substitutions between quotes is equivalent to a textual substitution of the source code. -Similarly one can structurally deconstruct a tree using unquoting in pattern matching: +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 @@ -62,7 +62,7 @@ In this example we see three primary contexts being used: 2. `List[Int]` is a type 3. `List(a, b)` is a pattern -Each of this contexts is covered by separate interpolator: +Each of these contexts is covered by a separate interpolator:   | Used for ----|---------------- @@ -75,7 +75,7 @@ Syntactical similarity between different contexts doesn\'t imply similarity betw scala> println(q"List[Int]" equalsStructure tq"List[Int]") false -If we peek under the hood we'll see that trees are indeed different: +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")))) @@ -83,12 +83,12 @@ If we peek under the hood we'll see that trees are indeed different: scala> println(showRaw(tq"List[Int]")) AppliedTypeTree(Ident(TypeName("List")), List(Ident(TypeName("Int")))) -Similarly patterns and expressions are not equivalent either: +Similarly, patterns and expressions are also not equivalent: scala> println(pq"List(a, b)" equalsStructure q"List(a, b)") false -So it's extremely important to use the right interpolator for the job to construct a valid syntax tree. +It's extremely important to use the right interpolator for the job in order to construct a valid syntax tree. Additionally there are two auxiliary interpolators that let you work with minor areas of scala syntax: @@ -97,7 +97,7 @@ Additionally there are two auxiliary interpolators that let you work with minor cq | [case clause](/overviews/quasiquotes/syntax-summary.html#auxiliary) fq | [for loop enumerator](/overviews/quasiquotes/syntax-summary.html#auxiliary) -See [syntax summary](/overviews/quasiquotes/syntax-summary.html) section for details. +See the section [syntax summary](/overviews/quasiquotes/syntax-summary.html) for details. ## Splicing @@ -107,9 +107,9 @@ Unquote splicing is a way to unquote a variable number of elements: 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]]`. +Dots before the unquotee annotate indicate a degree of flattenning and are called a *splicing rank*. `..$` expects the argument to be an `Iterable[Tree]` and `...$` expects an `Iterable[Iterable[Tree]]`. -Splicing can be easily combined with regular unquotation: +Splicing can easily be combined with regular unquotation: scala> val c = q"c" scala> val fabc = q"f(..$ab, $c)" @@ -121,7 +121,7 @@ Splicing can be easily combined with regular unquotation: 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 `...$`: +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)) @@ -129,7 +129,7 @@ If you want to abstract over applications even further you can use `...$`: 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. +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: @@ -139,17 +139,17 @@ Similarly to construction one can also use `..$` and `...$` to tear trees apart: 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: +There are some limitations in the way you can combine splicing 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 `...$`: +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. +In this section we only worked with function arguments but the same splicing rules are true for all syntax forms with a variable number of elements. [Syntax summary](/overviews/quasiquotes/syntax-summary.html) and the corresponding details sections demonstrate how you can use splicing with other syntactic forms. diff --git a/overviews/quasiquotes/lifting.md b/overviews/quasiquotes/lifting.md index ec603f0fa4..9106fec536 100644 --- a/overviews/quasiquotes/lifting.md +++ b/overviews/quasiquotes/lifting.md @@ -18,15 +18,15 @@ Lifting is an extensible way to unquote custom data types in quasiquotes. Its pr scala> val four = q"$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 abstract method that defines a mapping of given type to tree: +This code runs successfully because `Int` is considered to be `Liftable` by default. The `Liftable` type is just a trait with a single abstract method that defines a mapping of a given type to tree: trait Liftable[T] { def apply(value: T): Tree } -Whenever there is an implicit value of `Liftable[T]` available, one can unquote `T` in quasiquotes. 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). +Whenever there is an implicit value of `Liftable[T]` available, one can unquote `T` in quasiquotes. 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 trigger usage of `Liftable` representation even if it\'s available: subtypes of `Tree`, `Symbol`, `Name`, `Modifiers` and `FlagSet`. +A number of data types that are supported natively by quasiquotes will never trigger the usage of a `Liftable` representation, even if it\'s available: subtypes of `Tree`, `Symbol`, `Name`, `Modifiers` and `FlagSet`. One can also combine lifting and unquote splicing: @@ -38,7 +38,7 @@ One can also combine lifting and unquote splicing: 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. +In this case, each element of the list will be lifted separately and the result will be spliced in at the definition point. ## Bring your own @@ -55,21 +55,20 @@ To define tree representation for your own data type just provide an implicit in } } -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: +This way, whenever a value of type `Point` is unquoted at runtime it will be automatically transformed into a case class constructor call. In this example there are three important points you should consider: -0. Liftable companion contains helper `apply` method to simplifies creation of `Liftable` instances. +1. The `Liftable` companion contains a helper `apply` method to simplify the 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-dependent nature of current reflection - api. (see [sharing liftable implementation between universes](#reusing-liftable-implementation-between-universes)) +2. 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 the path-dependent nature of the current reflection + API. (see [sharing liftable implementation between universes](#reusing-liftable-implementation-between-universes)) -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: +3. Due to a lack of [hygiene](/overviews/quasiquotes/hygiene.html), the reference to `Point`'s companion + has to be fully qualified to ensure the correctness of this tree in every possible context. Another + way to workaround this reference issue is to use symbols instead: val PointSym = symbolOf[Point].companionModule implicit val lift = Liftable[Point] { p => @@ -112,7 +111,7 @@ into a case class constructor call. In this example there two important points t ## Reusing Liftable implementation between universes -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: +Due to the path dependent nature of the current reflection API, it is non-trivial to share the same `Liftable` definition between the *macro* and the *runtime* universes. One possible way to do this is to define `Liftable` implementations in a trait and instantiate it for each universe separately: import scala.reflect.api.Universe import scala.reflect.macros.blackbox.Context @@ -140,7 +139,7 @@ Due to path dependent nature of current reflection API it isn't trivial to share // ... } -So in practice it's much easier to just define a liftable for given universe at hand: +So, in practice, it's much easier to just define a `Liftable` for given universe at hand: import scala.reflect.macros.blackbox.Context diff --git a/overviews/quasiquotes/pattern-details.md b/overviews/quasiquotes/pattern-details.md index 5647dec8e2..2b56d7a7c6 100644 --- a/overviews/quasiquotes/pattern-details.md +++ b/overviews/quasiquotes/pattern-details.md @@ -12,7 +12,7 @@ outof: 13 ## Wildcard Pattern -Wildcard pattern (`pq"_"`) is the simplest form of pattern that matches any input. +The wildcard pattern (`pq"_"`) is the simplest pattern that matches any input. ## Literal Pattern @@ -21,11 +21,11 @@ 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. +See the chapter on [literal expressions](/overviews/quasiquotes/expression-details.html#literal) for details. ## Binding Pattern -Binding pattern is a way to name pattern or one it's part as local variable: +A binding pattern is a way to name pattern or one of its parts to a local variable: scala> val bindtup = pq"foo @ (1, 2)" bindtup: universe.Bind = (foo @ scala.Tuple2(1, 2)) @@ -34,17 +34,17 @@ Binding pattern is a way to name pattern or one it's part as local variable: name: universe.Name = foo pat: universe.Tree = scala.Tuple2(1, 2) -Binding without explicit pattern is equivalent to the one with wildcard pattern: +Binding without an explicit pattern is equivalent to one with the wildcard pattern: scala> val pq"$name @ $pat" = pq"foo" name: universe.Name = foo pat: universe.Tree = _ -See also [type pattern](#type-pattern) for an example of type variable binding. +See [type pattern](#type-pattern) for an example of type variable binding. ## Extractor Pattern -Extractors are a neat way to delegate a pattern matching to another object's unapply method: +Extractors are a neat way to delegate pattern matching to another object's unapply method: scala> val extractor = pq"Foo(1, 2, 3)" extractor: universe.Tree = Foo(1, 2, 3) @@ -55,7 +55,7 @@ Extractors are a neat way to delegate a pattern matching to another object's una ## Type Pattern -Type patterns are a way to check type of a scrutinee: +Type patterns are a way to check the type of a scrutinee: scala> val isT = pq"_: T" isT: universe.Typed = (_: T) @@ -63,7 +63,7 @@ Type patterns are a way to check type of a scrutinee: 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: +The combination of non-wildcard name and type pattern is represented as a bind over the wildcard type pattern: scala> val fooIsT = pq"foo: T" fooIsT: universe.Bind = (foo @ (_: T)) @@ -72,12 +72,12 @@ Combination of non-wildcard name and type pattern is represented as bind over wi name: universe.Name = foo tpt: universe.Tree = T -Another important thing to mention is a type variable patterns: +Another important thing to mention is a type variable pattern: scala> val typevar = pq"_: F[t]" typevar: universe.Typed = (_: F[(t @ )]) -One can construct (and similarly deconstruct) such patterns by following steps: +One can construct (and similarly deconstruct) such patterns with the following steps: scala> val name = TypeName("t") scala> val empty = q"" @@ -88,7 +88,7 @@ One can construct (and similarly deconstruct) such patterns by following steps: ## Alternative Pattern -Pattern alternatives represent a pattern that matches whenever at least one of the branches matches: +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()) @@ -103,12 +103,10 @@ Pattern alternatives represent a pattern that matches whenever at least one of t ## Tuple Pattern -Similarly to [tuple expressions](/overviews/quasiquotes/expression-details.html#tuple) and [tuple types](/overviews/quasiquotes/type-details.html#tuple-type), tuple patterns are just a syntactic sugar that expands as `TupleN` extractor: +Similar to [tuple expressions](/overviews/quasiquotes/expression-details.html#tuple) and [tuple types](/overviews/quasiquotes/type-details.html#tuple-type), tuple patterns are just syntactic sugar that expands as a `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 index ef7973c1ad..8e97010ebb 100644 --- a/overviews/quasiquotes/syntax-summary.md +++ b/overviews/quasiquotes/syntax-summary.md @@ -178,5 +178,4 @@ Prefixes of unquotees imply the following: * `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. - +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 `List`s of expressions. diff --git a/overviews/quasiquotes/terminology.md b/overviews/quasiquotes/terminology.md index 96c5a77bef..edbececee4 100644 --- a/overviews/quasiquotes/terminology.md +++ b/overviews/quasiquotes/terminology.md @@ -10,13 +10,13 @@ outof: 13 --- 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. +* **Quasiquote** (not quasi-quote) can refer to either the quasiquote library or any usage of one its [interpolators](/overviews/quasiquotes/intro.html#interpolators). The name is not hyphenated for the 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 a representation of a Scala program or a part of it through means of the 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. +* **Tree deconstruction** refers to usages of quasiquotes as patterns to structurally tear apart trees. +* **Unquoting** is a way of either putting things in or extracting things out of quasiquotes. 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. +* [**Lifting**](/overviews/quasiquotes/lifting.html) is a way to unquote non-tree values and transform them into trees with the help of the `Liftable` typeclass. +* [**Unlifting**](/overviews/quasiquotes/unlifting.html) is a way to unquote non-tree values out of quasiquote patterns with the help of the `Unliftable` typeclass. diff --git a/overviews/quasiquotes/type-details.md b/overviews/quasiquotes/type-details.md index 2c06f13059..31ebf9fbc8 100644 --- a/overviews/quasiquotes/type-details.md +++ b/overviews/quasiquotes/type-details.md @@ -12,7 +12,7 @@ outof: 13 ## Empty Type -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: +The empty type (`tq""`) is a canonical way to say that the 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-definition) with unknown return type 2. [Val or Var](/overviews/quasiquotes/definition-details.html#val-and-var-definitions) with unknown type @@ -33,7 +33,7 @@ And deconstruct it back through [unlifting](/overviews/quasiquotes/unlifting.htm 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. +It is recommended to always ascribe the name as `TypeName` when you work with type identifiers. A non-ascribed pattern is equivalent to a pattern variable binding. ## Singleton Type @@ -56,7 +56,7 @@ Type projection is a fundamental way to select types as members of other types: 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. +Similarly to identifiers, it\'s recommended to always ascribe the name as `TypeName`. Non-ascribed matching behaviour might change in the future. As a convenience one can also select type members of terms: @@ -66,12 +66,12 @@ As a convenience one can also select type members of terms: 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: +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-and-this) one can select members through super and this: +Lastly and [similarly to expressions](/overviews/quasiquotes/expression-details.html#super-and-this) one can select members through `super` and `this`: scala> val superbar = tq"super.Bar" superbar: universe.Select = super.Bar @@ -97,14 +97,14 @@ Instantiations of parametized types can be expressed with the help of applied ty 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: +Deconstruction of non-applied types will cause `targs` to be extracted as an empty list: scala> val tq"Foo[..$targs]" = tq"Foo" targs: List[universe.Tree] = List() ## Annotated Type -Similarly to expressions types can be annotated: +Similarly to expressions, types can be annotated: scala> val annotated = tq"T @Fooable" annotated: universe.Annotated = T @Fooable @@ -115,16 +115,16 @@ Similarly to expressions types can be annotated: ## Compound Type -Compound type lets users to express a combination of a number of types with optional refined member list: +A compound type lets users express a combination of a number of types with an 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 + scala> val tq"..$parents { ..$defns }" = 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. +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 the `with` keyword. On the other side of the spectrum are pure refinements without explicit parents (a.k.a. structural types): @@ -156,7 +156,7 @@ Alternatively there is also an underscrore notation: ## Tuple Type -[Similarly to expressions](/overviews/quasiquotes/expression-details.html#tuple), tuple types are just a syntactic sugar over `TupleN` classes: +[Similar to expressions](/overviews/quasiquotes/expression-details.html#tuple), tuple types are just syntactic sugar over `TupleN` classes: scala> val tup2 = tq"(A, B)" tup2: universe.Tree = scala.Tuple2[A, B] @@ -164,16 +164,16 @@ Alternatively there is also an underscrore notation: scala> val tq"(..$tpts)" = tup2 tpts: List[universe.Tree] = List(A, B) -Analogously `Unit` type is considered to be nullary tuple: +Analogously the `Unit` type is considered to be a 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)) +It is important to mention that pattern matching a reference to `Unit` is limited to either fully the qualified path or a reference that contains symbols. (see [hygiene](/overviews/quasiquotes/hygiene.html)) ## Function Type -Similarly to tuples, function types are a syntactic sugar over `FunctionN` classes: +Similar to tuples, function types are syntactic sugar over `FunctionN` classes: scala> val funtype = tq"(A, B) => C" funtype: universe.Tree = _root_.scala.Function2[A, B, C] @@ -181,5 +181,3 @@ Similarly to tuples, function types are a syntactic sugar over `FunctionN` class 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 index 1bb03df9d4..0870bab627 100644 --- a/overviews/quasiquotes/unlifting.md +++ b/overviews/quasiquotes/unlifting.md @@ -10,15 +10,15 @@ outof: 13 --- **Denys Shabalin** EXPERIMENTAL -Unlifting is the reverse operation to [lifting](/overviews/quasiquotes/lifting.html): it takes a tree and recovers value from it: +Unlifting is the reverse operation to [lifting](/overviews/quasiquotes/lifting.html): it takes a tree and recovers a 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. +Due to the fact that the tree may not be a represention of our data type, the return type of unapply is `Option[T]` rather than just `T`. This signature makes it easy to use `Unliftable` instances as extractors. -Whenever implicit instance of `Unliftable` is available for given data type you can use it for pattern matching with the help of ascription syntax: +Whenever an implicit instance of `Unliftable` is available for a given data type you can use it for pattern matching with the help of an ascription syntax: scala> val q"${left: Int} + ${right: Int}" = q"2 + 2" left: Int = 2 @@ -27,7 +27,7 @@ Whenever implicit instance of `Unliftable` is available for given data type you 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: +It's important to note that unlifting will not be performed at locations where `Name`, `TermName` or `Modifiers` are extracted by default: scala> val q"foo.${bar: Int}" = q"foo.bar" :29: error: pattern type is incompatible with expected type; @@ -44,7 +44,7 @@ One can also successfully combine unquote splicing and unlifting: 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. +Analogously to lifting, this would unlift arguments of the function, element-wise and wrap the result into a `List`. ## Bring your own @@ -61,22 +61,22 @@ Similarly to liftables one can define your own unliftables: } } -Here one needs to pay attention to a few nuances: +Here one must 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. Similarly to `Liftable`, `Unliftable` defines a helper `apply` function in + the companion object to simplify the creation of `Unliftable` instances. It + take a type parameter `T` as well as a partial function `PartialFunction[Tree, T]` + and returns an `Unliftable[T]`. At all inputs where a partial function is defined + it is expected to return an instance of `T` unconditionally. -1. We only define `Unliftable` for runtime universe, it won't be available in macros. +2. We've only define `Unliftable` for the runtime universe, it won't be available in macros. (see [sharing liftable implementations](/overviews/quasiquotes/lifting.html#reusing-liftable-implementation-between-universes)) -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. Patterns used in this unliftable will only match a fully qualified reference to `Point` that + starts with `_root_`. It won't match other possible shapes of the reference; they have + to be specified by hand. This problem is caused by a lack of [hygiene](/overviews/quasiquotes/hygiene.html). -3. The pattern will also only match trees that have literal `Int` arguments. +4. The pattern will only match trees that have literal `Int` arguments. It won't work for other expressions that might evaluate to `Int`. ## Standard Unliftables @@ -98,5 +98,3 @@ Here one needs to pay attention to a few nuances: `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 index ea398ce88f..be255353e6 100644 --- a/overviews/quasiquotes/usecases.md +++ b/overviews/quasiquotes/usecases.md @@ -12,11 +12,11 @@ outof: 13 ## AST manipulation in macros and compiler plugins -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: +Quasiquotes were designed primary as tool for ast manipulation in macros. A common workflow is to deconstruct arguments with quasiquote patterns and then construct a rewritten result with another quasiquote: - // macro that prints expression code before executing it + // macro that prints the expression code before executing it object debug { - def apply[T](x: =>T): T = macro impl + 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 => @@ -43,7 +43,7 @@ Quasiquotes were designed primary as tool for ast manipulation in macros. Common 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: +To simplify integration with macros we've also made it easier to simply use trees in macro implementations instead of the reify-centric `Expr` api that might be used previously: // 2.10 object Macro { @@ -61,13 +61,13 @@ To simplify integration with macros we've also made it easier to just use trees } } -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. +You no longer need to wrap the return value of a macro with `c.Expr`, or to specify the argument types twice, and the return type in `impl` is now optional. -Quasiquotes can also be used as is in compiler plugins as reflection api is strict subset of compiler's `Global` api. +Quasiquotes can also be used "as is" in compiler plugins as the reflection API is strict subset of the compiler's `Global` API. ## Just in time compilation -Thanks to `ToolBox` api one can generate, compile and run Scala code at runtime: +Thanks to the `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) @@ -77,7 +77,7 @@ Thanks to `ToolBox` api one can generate, compile and run Scala code at runtime: ## 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: +Thanks to the new `showCode` "pretty printer" one can implement an offline code generator that does AST manipulation with the help of quasiquotes, and then serializes that into actual source code right before writing it to disk: object OfflineCodeGen extends App { def generateCode() = @@ -89,5 +89,3 @@ Thanks to new `showCode` pretty printer one can implement offline code generator } saveToFile("myfile.scala", generateCode()) } - -