Last active
November 30, 2017 16:28
-
-
Save TiarkRompf/4738132 to your computer and use it in GitHub Desktop.
generic class lifting and method forwarders
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import language.experimental.macros | |
import language.dynamics | |
import scala.reflect.macros._ | |
case class Rep[+T](x:String) | |
implicit def anyToRep[T](x:T) = new Rep[T](x.toString) | |
class ShouldLift[T] | |
def dyncall[A,B,C](rec: Rep[A], name: String, arg: Rep[B]): Rep[C] = new Rep(rec.x + "." + name + " " + arg.x) | |
type RepContext[T] = Context { type PrefixType = Rep[T] } | |
def impl[T:c.WeakTypeTag](c:RepContext[T])(name: c.Expr[String])(arg: c.Expr[Rep[Any]])(ev: c.Expr[ShouldLift[T]]): c.Expr[Rep[Any]] = { | |
import c.universe._ | |
val Literal(Constant(n: String)) = name.tree | |
val tpe = implicitly[c.WeakTypeTag[T]].tpe | |
val member = tpe.member(newTermName(n)) | |
if (member != NoSymbol) { | |
val Apply(TypeApply(call, _), args1) = reify(dyncall[Any,Any,Any](c.prefix.splice,name.splice,arg.splice)).tree | |
val MethodType(List(pt),rt) = member.typeSignature | |
c.Expr[Rep[Any]](Apply(TypeApply(call,List(tpe,pt.typeSignature,rt) map (TypeTree(_))), args1)) | |
} else c.abort(c.prefix.tree.pos,"not found: "+n) | |
} | |
class LRep[+T](x:String) extends Rep[T](x) with Dynamic { | |
def applyDynamic(name: String)(arg: Rep[Any])(implicit ev: ShouldLift[T]): Rep[Any] = macro impl[T] | |
} | |
// example: lift class Foo | |
class Foo { def bar(x: Int) = x.toString } | |
implicit object liftFoo extends ShouldLift[Foo] | |
val x = new LRep[Foo]("FOO") | |
x bar 7 // ok, returns Rep[String] | |
x bar "7" // wrong type | |
x baz 7 // method not found | |
/* | |
scala> class Foo { def bar(x: Int) = x.toString } | |
defined class Foo | |
scala> implicit object liftFoo extends ShouldLift[Foo] | |
defined module liftFoo | |
scala> val x = new LRep[Foo]("FOO") | |
x: LRep[Foo] = Rep(FOO) | |
scala> x bar 7 | |
res25: Rep[String] = Rep(FOO.bar 7) | |
scala> x bar "7" | |
<console>:246: error: type mismatch; | |
found : Rep[String] | |
required: Rep[Int] | |
x bar "7" | |
^ | |
scala> x baz 7 | |
<console>:246: error: not found: baz | |
x baz 7 | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment