Skip to content
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

Add more information to reflection guide #2005

Merged
merged 18 commits into from May 27, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
64 changes: 35 additions & 29 deletions _overviews/scala3-macros/best-practices.md
Expand Up @@ -65,32 +65,37 @@ object Box:
case class Leaf(x: Int) extends Base

// Quotes in contextual scope
val boxTpe = TypeRepr.of[Box.type]
val baseTpe = TypeRepr.of[Box.Base]
val baseSym = baseTpe.typeSymbol
val leafTpe = TypeRepr.of[Box.Leaf]
val leafSym = leafTpe.typeSymbol
val boxTpe : TypeRepr = TypeRepr.of[Box.type]
val baseTpe: TypeRepr = TypeRepr.of[Box.Base]
val baseSym: Symbol = baseTpe.typeSymbol
val leafTpe: TypeRepr = TypeRepr.of[Box.Leaf]
val leafSym: Symbol = leafTpe.typeSymbol
```

### Avoid `Symbol.tree`

`Symbol.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 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`.

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

In the previous paragraph we saw that `Symbol.tree` should be avoided and therefore
you should not use `Symbol.tree.tpe`.
Thus to obtain the `TypeRepr` corresponding to a `Symbol`, it is recommended
to use `TypeRepr.memberType`
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`.

We can obtain the `TypeRepr` of `Leaf` in two ways:
vincenzobaz marked this conversation as resolved.
Show resolved Hide resolved
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 sym

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.

### Use `Symbol`s to compare definitions

Expand All @@ -109,7 +114,7 @@ boxTpe.memberType(baseSym.children.head) == leafTpe // Is false
### Obtaining a Symbol for a type
julienrf marked this conversation as resolved.
Show resolved Hide resolved

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

```scala
TypeTree.of[T].tpe.typeSymbol
Expand All @@ -120,32 +125,33 @@ you can use
TypeRepr.of[T].typeSymbol
```

### Use pattern match your way into the API
### Pattern match your way into the API

Pattern matching is a very ergonomic approach to the API. Always have a look at
the `unapply` defined in `*Module` objects.
the `unapply` method defined in `*Module` objects.

### Search the contextual scope in your macros

You can search for implicits instances using `Implicits.search`.
You can search for given instances using `Implicits.search`.

For example:

```scala
val leafMirrorTpe = TypeRepr.of[Mirror.ProductOf[Box.Leaf]]

Implicits.search(leafMirrorTpe) match
case success: ImplicitSearchSuccess =>
val implicitTerm = success.tree
// ...
case faliure: ImplicitSearchFailure =>
def summonOrFail[T: Type]: Expr[T] =
val tpe = TypeRepr.of[T]
Implicits.search(tpe) match
case success: ImplicitSearchSuccess =>
val implicitTerm = success.tree
implicitTerm.asExprOf[T]
case failure: ImplicitSearchFailure =>
reflect.report.throwError("Could not find an implicit for " + Type.show[T])
```
vincenzobaz marked this conversation as resolved.
Show resolved Hide resolved

If you are writing and prefer to handle `Expr`, `Expr.summon` is a
If you are writing a macro and prefer to handle `Expr`s, `Expr.summon` is a
convient wrapper around `Implicits.search`:

```scala
def summoned[T: Type]: Expr[T] =
def summonOrFail[T: Type]: Expr[T] =
Expr.summon[T] match
case Some(imp) => imp
case None => reflect.report.throwError("Could not find an implicit for " + Type.show[T])
Expand Down
6 changes: 3 additions & 3 deletions _overviews/scala3-macros/tutorial/reflection.md
Expand Up @@ -40,14 +40,14 @@ This will import all the types and modules (with extension methods) of the API.

## How to navigate the API

The full imported API can be found in the [API documentation for `scala.quoted.Quotes.reflectModule`][reflection doc].
The full API can be found in the [API documentation for `scala.quoted.Quotes.reflectModule`][reflection doc].
Unfortunately, at this stage, this automatically generated documentation is not very easy to navigate.

The most important element on the page is the hierarchy tree which provides a synthetic overview of the subtyping relationships of
the types in the API. For each type `Foo` in the tree:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might not be fully clear that Foo is a metavariable, we could also use something like <TYPE>Methods. But if you think that is not necessary, then I am also ok with how it is.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But this can also be addressed in a separate PR. To not block any longer, I am going to accept and merge it as is.


- the object `FooMethods` contains the methods available for `Foo`
- the object `FooModule` contains some _static-ish_ methods, most notably constructors (`apply/copy`) and the `unapply` method which provides the extractor(s) required for pattern matching.
- the trait `FooMethods` contains the methods available on the type `Foo`
- the trait `FooModule` contains the static methods available on the object `Foo`. Most notably, constructors (`apply/copy`) and the `unapply` method which provides the extractor(s) required for pattern matching.
- For all types `Upper` such that `Foo <: Upper`, the methods defined in `UpperMethods` are available on `Foo` as well.

For example [`TypeBounds`](https://dotty.epfl.ch/api/scala/quoted/Quotes$reflectModule.html#TypeBounds-0), a subtype of `TypeRepr`, represents a type tree of the form `T >: L <: U`: a type `T` which is a super type of `L`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
For example [`TypeBounds`](https://dotty.epfl.ch/api/scala/quoted/Quotes$reflectModule.html#TypeBounds-0), a subtype of `TypeRepr`, represents a type tree of the form `T >: L <: U`: a type `T` which is a super type of `L`
For example [`TypeBounds`](https://dotty.epfl.ch/api/scala/quoted/Quotes$reflectModule.html#TypeBounds-0), a subtype of `TypeRepr`, represents a type of the form `T >: L <: U`: a type `T` which is a super type of `L`

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might be confusing to call TypeRepr when we have TypeTree around

Expand Down