• Stars
    star
    125
  • Rank 286,335 (Top 6 %)
  • Language
    Scala
  • Created over 4 years ago
  • Updated about 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Various tasks solved via metaprogramming in Dotty

Scala 3 Metaprogramming Examples

CI

This repo contains a bunch of examples of doing metaprogramming in Scala 3 using the low level reflection API.

Every folder is a separate example. Each example contains a README.md file with a description of what the example does.

To run an example:

  1. Clone and cd into the repo using git clone https://github.com/anatoliykmetyuk/dotty-macro-examples.git && cd dotty-macro-examples

  2. Use ./mill <example_name>.run command to run the example you are interested in. E.g. ./mill macroTypeClassDerivation.run runs macroTypeClassDerivation example.

Examples

  • abstractTypeclassBody – how to abstract a body of a function inside a macro-generated class into a separate macro.
  • accessMembersByName – access an arbitrary member of a value given this member's name as a String.
  • accessEnclosingParameters - access the arguments passed to the enclosing method of the macro call.
  • defaultParamsInference – given a case class with default parameters, obtain the values of these default parameters.
  • fullClassName - get a fully qualified name of a class.
  • isMemberOfSealedTraitHierarchy - check if a class inherits from a sealed trait.
  • macroTypeClassDerivation – typeclass construction done with Quotes Reflection.
  • outOfScopeMethodCall – get a reference to this where the type of this may not be known on macro definition site, and call a method on this.
  • outOfScopeTypeParam – get a reference to a type that is not available to the macro definition's scope. Then use this reference as a type parameter to call a method.
  • outOfScopeClassConstructor – get a reference to a type that is not available to the macro definition's scope. Then use this reference to construct an instance of that type via new.
  • buildingCustomASTs – Quotes Reflection ASTs are powerful, but how do you know the right way to build one? This example demonstrates how to inspect compiler-generated ASTs for a given Scala code. You can then mimic the compiler when constructing similar ASTs.
  • contextParamResolution – showcases how to use Quotes Reflection to construct an AST for a method call that takes context parameters. Shows how to resolve those parameters using Quotes Reflection API.
  • passVarargsIntoAST - showcases how to pass varargs as parameters into the AST of the method
  • primaryConstructor - showcases how to use primary constructor Symbol and Term.
  • referenceVariableFromOtherExpr - how to use a variable at an Expr other than where it is defined at.
  • reflectionLambda - how to create a lambda via TASTy Reflection.

Tips and Tricks

  • When working with reflect API, all of the API available to you is defined in the dotty/library/src/scala/quoted/Quotes.scala. As a rule, for every reflected type X, you have a group of extension methods in trait XMethods which defines all of the methods you can call on X. For example, for Symbol, you can search for SymbolMethods in Quotes.scala to see what you can do with it.
  • TypeTree.of[T] gives you a quotes.reflect.TypeTree. To get a quotes.reflect.TypeRepr, you can call TypeRepr.of[T].
  • Most of the interesting data about the types reside in their Symbols. To get a Symbol given a tpe: quoted.Type[T], use TypeTree.of[T].symbol.
  • If you have a Symbol, you can use Ref to get a Tree referring to that symbol – either Ident or Select. For example, if you have a sym: Symbol that refers to a DefDef method definition and you want to obtain a tree referring to that method, you can get this tree via Ref(sym).