Preamble: this is based on @Travis Brown's macro based solution to copying case class properties.
Given:
trait Entity[E <: Entity[E]]{self:E=>
def id: Int
def withId(id: Int) = MacroCopy.withId(self,id)
}
case class User(id: Int, name: String) extends Entity[User]
and the Macro implementation:
object MacroCopy {
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
def withId[T](entity: T, id: Int): T = macro withIdImpl[T]
def withIdImpl[T: c.WeakTypeTag]
(c: Context)(entity: c.Expr[T], id: c.Expr[Int]): c.Expr[T] = {
import c.universe._
val tree = reify(entity.splice).tree
val copy = entity.actualType.member(TermName("copy"))
val params = copy match {
case s: MethodSymbol if (s.paramLists.nonEmpty) => s.paramLists.head
case _ => c.abort(c.enclosingPosition, "No eligible copy method!")
}
c.Expr[T](Apply(
Select(tree, copy),
AssignOrNamedArg(Ident(TermName("id")), reify(id.splice).tree) :: Nil
))
}
}
is there a way to somehow defer type inference in such a way that the macro operates on a User
and not the Entity's self type? As it stands the type checker knows nothing about the User
's case class copy method since all it sees is a value of type E
.
I'd like to do:
val u = User(2,"foo")
u.withId(3)
Most of the alternative solutions I've seen entail defining withId as abstract in the Entity trait and then implementing the method in every case class, would prefer to avoid that if possible.