Skip to content

Instantly share code, notes, and snippets.

@paulp
Last active December 15, 2015 13:09
Show Gist options
  • Save paulp/5265030 to your computer and use it in GitHub Desktop.
Save paulp/5265030 to your computer and use it in GitHub Desktop.
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
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