Skip to content

Instantly share code, notes, and snippets.

@rkuhn
Last active December 10, 2015 22:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rkuhn/4505639 to your computer and use it in GitHub Desktop.
Save rkuhn/4505639 to your computer and use it in GitHub Desktop.
Makkros blog post
def err(msg: String) = c.error(c.enclosingPosition, msg)
def getSenderChannel = {
val replyChannel = c.inferImplicitValue(c.typeOf[ChannelRef[_]])
if (!replyChannel.isEmpty) {
import u._
replyChannel.tpe match {
case TypeRef(_, _, param :: Nil) ⇒
Some((param,
replyChannel,
c.Expr(Select(replyChannel, "actorRef"))(
u.weakTypeTag[ActorRef])
))
}
} else None
}
lazy val macros = Project(
id = "akka-macros",
base = file("akka-macros"),
dependencies = Seq(actor),
settings = defaultSettings ++ Seq(
libraryDependencies <+=
(scalaVersion)("org.scala-lang" % "scala-reflect" % _)
)
)
lazy val macroTests = Project(
id = "akka-macro-tests",
base = file("akka-macro-tests"),
dependencies = Seq(macros, testkit % "compile;test->test"),
settings = defaultSettings ++ Seq(
libraryDependencies <+=
(scalaVersion)("org.scala-lang" % "scala-compiler" % _)
)
)
"A Channel" must {
"not permit wrong message type" in {
intercept[ToolBoxError] {
eval("""
|import akka.channels._
|import ChannelSpec._
|new ChannelRef[(A, C) :+: TNil](null) ! B
""".stripMargin)
}.message must include("target ChannelRef does not support " +
"messages of types akka.channels.ChannelSpec.B.type")
}
}
"A Channel" must {
"select return channels" in {
val ext = ChannelExt(system).
val ref = ext.actorOf(new Tester)
implicit val selfChannel = ext.actorOf(new RecvC(testActor))
ref ! A
expectMsg(C)
lastSender must be(selfChannel.actorRef)
}
}
verify(Set(u.weakTypeOf[M]), Set(u.typeOf[Nothing]), 1)
u.reify(c.prefix.splice.actorRef.tell(msg.splice, sender.splice))
/**
* find all input channels matching the given message type and return a
* list of their respective reply channels
*/
final def replyChannels(u: Universe)(
list: u.Type, msg: u.Type): List[u.Type] = {
import u._
def rec(l: Type, acc: List[Type]): List[Type] = {
l match {
case TypeRef(_, _, TypeRef(_, _, in :: out :: Nil) :: tail :: Nil)
if msg <:< in ⇒
rec(tail, if (acc contains out) acc else out :: acc)
case TypeRef(_, _, _ :: tail :: Nil) ⇒
rec(tail, acc)
case _ ⇒ acc.reverse
}
}
val n = typeOf[Nothing]
if (msg =:= n) List(n) else rec(list, Nil)
}
sealed trait ChannelList
sealed trait TNil extends ChannelList
sealed trait :+:[A <: (_, _), B <: ChannelList] extends ChannelList
class ChannelRef[+T <: ChannelList](val actorRef: ActorRef) extends AnyVal {
def ![M](msg: M): Unit = macro ChannelRef.tell[T, M]
}
object ChannelRef {
def tell[T <: ChannelList: c.WeakTypeTag, M: c.WeakTypeTag](
c: Context {
type PrefixType = ChannelRef[T]
})(msg: c.Expr[M]): c.Expr[Unit] = {
import c.{ universe ⇒ u }
// the other parts shown below go in here
}
}
def eval(code: String, compileOptions: String =
"-cp akka-actor/target/classes:akka-macros/target/classes"): Any = {
val tb = mkToolbox(compileOptions)
tb.eval(tb.parse(code))
}
def mkToolbox(compileOptions: String = ""): ToolBox[_ <: Universe] = {
val m = scala.reflect.runtime.currentMirror
m.mkToolBox(options = compileOptions)
}
def verify(msg: Set[u.Type], checked: Set[u.Type], depth: Int): Unit =
if (msg.nonEmpty) {
val replies = msg map (m ⇒ m -> replyChannels(u)(tT, m))
val missing = replies collect { case (k, v) if v.size == 0 ⇒ k }
if (missing.nonEmpty)
err(s"target ChannelRef does not support messages of types ${
missing mkString ", "
} (at depth $depth)")
else {
val nextSend =
replies.map(_._2).flatten map (m ⇒ m -> replyChannels(u)(tS, m))
val nextMissing = nextSend collect { case (k, v) if v.size == 0 ⇒ k }
if (nextMissing.nonEmpty)
err(s"implicit sender `$senderTree` does not support messages of" +
s" the reply types ${nextMissing mkString ", "} (at depth $depth)")
else {
val nextChecked = checked ++ msg
val nextMsg = nextSend.map(_._2).flatten -- nextChecked
verify(nextMsg, nextChecked, depth + 1)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment