-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add basic hands on tutorial #5493
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
nicolasstucki
merged 6 commits into
scala:master
from
dotty-staging:add-tasty-reflect-basic-tutorial
Nov 26, 2018
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
c514aaa
Add basic hands on tutorial
nicolasstucki b0408eb
Address comments
nicolasstucki d727a7b
Standardize terminology
nicolasstucki 13e5e0d
Fix typo and move text
nicolasstucki 5b5ef9a
Remove DoWhile from the docs
nicolasstucki 906fc1f
Fix typos
nicolasstucki File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
--- | ||
layout: doc-page | ||
title: "TASTy reflect" | ||
--- | ||
|
||
TASTy Reflect enables inspection and construction of Typed Abstract Syntax Trees (TAST). | ||
It may be used on quoted expressions (`quoted.Expr`) and quoted types (`quoted.Type`) from [Principled Meta-programming](./principled-meta-programming.html) | ||
or on full TASTy files. | ||
|
||
If you are writing macros, please first read [Principled Meta-programming](./principled-meta-programming.html). | ||
You may find all you need without using TASTy Reflect. | ||
|
||
|
||
## From quotes and splices to TASTs Reflect trees and back | ||
|
||
`quoted.Expr` and `quoted.Type` are only meant for generative meta-programming, generation of code without inspecting the ASTs. | ||
[Principled Meta-programming](./principled-meta-programming.html) provides the guarantee that the generation of code will be type-correct. | ||
Using TASTy Reflect will break these guarantees and may fail at macro expansion time, hence additional explicit check must be done. | ||
|
||
|
||
To provide reflection capabilities in macros we need to add an implicit parameter of type `scala.tasty.Reflection` and import it in the scope where it is used. | ||
|
||
```scala | ||
import scala.quoted._ | ||
import scala.tasty._ | ||
|
||
inline def natConst(x: Int): Int = ~natConstImpl('(x)) | ||
|
||
def natConstImpl(x: Expr[Int])(implicit reflection: Reflection): Expr[Int] = { | ||
import reflection._ | ||
... | ||
} | ||
``` | ||
|
||
`import reflection._` will provide a `reflect` extension method on `quoted.Expr` and `quoted.Type` which return a `reflection.Term` and `reflection.TypeTree` respectively. | ||
It will also import all extractors and methods on TASTy Reflect trees. For example the `Term.Literal(_)` extractor used below. | ||
|
||
```scala | ||
def natConstImpl(x: Expr[Int])(implicit reflection: Reflection): Expr[Int] = { | ||
import reflection._ | ||
val xTree: Term = x.reflect | ||
xTree match { | ||
case Term.Literal(Constant.Int(n)) => | ||
if (n <= 0) | ||
throw new QuoteError("Parameter must be natural number") | ||
n.toExpr | ||
case _ => | ||
throw new QuoteError("Parameter must be a known constant") | ||
} | ||
} | ||
``` | ||
|
||
To easily know which extractors are needed, the `reflection.Term.show` method returns the string representation of the extractors. | ||
|
||
The method `reflection.Term.reify[T]` provides a way to to go back to a `quoted.Expr`. | ||
Note that the type must be set explicitly and that if it does not conform to it an exception will be thrown. | ||
In the code above we could have replaced `n.toExpr` by `xTree.reify[Int]`. | ||
|
||
## Inspect a TASTy file | ||
|
||
To inspect the TASTy Reflect trees of a TASTy file a consumer can be defined in the following way. | ||
|
||
```scala | ||
class Consumer extends TastyConsumer { | ||
final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { | ||
import reflect._ | ||
// Do somthing 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. | ||
|
||
```scala | ||
object Test { | ||
def main(args: Array[String]): Unit = { | ||
ConsumeTasty("", List("foo.Bar"), new Consumer) | ||
} | ||
} | ||
``` | ||
nicolasstucki marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## TASTy Reflect API | ||
|
||
TASTy Reflect provides the following types: | ||
|
||
```none | ||
+- Tree -+- PackageClause | ||
+- Import | ||
+- Statement -+- Definition --+- PackageDef | ||
| +- ClassDef | ||
| +- TypeDef | ||
| +- DefDef | ||
| +- ValDef | ||
| | ||
+- Term --------+- Ident | ||
+- Select | ||
+- Literal | ||
+- This | ||
+- New | ||
+- NamedArg | ||
+- Apply | ||
+- TypeApply | ||
+- Super | ||
+- Typed | ||
+- Assign | ||
+- Block | ||
+- Lambda | ||
+- If | ||
+- Match | ||
+- Try | ||
+- Return | ||
+- Repeated | ||
+- Inlined | ||
+- SelectOuter | ||
+- While | ||
|
||
|
||
+- TypeTree ----+- Synthetic | ||
| +- Ident | ||
| +- Select | ||
| +- Project | ||
| +- Singleton | ||
+- TypeOrBoundsTree ---+ +- Refined | ||
| +- Applied | ||
| +- Annotated | ||
| +- And | ||
| +- Or | ||
| +- MatchType | ||
| +- ByName | ||
| +- LambdaTypeTree | ||
| +- Bind | ||
| | ||
+- TypeBoundsTree | ||
+- SyntheticBounds | ||
|
||
+- CaseDef | ||
+- TypeCaseDef | ||
|
||
+- Pattern --+- Value | ||
+- Bind | ||
+- Unapply | ||
+- Alternative | ||
+- TypeTest | ||
|
||
|
||
+- NoPrefix | ||
+- TypeOrBounds -+- TypeBounds | ||
| | ||
+- Type -------+- ConstantType | ||
+- SymRef | ||
+- TermRef | ||
+- TypeRef | ||
+- SuperType | ||
+- Refinement | ||
+- AppliedType | ||
+- AnnotatedType | ||
+- AndType | ||
+- OrType | ||
+- MatchType | ||
+- ByNameType | ||
+- ParamRef | ||
+- ThisType | ||
+- RecursiveThis | ||
+- RecursiveType | ||
+- LambdaType[ParamInfo <: TypeOrBounds] -+- MethodType | ||
+- PolyType | ||
+- TypeLambda | ||
|
||
+- ImportSelector -+- SimpleSelector | ||
+- RenameSelector | ||
+- OmitSelector | ||
|
||
+- Id | ||
|
||
+- Signature | ||
|
||
+- Position | ||
|
||
+- Constant | ||
|
||
+- Symbol --+- PackageSymbol | ||
+- ClassSymbol | ||
+- TypeSymbol | ||
+- DefSymbol | ||
+- ValSymbol | ||
+- BindSymbol | ||
+- NoSymbol | ||
|
||
Aliases: | ||
# TermOrTypeTree = Term | TypeTree | ||
``` | ||
|
||
## More Examples | ||
|
||
* Start experimenting with TASTy Reflect ([link](https://github.com/nicolasstucki/tasty-reflection-exercise)) | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
tests/run-separate-compilation/tasty-macro-const/quoted_1.scala
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import scala.quoted._ | ||
import scala.tasty._ | ||
|
||
object Macros { | ||
|
||
inline def natConst(x: Int): Int = ~natConstImpl('(x)) | ||
|
||
def natConstImpl(x: Expr[Int])(implicit reflection: Reflection): Expr[Int] = { | ||
import reflection._ | ||
val xTree: Term = x.reflect | ||
xTree match { | ||
case Term.Literal(Constant.Int(n)) => | ||
if (n <= 0) | ||
throw new QuoteError("Parameter must be natural number") | ||
xTree.reify[Int] | ||
case _ => | ||
throw new QuoteError("Parameter must be a known constant") | ||
} | ||
} | ||
|
||
} |
9 changes: 9 additions & 0 deletions
9
tests/run-separate-compilation/tasty-macro-const/quoted_2.scala
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
|
||
import Macros._ | ||
|
||
object Test { | ||
def main(args: Array[String]): Unit = { | ||
println(natConst(2)) | ||
} | ||
|
||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is better illustrated by an example, or just remove it and leave it for more advanced examples.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left the example out on purpose, to push them to use quotes and splices when possible.