I solved this with a simple macro:
trait CallApply[C] {
type Ret
def apply(c: C): Ret
}
object CallApply {
type Aux[C, R] = CallApply[C] { type Ret = R }
implicit def materialize[C, R]: Aux[C, R] = macro CallApplyImpl.materialize[C]
}
object CallApplyImpl {
import scala.reflect.macros.whitebox
def materialize[C: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
import c.universe._
val C = weakTypeOf[C]
val assignM = C.decls.collect {
case sym: MethodSymbol if sym.name == TermName("apply") => sym
}
if (assignM.headOption.isEmpty) c.abort(c.enclosingPosition, "case class must define an apply() method")
val R = assignM.head.returnType
q"""new _root_.fwb.api.CallApply[$C] { type Ret = $R; def apply(c: $C) : $R = c.apply() }"""
}
}
Using:
object Node {
def call[L](implicit lgen: LabelledGeneric[L], ev: CallApply[L]): Node[lgen.Repr, ev.Ret] = {
new Node[lgen.Repr, ev.Ret] {
def call(args: lgen.Repr): ev.Ret = {
ev(lgen.from(args))
}
}
}
}
And it val n = Node[Kls]works as expected. However, it would be nice to see a solution without metaprogramming (if possible).
source
share