Skip to content

Instantly share code, notes, and snippets.

@ciuncan
Created March 30, 2021 05:42
Show Gist options
  • Save ciuncan/8b34998da4ac41a41ee560c128f19ac0 to your computer and use it in GitHub Desktop.
Save ciuncan/8b34998da4ac41a41ee560c128f19ac0 to your computer and use it in GitHub Desktop.
Add `tupled` method to the companion object of a case class via macro annotations
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
@compileTimeOnly("enable macro paradise to expand macro annotations")
class Tupled() extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro Tupled.impl
}
object Tupled {
def impl(c: whitebox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
annottees match {
case (c@q"$_ class $tpname[..$tparamss] $_(...$paramss) extends { ..$_ } with ..$_ { $_ => ..$_ }") ::
q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body }" :: Nil =>
val paramDefs = paramss
.map(_.asInstanceOf[List[ValDef]])
val tupleType = paramDefs
.filterNot(_.exists(_.mods.hasFlag(Flag.IMPLICIT)))
.map(_.map(_.tpt))
.foldRight(tq"$tpname[..$tparamss]") { (params, ret) =>
tq"((..$params)) => $ret"
}
val implicitParams = paramDefs
.filter(_.exists(_.mods.hasFlag(Flag.IMPLICIT)))
.flatMap(_.map { case q"$_ val $n: $tpe = $_" =>
q"implicit val $n: $tpe"
})
val tupledDef = implicitParams match {
case Seq() => q"def tupled[..$tparamss]: $tupleType = ($tname.apply[..$tparamss] _).tupled"
case _ => q"def tupled[..$tparamss](..$implicitParams): $tupleType = ($tname.apply[..$tparamss] _).tupled"
}
q"""
$c
$mods object $tname extends { ..$earlydefns } with ..$parents { $self =>
..$body
$tupledDef
}
"""
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment