Skip to content

Instantly share code, notes, and snippets.

@adamw
Created September 6, 2022 07:15
Show Gist options
  • Save adamw/c3d9cdc19b2a2b466479a907cf882b2b to your computer and use it in GitHub Desktop.
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?
// 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