Last active
December 15, 2015 13:09
-
-
Save paulp/5265030 to your computer and use it in GitHub Desktop.
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
scala> class Bippy(xs: List[Int]) extends improving.TypesafeProxy(xs) { def isEmpty = true } | |
defined class Bippy | |
scala> val bippy = new Bippy(1 to 10 toList) | |
bippy: Bippy = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) | |
scala> bippy.slice(3, 7) | |
[proxy] $line4.$read.$iw.$iw.bippy.slice(3, 7) | |
res1: List[Int] = List(4, 5, 6, 7) | |
scala> bippy.dingo(3, 7) | |
<console>:10: error: not found: dingo | |
bippy.dingo(3, 7) | |
^ | |
scala> bippy.isEmpty | |
res3: Boolean = true | |
scala> bippy.nonEmpty | |
res4: Boolean = true |
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
package improving | |
import scala.language.dynamics | |
import scala.language.experimental.macros | |
import scala.reflect.runtime.{ universe => ru } | |
import scala.reflect.macros.Context | |
class TypesafeProxy[T: ru.TypeTag](val underlying: T) extends Dynamic { | |
def selectDynamic(selection: String): Any = macro Macro.proxySelectDynamic[T] | |
def applyDynamic(selection: String)(args: Any*): Any = macro Macro.proxyApplyDynamic[T] | |
override def toString = underlying.toString | |
override def hashCode = underlying.hashCode | |
override def equals(other: Any) = underlying equals other | |
} | |
object Macro { | |
def proxyApplyDynamic[T: c.WeakTypeTag](c: Context)(selection: c.Expr[String])(args: c.Expr[Any]*) = { | |
import c.universe._ | |
val Literal(Constant(name: String)) = selection.tree | |
val methods = weakTypeOf[T].members filter (_.isMethod) map (_.asMethod) | |
val sameName = methods filter (_.name.toString == name) | |
val sameArity = sameName filter (m => m.paramss.nonEmpty && m.paramss.head.size == args.size) | |
if (sameName.isEmpty) | |
c.abort(c.enclosingPosition, s"not found: $name") | |
else if (sameArity.isEmpty) | |
c.abort(c.enclosingPosition, s"not found: $name with first parameter list arity ${args.size} (did find: $sameName)") | |
else if (sameArity.tail.nonEmpty) | |
c.abort(c.enclosingPosition, s"couldn't resolve overloaded selection among " + sameArity.mkString(" <and> ")) | |
else { | |
val m = sameArity.head | |
val fun = Select(Select(c.prefix.tree, newTermName("underlying")), m) | |
val fargs = args.toList map (_.tree) | |
val fargs_s = fargs mkString ("(", ", ", ")") | |
val msg = c.Expr[String](Literal(Constant(s"[proxy] ${c.prefix.tree}.$name$fargs_s"))) | |
val echo = reify(Console.err.println(msg.splice)).tree | |
c.Expr( | |
Block( | |
echo, | |
Apply(fun, fargs) | |
) | |
) | |
} | |
} | |
def proxySelectDynamic[T: c.WeakTypeTag](c: Context)(selection: c.Expr[String]) = { | |
import c.universe._ | |
val Literal(Constant(name: String)) = selection.tree | |
val nullaryMembers = weakTypeOf[T].members filter (s => s.isMethod && s.asMethod.paramss.flatten.isEmpty) | |
nullaryMembers find (_.name.toString == name) match { | |
case Some(m) => c.Expr(Select(Select(c.prefix.tree, newTermName("underlying")), m.name)) | |
case _ => c.abort(c.enclosingPosition, s"not found: $name") | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment