Skip to content

Grammar and Cleanup in Scala 3 Macro Documentation #2121

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 20 additions & 25 deletions _overviews/scala3-macros/best-practices.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ num: 8
## Inline

### Be careful when inlining for performance
To take the most advantage of the JVM JIT optimisations you want to avoid generating large methods.
To take the most advantage of the JVM JIT optimisations, you want to avoid generating large methods.


## Macros
Expand All @@ -16,24 +16,24 @@ To take the most advantage of the JVM JIT optimisations you want to avoid genera
## Quoted code

### Keep quotes readable
* Try to avoid `${..}` with arbitrary expressions inside
* Try to avoid `${...}` with arbitrary expressions inside
* Use `$someExpr`
* Use `${ someExprFrom('localExpr) }`

To illustrate, consider the following example:
```scala
val x: StringContext = ...
'{ StringContext(${Varargs(stringContext.parts.map(Expr(_)))}: _*) }
val sc: StringContext = ...
'{ StringContext(${Varargs(sc.parts.map(Expr(_)))}: _*) }
```
Instead we can write the following:
Instead, we can write the following:

```scala
val x: StringContext = ...
val partExprs = stringContext.parts.map(Expr(_))
val sc: StringContext = ...
val partExprs = sc.parts.map(Expr(_))
val partsExpr = Varargs(partExprs)
'{ StringContext($partsExpr: _*) }
```
The contents of the quote are cleared this way.
The contents of the quote are much more clear in the second example.

### Avoid nested contexts

Expand Down Expand Up @@ -74,34 +74,29 @@ val leafSym: Symbol = leafTpe.typeSymbol

### Avoid `Symbol.tree`

On an object `sym: Symbol`, `sym.tree` returns the `Tree` associated to the
symbol. Be careful when using this method as the tree for a symbol might not be
defined. When the code associated to the symbol is defined in a different
moment than this access, if the `-Yretain-trees` compilation option is not
used, then the `tree` of the symbol will not be available. Symbols originated
from Java code do not have an associated `tree`.
On an object `sym: Symbol`, `sym.tree` returns the `Tree` associated with the symbol.
Be careful when using this method, as the tree for a symbol might not be defined.
When the code associated with a symbol is defined at a different time than this access, if the `-Yretain-trees` compilation option is not used, then the `tree` of the symbol will not be available.
Symbols originating from Java code do not have an associated `tree`.

### Obtaining a `TypeRepr` from a `Symbol`

In the previous paragraph we saw that `Symbol.tree` should be avoided and
therefore you should not use `sym.tree.tpe` on `sym: Symbol`. Thus to obtain
the `TypeRepr` corresponding to a `Symbol`, it is recommended to use
`tpe.memberType` on objects `tpe: TypeRepr`.
In the previous heading, we saw that `Symbol.tree` should be avoided and that therefore you should not use `sym.tree.tpe` on `sym: Symbol`.
Thus, to obtain the `TypeRepr` corresponding to a `Symbol`, it is recommended to use `tpe.memberType` on `tpe: TypeRepr` objects.

We can obtain the `TypeRepr` of `Leaf` in two ways:
1. `TypeRepr.of[Box.Leaf]`
2. `boxTpe.memberType(leafSym)`, in other words we request the `TypeRepr` of
the member of `Box` whose symbol is equal to the symbol of sym
2. `boxTpe.memberType(leafSym)`
(In other words, we request the `TypeRepr` of the member of `Box` whose symbol is equal to the symbol of `leafSym`.)

while the two approaches are equivalent, the first is possible only if you
already know that you are looking for `Box.Leaf`. The second approach allows
you to explore an uknown API.
While the two approaches are equivalent, the first is only possible if you already know that you are looking for the type `Box.Leaf`.
The second approach allows you to explore an unknown API.

### Use `Symbol`s to compare definitions

Read more about Symbols [here][symbol].

Symbols allow comparing definitions using `==`:
Symbols allow you to compare definitions using `==`:
```scala
leafSym == baseSym.children.head // Is true
```
Expand All @@ -113,7 +108,7 @@ boxTpe.memberType(baseSym.children.head) == leafTpe // Is false

### Obtaining a Symbol for a type

There is a handy shortcut to get the symbol of the definition of `T`.
There is a handy shortcut to get the symbol for the definition of `T`.
Instead of

```scala
Expand Down
10 changes: 5 additions & 5 deletions _overviews/scala3-macros/tutorial/macros.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ As a technicaly consequence, we cannot define and use a macro in the **same clas
However, it is possible to have the macro definition and its call in the **same project** as long as the implementation of the macro can be compiled first.

> ##### Suspended Files
> To allow defining and using macros in the same project, only those calls to macros are expanded, where the macro has already been compiled.
> To allow defining and using macros in the same project, only those calls to macros that have already been compiled are expanded.
> For all other (unknown) macro calls, the compilation of the file is _suspended_.
> Suspended files are only compiled after all non suspended files have been successfully compiled.
> In some cases, you will have _cyclic dependencies_ that will block the completion of the compilation.
Expand Down Expand Up @@ -206,7 +206,7 @@ def sumCode(nums: Expr[Seq[Int]])(using Quotes): Expr[Int] =
The extractor will match a call to `sumNow(1, 2, 3)` and extract a `Seq[Expr[Int]]` containing the code of each parameter.
But, if we try to match the argument of the call `sumNow(nums: _*)`, the extractor will not match.

`Varargs` can also be used as a constructor, `Varargs(Expr(1), Expr(2), Expr(3))` will return a `Expr[Seq[Int]]`.
`Varargs` can also be used as a constructor. `Varargs(Expr(1), Expr(2), Expr(3))` will return an `Expr[Seq[Int]]`.
We will see how this can be useful later.


Expand All @@ -226,8 +226,8 @@ while subsequent chapters introduce the more advanced APIs.
### Collections

We have seen how to convert a `List[Int]` into an `Expr[List[Int]]` using `Expr.apply`.
How about converting a `List[Expr[Int]]` into `Expr[List[Int]]`?
We mentioned that `Varargs.apply` can do this for sequences -- likewise for other collection types, corresponding methods are available:
How about converting a `List[Expr[Int]]` into an `Expr[List[Int]]`?
We mentioned that `Varargs.apply` can do this for sequences; likewise, for other collection types, corresponding methods are available:

* `Expr.ofList`: Transform a `List[Expr[T]]` into `Expr[List[T]]`
* `Expr.ofSeq`: Transform a `Seq[Expr[T]]` into `Expr[Seq[T]]` (just like `Varargs`)
Expand Down Expand Up @@ -269,7 +269,7 @@ Note, that `matches` only performs a limited amount of normalization and while f
### Arbitrary Expressions

Last but not least, it is possible to create an `Expr[T]` from arbitary Scala code by enclosing it in [quotes][quotes].
For example `'{ ${expr}; true }` will generate an `Expr[Int]` equivalent to `Expr.block(List(expr), Expr(true))`.
For example, `'{ ${expr}; true }` will generate an `Expr[Int]` equivalent to `Expr.block(List(expr), Expr(true))`.
The subsequent section on [Quoted Code][quotes] presents quotes in more detail.

[contributing]: {% link scala3/contribute-to-docs.md %}
Expand Down
Loading