Skip to content

Update metaprogramming docs #11033

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
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
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ We also connect the new lower-level reflection layer to the existing principled
- `unseal` that unseals an `Expr[T]` (non traversable code) into a `Term` and
- `seal` that seals back a `Term` into an `Expr[T]`.

Read the [relevant documentation](https://dotty.epfl.ch/docs/reference/metaprogramming/tasty-reflect.html) to learn how to go from quotes and splices to TASTys Reflect trees and back .
Read the [relevant documentation](https://dotty.epfl.ch/docs/reference/metaprogramming/reflection.html) to learn how to go from quotes and splices to TASTys Reflect trees and back .

### Alignments with the Scala Improvement Process

Expand Down
4 changes: 2 additions & 2 deletions docs/docs/reference/dropped-features/macros.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ The previous, experimental macro system has been dropped.
Instead, there is a cleaner, more restricted system based on two complementary concepts: `inline` and `'{ ... }`/`${ ... }` code generation.
`'{ ... }` delays the compilation of the code and produces an object containing the code, dually `${ ... }` evaluates an expression which produces code and inserts it in the surrounding `${ ... }`.
In this setting, a definition marked as inlined containing a `${ ... }` is a macro, the code inside the `${ ... }` is executed at compile-time and produces code in the form of `'{ ... }`.
Additionally, the contents of code can be inspected and created with a more complex reflection API (TASTy Reflect) as an extension of `'{ ... }`/`${ ... }` framework.
Additionally, the contents of code can be inspected and created with a more complex reflection API as an extension of `'{ ... }`/`${ ... }` framework.

* `inline` has been [implemented](../metaprogramming/inline.md) in Scala 3.
* Quotes `'{ ... }` and splices `${ ... }` has been [implemented](../metaprogramming/macros.md) in Scala 3.
* [TASTy reflect](../metaprogramming/tasty-reflect.md) provides more complex tree based APIs to inspect or create quoted code.
* [TASTy reflect](../metaprogramming/reflection.md) provides more complex tree based APIs to inspect or create quoted code.
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
---
layout: doc-page
title: "TASTy Reflect"
title: "Reflection"
---

TASTy Reflect enables inspection and construction of Typed Abstract Syntax Trees
Reflection enables inspection and construction of Typed Abstract Syntax Trees
(Typed-AST). It may be used on quoted expressions (`quoted.Expr`) and quoted
types (`quoted.Type`) from [Macros](./macros.md) or on full TASTy files.

If you are writing macros, please first read [Macros](./macros.md).
You may find all you need without using TASTy Reflect.
You may find all you need without using quote reflection.

## API: From quotes and splices to TASTy reflect trees and back

With `quoted.Expr` and `quoted.Type` we can compute code but also analyze code
by inspecting the ASTs. [Macros](./macros.md) provide the guarantee that the
generation of code will be type-correct. Using TASTy Reflect will break these
generation of code will be type-correct. Using quote reflection will break these
guarantees and may fail at macro expansion time, hence additional explicit
checks must be done.

Expand All @@ -34,20 +34,20 @@ def natConstImpl(x: Expr[Int])(using Quotes): Expr[Int] =

### Extractors

`import quotes.reflect._` will provide all extractors and methods on TASTy
Reflect trees. For example the `Literal(_)` extractor used below.
`import quotes.reflect._` will provide all extractors and methods on `quotes.reflect.Tree`s.
For example the `Literal(_)` extractor used below.

```scala
def natConstImpl(x: Expr[Int])(using Quotes): Expr[Int] =
import quotes.reflect._
val xTree: Term = x.asTerm
xTree match
val tree: Term = x.asTerm
tree match
case Inlined(_, _, Literal(IntConstant(n))) =>
if n <= 0 then
report.error("Parameter must be natural number")
'{0}
else
xTree.asExprOf[Int]
tree.asExprOf[Int]
case _ =>
report.error("Parameter must be a known constant")
'{0}
Expand All @@ -58,9 +58,9 @@ which returns the string representation the structure of the tree. Other printer
can also be found in the `Printer` module.

```scala
xTree.show(using Printer.TreeStructure)
tree.show(using Printer.TreeStructure)
// or
Printer.TreeStructure.show(xTree)
Printer.TreeStructure.show(tree)
```

The methods `quotes.reflect.Term.{asExpr, asExprOf}` provide a way to go back to
Expand Down Expand Up @@ -94,40 +94,37 @@ def macroImpl()(quotes: Quotes): Expr[Unit] =

### Tree Utilities

`scala.tasty.reflect` contains three facilities for tree traversal and
`quotes.reflect` contains three facilities for tree traversal and
transformation.

`TreeAccumulator` ties the knot of a traversal. By calling `foldOver(x, tree))`
`TreeAccumulator` ties the knot of a traversal. By calling `foldOver(x, tree)(owner)`
we can dive into the `tree` node and start accumulating values of type `X` (e.g.,
of type `List[Symbol]` if we want to collect symbols). The code below, for
example, collects the pattern variables of a tree.
example, collects the `val` definitions in the tree.

```scala
def collectPatternVariables(tree: Tree)(using ctx: Context): List[Symbol] =
val acc = new TreeAccumulator[List[Symbol]]:
def apply(syms: List[Symbol], tree: Tree)(using ctx: Context): List[Symbol] = tree match
case Bind(_, body) => apply(tree.symbol :: syms, body)
case _ => foldOver(syms, tree)
def foldTree(syms: List[Symbol], tree: Tree)(owner: Symbol): List[Symbol] = tree match
case ValDef(_, _, rhs) =>
val newSyms = tree.symbol :: syms
foldTree(newSyms, body)(tree.symbol)
case _ =>
foldOverTree(syms, tree)(owner)
acc(Nil, tree)
```

A `TreeTraverser` extends a `TreeAccumulator` and performs the same traversal
but without returning any value. Finally a `TreeMap` performs a transformation.
but without returning any value. Finally, a `TreeMap` performs a transformation.

#### Let
#### ValDef.let

`scala.tasty.Reflection` also offers a method `let` that allows us to bind the
`rhs` (right-hand side) to a `val` and use it in `body`. Additionally, `lets`
binds the given `terms` to names and allows to use them in the `body`. Their type
definitions are shown below:
`quotes.reflect.ValDef` also offers a method `let` that allows us to bind the `rhs` (right-hand side) to a `val` and use it in `body`.
Additionally, `lets` binds the given `terms` to names and allows to use them in the `body`.
Their type definitions are shown below:

```scala
def let(rhs: Term)(body: Ident => Term): Term = ...

def lets(terms: List[Term])(body: List[Term] => Term): Term = ...
```

## More Examples

* Start experimenting with TASTy Reflect ([link](https://github.com/nicolasstucki/tasty-reflection-exercise))
(outdated, need update)
2 changes: 1 addition & 1 deletion docs/docs/reference/metaprogramming/staging.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
layout: doc-page
title: "Multi-Stage Programming"
title: "Runtime Multi-Stage Programming"
---

The framework expresses at the same time compile-time metaprogramming and
Expand Down
25 changes: 11 additions & 14 deletions docs/docs/reference/metaprogramming/tasty-inspect.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,27 @@ through the TASTy reflect API.

## Inspecting TASTy files

To inspect the TASTy Reflect trees of a TASTy file a consumer can be defined in
the following way.
To inspect the trees of a TASTy file a consumer can be defined in the following way.

```scala
import scala.tasty.Reflection
import scala.tasty.file._
import scala.quoted._
import scala.tasty.inspector._

class Consumer extends TastyInspector:
final def apply(reflect: Reflection)(root: reflect.Tree): Unit =
import reflect._
// Do something with the tree
class MyInspector extends TastyInspector:
protected def processCompilationUnit(using Quotes)(tree: quotes.reflect.Tree): Unit =
import quotes.reflect._
// Do something with the tree
```

Then the consumer can be instantiated with the following code to get the tree of
the class `foo.Bar` for a foo in the classpath.
Then the consumer can be instantiated with the following code to get the tree of the `foo/Bar.tasty` file.

```scala
object Test:
def main(args: Array[String]): Unit =
InspectTasty("", List("foo.Bar"), new Consumer)
def main(args: Array[String]): Unit =
new MyInspector().inspectTastyFiles("foo/Bar.tasty")
```

Note that if we need to run the main (in the example below defined in an object called `Test`) after
compilation we need to make the compiler available to the runtime:
Note that if we need to run the main (in the example below defined in an object called `Test`) after compilation we need to make the compiler available to the runtime:

```shell
scalac -d out Test.scala
Expand Down
4 changes: 2 additions & 2 deletions docs/docs/reference/metaprogramming/toc.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ introduce the following fundamental facilities:
to program code. Together with `inline`, these two abstractions allow
to construct program code programmatically.

3. [Staging](./staging.md) Where macros construct code at _compile-time_,
3. [Runtime Staging](./staging.md) Where macros construct code at _compile-time_,
staging lets programs construct new code at _runtime_. That way,
code generation can depend not only on static data but also on data available at runtime. This splits the evaluation of the program in two or more phases or ...
stages. Consequently, this method of generative programming is called "Multi-Stage Programming". Staging is built on the same foundations as macros. It uses
quotes and splices, but leaves out `inline`.

4. [TASTy Reflection](./tasty-reflect.md) Quotations are a "black-box"
4. [Reflection](./reflection.md) Quotations are a "black-box"
representation of code. They can be parameterized and composed using
splices, but their structure cannot be analyzed from the outside. TASTy
reflection gives a way to analyze code structure by partly revealing the representation type of a piece of code in a standard API. The representation
Expand Down
6 changes: 3 additions & 3 deletions docs/sidebar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,10 @@ sidebar:
url: docs/reference/metaprogramming/inline.html
- title: Macros
url: docs/reference/metaprogramming/macros.html
- title: Staging
- title: Runtime Staging
url: docs/reference/metaprogramming/staging.html
- title: TASTy Reflection
url: docs/reference/metaprogramming/tasty-reflect.html
- title: Reflection
url: docs/reference/metaprogramming/reflection.html
- title: TASTy Inspection
url: docs/reference/metaprogramming/tasty-inspect.html
- title: Other New Features
Expand Down