Skip to content

Instantly share code, notes, and snippets.

@xuwei-k
Created May 26, 2021 04:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xuwei-k/024cb54b41b98faba1f46058d286cd3e to your computer and use it in GitHub Desktop.
Save xuwei-k/024cb54b41b98faba1f46058d286cd3e to your computer and use it in GitHub Desktop.
case class companion `.tupled` in Scala 3
package example
import scala.quoted.*
object CompanionFunctionN {
extension [A] (self: A) {
transparent inline def asFunction: Any = ${Macro.asFunction[A]('self)}
transparent inline def tupled: Any = ${Macro.tupled[A]('self)}
}
}
object Macro {
private def impl[A: Type](expr: Expr[A])(using q: Quotes): q.reflect.Term = {
import q.reflect.*
val a = TypeRepr.of[A]
val companion = a.typeSymbol.companionModule
val ap = companion.declaredMethods.filter(_.name == "apply") match {
case List(m) =>
m
case Nil =>
report.throwError("not found `apply`")
case values =>
report.throwError("found multiple `apply`")
}
Select(expr.asTerm, ap).etaExpand(ap)
}
def asFunction[A: Type](expr: Expr[A])(using q: Quotes): Expr[Any] = {
import q.reflect.*
impl[A](expr).asExpr
}
def tupled[A: Type](expr: Expr[A])(using q: Quotes): Expr[Any] = {
import q.reflect.*
val f = impl[A](expr)
Select.unique(f, "tupled").asExpr
}
}
package example
case class Foo(x1: Int, x2: String)
object Main {
import CompanionFunctionN._
def typed[A](a: A): Unit = ()
def main(args: Array[String]): Unit = {
val f1 = Foo.asFunction
val f2 = Foo.tupled
typed[(Int, String) => Foo](f1)
typed[Tuple2[Int, String] => Foo](f2)
println(f1.getClass.getInterfaces.toList)
assert(f1.getClass.getInterfaces.exists(_ == classOf[Function2[_, _, _]]))
println(f2.getClass.getInterfaces.toList)
assert(f2.getClass.getInterfaces.exists(_ == classOf[Function1[_, _]]))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment