Last active
December 10, 2015 22:58
-
-
Save rkuhn/4505639 to your computer and use it in GitHub Desktop.
Makkros blog post
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
def err(msg: String) = c.error(c.enclosingPosition, msg) |
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
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 | |
} |
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
lazy val macros = Project( | |
id = "akka-macros", | |
base = file("akka-macros"), | |
dependencies = Seq(actor), | |
settings = defaultSettings ++ Seq( | |
libraryDependencies <+= | |
(scalaVersion)("org.scala-lang" % "scala-reflect" % _) | |
) | |
) |
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
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" % _) | |
) | |
) |
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
"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") | |
} | |
} |
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
"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) | |
} | |
} |
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
verify(Set(u.weakTypeOf[M]), Set(u.typeOf[Nothing]), 1) | |
u.reify(c.prefix.splice.actorRef.tell(msg.splice, sender.splice)) |
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
/** | |
* 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) | |
} |
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
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 | |
} | |
} |
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
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) | |
} |
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
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