Created
September 6, 2022 07:15
-
-
Save adamw/c3d9cdc19b2a2b466479a907cf882b2b to your computer and use it in GitHub Desktop.
How to generate an exhaustive match for a sealed trait in a Scala 3 macro?
This file contains 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
// TestMacro.scala | |
import scala.quoted.* | |
object TestMacro { | |
inline def name[E](e: E): String = ${ nameImpl[E]('e) } | |
def nameImpl[E: Type](e: Expr[E])(using Quotes): Expr[String] = { | |
import quotes.reflect.* | |
val eIdent = e.asTerm match { | |
case Inlined(_, _, ei: Ident) => ei | |
case ei: Ident => ei | |
} | |
val tpe = TypeRepr.of[E] | |
val symbol = tpe.typeSymbol | |
val subclasses = symbol.children.toList.sortBy(_.name) | |
val t = Match( | |
eIdent, | |
subclasses.map { subclass => | |
val bindSymbol = Symbol.newBind(Symbol.spliceOwner, "v", Flags.EmptyFlags, TypeIdent(subclass).tpe) | |
CaseDef( | |
Bind(bindSymbol, Typed(Wildcard(), TypeIdent(subclass))), | |
None, | |
Block(Nil, '{ ${Expr(subclass.name)} + ": " + ${Ref(bindSymbol).asExprOf[E]} }.asTerm) | |
) | |
} | |
) | |
println(t.show) | |
t.asExprOf[String] | |
} | |
} | |
// Test.scala | |
sealed trait Base | |
case class One(v: String) extends Base | |
case class Two(u: Int) extends Base | |
object Test extends App { | |
import TestMacro._ | |
val b1: Base = One("x") | |
val b2: Base = Two(2) | |
println(name(b1)) | |
println(name(b2)) | |
} | |
// compiler output, generated code: | |
Test.b1 match { | |
case v: One => | |
"One".+(": ").+(v) | |
case v: Two => | |
"Two".+(": ").+(`v₂`) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment