major facelift: -!-> and -?-> appear

- rename projects to akka-channels and akka-channels-tests
- move implementation into akka.channels.macros package
- remove picking up ActorRef as sender (or none at all)
- factor out logic to make different façades acting upon Future[] or Any
  so that -!-> and -?-> can complement the traditional <-!- and <-?-
- the new operators are easily distinguishable from !/? and the
  rightwards-pointing go with the flow and compose better, let’s try
  them out
This commit is contained in:
Roland 2013-01-22 22:50:09 +01:00
parent cfcc9da9bc
commit e862890ded
18 changed files with 718 additions and 572 deletions

View file

@ -0,0 +1,24 @@
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.channels
import akka.actor.ExtensionKey
import akka.actor.Extension
import akka.actor.ExtendedActorSystem
import scala.reflect.runtime.universe._
import akka.actor.Props
import scala.reflect.ClassTag
import scala.reflect.runtime.universe
object ChannelExt extends ExtensionKey[ChannelExtension]
class ChannelExtension(system: ExtendedActorSystem) extends Extension {
// kick-start the universe (needed due to thread safety issues in runtime mirror)
private val t = typeTag[(Int, Int) :+: TNil]
def actorOf[Ch <: ChannelList](factory: Channels[TNil, Ch]): ChannelRef[Ch] =
new ChannelRef[Ch](system.actorOf(Props(factory)))
}

View file

@ -0,0 +1,30 @@
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.channels
import language.experimental.{ macros makkros }
import akka.actor.ActorRef
import scala.reflect.runtime.universe.TypeTag
import scala.reflect.macros.Context
import scala.annotation.tailrec
import scala.reflect.macros.Universe
import akka.actor.Actor
import akka.actor.ActorContext
import scala.concurrent.Future
import akka.util.Timeout
import akka.AkkaException
import scala.util.control.NoStackTrace
case class NarrowingException(errors: String) extends AkkaException(errors) with NoStackTrace
class ChannelRef[+T <: ChannelList](val actorRef: ActorRef) extends AnyVal {
def <-!-[M](msg: M): Unit = macro macros.Tell.impl[T, M]
def <-?-[M](msg: M): Future[_] = macro macros.Ask.impl[T, M]
def narrow[C <: ChannelList]: ChannelRef[C] = macro macros.Narrow.impl[C, T]
}

View file

@ -0,0 +1,172 @@
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.channels
import language.experimental.{ macros makkros }
import akka.actor.{ Actor, ActorRef }
import scala.reflect.macros.Context
import scala.reflect.runtime.{ universe ru }
import scala.reflect.runtime.universe.TypeTag
import scala.reflect.api.Universe
import scala.runtime.AbstractPartialFunction
import akka.actor.Props
import scala.collection.immutable
import scala.collection.mutable.ArrayBuffer
import scala.reflect.{ classTag, ClassTag }
import scala.concurrent.{ ExecutionContext, Future }
import akka.util.Timeout
import akka.pattern.ask
import scala.util.control.NoStackTrace
import akka.AkkaException
import akka.actor.ExtendedActorSystem
import akka.actor.ActorInitializationException
/**
* Typed channels atop untyped actors.
*
* The idea is that the actor declares all its input types up front, including
* what it expects the sender to handle wrt.replies, and then ChannelRef
* carries this information for statically verifying that messages sent to an
* actor have an actual chance of being processed.
*
* There are several implementation-imposed restrictions:
*
* - not two channels with different input types may have the same erased
* type; this is currently not enforced at compile time (and leads to
* channels being ignored at runtime)
* - messages received by the actor are dispatched to channels based on the
* erased type, which may be less precise than the actual channel type; this
* can lead to ClassCastExceptions if sending through the untyped ActorRef
*/
trait Channels[P <: ChannelList, C <: ChannelList] extends Actor {
import macros.Helpers._
/**
* Create a child actor with properly typed ChannelRef, verifying that this
* actor can handle everything which the child tries to send via its
* `parent` ChannelRef.
*/
def createChild[Pa <: ChannelList, Ch <: ChannelList](factory: Channels[Pa, Ch]): ChannelRef[Ch] = macro macros.CreateChild.impl[C, Pa, Ch]
/**
* Properly typed ChannelRef for the context.parent.
*/
final def parentChannel: ChannelRef[P] = new ChannelRef(context.parent)
/**
* The properly typed self-channel is used implicitly when sending to other
* typed channels for verifying that replies can be handled.
*/
implicit final def selfChannel = new ChannelRef[C](self)
/*
* This map holds the current behavior for each erasure-tagged channel; the
* basic receive impl will dispatch incoming messages according to the most
* specific erased type in this map.
*/
private var behavior = Map.empty[Class[_], FF]
/**
* Functions for storage in the behavior, to get around erasure
*/
private trait FF
private case class F1(f: (WrappedMessage[ChannelList], ChannelRef[ChannelList]) Unit) extends FF
private case class F2(f: (Any, ChannelRef[ChannelList]) Unit) extends FF
/**
* Declare an input channel of the given type; the returned object takes a partial function:
*
* {{{
* channel[A] {
* case (a, s) =>
* // a is of type A and
* // s is a ChannelRef for the sender, capable of sending the declared reply type for A
* }
* }}}
*/
def channel[T]: Channels[P, C]#Behaviorist[Nothing, T] = macro macros.Channel.impl[T, C, P]
class Behaviorist[-R, Ch](tt: ru.TypeTag[Ch], wrapped: Boolean) {
private def ff(recv: R): FF =
if (wrapped)
F1(recv.asInstanceOf[(WrappedMessage[ChannelList], ChannelRef[ChannelList]) Unit])
else
F2(recv.asInstanceOf[(Any, ChannelRef[ChannelList]) Unit])
def apply(recv: R): Unit =
behavior ++= (for (t inputChannels(ru)(tt.tpe)) yield tt.mirror.runtimeClass(t.widen) -> ff(recv))
}
/*
* HORRIBLE HACK AHEAD
*
* Id like to keep this a trait, but traits cannot have constructor
* arguments, not even TypeTags.
*/
protected var channelListTypeTag: TypeTag[C] = _
/**
* Sort so that subtypes always precede their supertypes, but without
* obeying any order between unrelated subtypes (insert sort).
*/
private def sortClasses(in: Iterable[Class[_]]): immutable.Seq[Class[_]] =
(new ArrayBuffer[Class[_]](in.size) /: in) { (buf, cls)
buf.indexWhere(_ isAssignableFrom cls) match {
case -1 buf append cls
case x buf insert (x, cls)
}
buf
}.to[immutable.IndexedSeq]
final lazy val receive = new AbstractPartialFunction[Any, Unit] {
val index = sortClasses(behavior.keys)
if (channelListTypeTag != null) verifyCompleteness()
private def verifyCompleteness() {
val channels = inputChannels(ru)(channelListTypeTag.tpe)
val classes = channels groupBy (e channelListTypeTag.mirror.runtimeClass(e.widen))
val missing = classes.keySet -- behavior.keySet
if (missing.nonEmpty) {
val m = missing.map(classes).flatten
throw ActorInitializationException(s"missing declarations for channels ${m mkString ", "}")
}
}
override def applyOrElse[A, B >: Unit](x: A, default: A B): B = x match {
case CheckType(tt)
narrowCheck(ru)(channelListTypeTag.tpe, tt.tpe) match {
case Nil sender ! CheckTypeACK
case err :: Nil sender ! CheckTypeNAK(err)
case list sender ! CheckTypeNAK(list mkString ("multiple errors:\n - ", " - ", ""))
}
case _
val msgClass = x.getClass
index find (_ isAssignableFrom msgClass) match {
case None default(x)
case Some(cls)
behavior(cls) match {
case F1(f) f(new WrappedMessage[ChannelList](x), new ChannelRef(sender))
case F2(f) f(x, new ChannelRef(sender))
}
}
}
def isDefinedAt(x: Any): Boolean = x match {
case c: CheckType[_] true
case _
val msgClass = x.getClass
index find (_ isAssignableFrom msgClass) match {
case None false
case Some(cls) true
}
}
}
}
object Channels {
}

View file

@ -0,0 +1,36 @@
package akka.channels
import language.experimental.{ macros makkros }
import akka.actor.ActorRef
import akka.util.Timeout
import akka.pattern.ask
import scala.concurrent.{ ExecutionContext, Future }
import scala.reflect.runtime.{ universe ru }
import scala.util.Success
sealed trait ChannelList
sealed trait TNil extends ChannelList
sealed trait :+:[A <: (_, _), B <: ChannelList] extends ChannelList
class ActorRefOps(val ref: ActorRef) extends AnyVal {
import macros.Helpers._
def narrow[C <: ChannelList](implicit timeout: Timeout, ec: ExecutionContext, tt: ru.TypeTag[C]): Future[ChannelRef[C]] = {
import Channels._
ref ? CheckType(tt) map {
case CheckTypeACK new ChannelRef[C](ref)
case CheckTypeNAK(error) throw NarrowingException(error)
}
}
}
class FutureOps[T](val future: Future[T]) extends AnyVal {
def -!->[C <: ChannelList](channel: ChannelRef[C]): Future[T] = macro macros.Tell.futureImpl[C, T]
def -?->[C <: ChannelList](channel: ChannelRef[C]): Future[_] = macro macros.Ask.futureImpl[C, T]
}
class AnyOps[T](val value: T) extends AnyVal {
def -!->[C <: ChannelList](channel: ChannelRef[C]): Unit = macro macros.Tell.opsImpl[C, T]
def -?->[C <: ChannelList](channel: ChannelRef[C]): Future[_] = macro macros.Ask.opsImpl[C, T]
}
class WrappedMessage[T <: ChannelList](val value: Any) extends AnyVal

View file

@ -0,0 +1,63 @@
package akka.channels.macros
import akka.channels._
import scala.concurrent.Future
import akka.util.Timeout
import scala.reflect.runtime.{ universe ru }
import ru.TypeTag
import scala.reflect.macros.Context
import scala.reflect.api.Universe
import akka.actor.ActorRef
import akka.dispatch.ExecutionContexts
object Ask {
import Helpers._
def impl[T <: ChannelList: c.WeakTypeTag, M: c.WeakTypeTag](c: Context {
type PrefixType = ChannelRef[T]
})(msg: c.Expr[M]): c.Expr[Future[_]] = {
import c.universe._
askTree(c)(weakTypeOf[M], weakTypeOf[T])(reify(c.prefix.splice.actorRef), msg)
}
def opsImpl[T <: ChannelList: c.WeakTypeTag, M: c.WeakTypeTag](c: Context {
type PrefixType = AnyOps[M]
})(channel: c.Expr[ChannelRef[T]]): c.Expr[Future[_]] = {
import c.universe._
askTree(c)(weakTypeOf[M], weakTypeOf[T])(reify(channel.splice.actorRef), reify(c.prefix.splice.value))
}
def futureImpl[T <: ChannelList: c.WeakTypeTag, M: c.WeakTypeTag](c: Context {
type PrefixType = FutureOps[M]
})(channel: c.Expr[ChannelRef[T]]): c.Expr[Future[_]] = {
import c.universe._
val tree = askTree(c)(weakTypeOf[M], weakTypeOf[T])(c.Expr(Ident("c$1")), c.Expr(Ident("x$1")))
reify({
val c$1 = channel.splice.actorRef
c.prefix.splice.future.flatMap(x$1 tree.splice)(ExecutionContexts.sameThreadExecutionContext)
})
}
def askTree[M](c: Context with Singleton)(msgT: c.universe.Type, chT: c.universe.Type)(target: c.Expr[ActorRef], msg: c.Expr[M]): c.Expr[Future[_]] = {
import c.universe._
val out = replyChannels(c.universe)(chT, msgT)
if (out.isEmpty) {
c.error(c.enclosingPosition, s"This ChannelRef does not support messages of type $msgT")
reify(null)
} else {
val timeout = c.inferImplicitValue(typeOf[Timeout])
if (timeout.isEmpty)
c.error(c.enclosingPosition, s"no implicit akka.util.Timeout found")
val result = appliedType(weakTypeOf[Future[_]].typeConstructor, List(lub(out)))
c.Expr(
TypeApply(
Select(
reify(akka.pattern.ask(
target.splice, msg.splice)(
c.Expr(timeout)(weakTypeTag[Timeout]).splice)).tree,
"asInstanceOf"),
List(TypeTree().setType(result))))(weakTypeTag[Future[_]])
}
}
}

View file

@ -0,0 +1,77 @@
package akka.channels.macros
import akka.channels._
import scala.reflect.runtime.{ universe ru }
import ru.TypeTag
import scala.reflect.macros.Context
import scala.reflect.api.Universe
object Channel {
import Helpers._
/**
* This macro transforms a channel[] call which returns some Behaviorist
* into a _channel[] call with precise reply channel descriptors, so that the
* partial function it is applied to can enjoy proper type checking.
*
* T is the message type
* C is the channel list of the enclosing Channels
* P is the parent channel list
*/
def impl[T: c.WeakTypeTag, C <: ChannelList: c.WeakTypeTag, P <: ChannelList: c.WeakTypeTag](
c: Context {
type PrefixType = Channels[P, C]
}): c.Expr[Channels[P, C]#Behaviorist[Nothing, T]] = {
val tT = c.weakTypeOf[T]
val tC = c.weakTypeOf[C]
import c.universe._
val undefined = missingChannels(c.universe)(tC, inputChannels(c.universe)(tT))
if (undefined.nonEmpty) {
c.error(c.enclosingPosition, s"no channel defined for types ${undefined mkString ", "}")
reify(null)
} else {
checkUnique(c.universe)(tT, tC) foreach (c.error(c.enclosingPosition, _))
val channels = toChannels(c.universe)(replyChannels(c.universe)(tC, tT))
val (receive, wrapped) =
if (tT <:< typeOf[ChannelList]) {
appliedType(typeOf[Function2[_, _, _]].typeConstructor, List(
appliedType(typeOf[WrappedMessage[_]].typeConstructor, List(tT)),
appliedType(typeOf[ChannelRef[_]].typeConstructor, List(channels)),
typeOf[Unit])) -> true
} else {
appliedType(typeOf[Function2[_, _, _]].typeConstructor, List(
tT,
appliedType(typeOf[ChannelRef[_]].typeConstructor, List(channels)),
typeOf[Unit])) -> false
}
c.Expr(
Block(List(
If(
{
val cltt = c.Expr(Select(c.prefix.tree, "channelListTypeTag"))
reify(cltt.splice == null).tree
},
Apply(
Select(c.prefix.tree, "channelListTypeTag_$eq"),
List(TypeApply(
Select(Select(Select(Select(Select(Ident("scala"), "reflect"), "runtime"), nme.PACKAGE), "universe"), "typeTag"),
List(TypeTree().setType(c.weakTypeOf[C]))))),
c.literalUnit.tree)),
Apply(
Select(
New(AppliedTypeTree(Select(c.prefix.tree, newTypeName("Behaviorist")), List(
TypeTree().setType(receive),
TypeTree().setType(tT)))),
nme.CONSTRUCTOR),
List(
TypeApply(
Select(Select(Select(Select(Select(Ident("scala"), "reflect"), "runtime"), nme.PACKAGE), "universe"), "typeTag"),
List(TypeTree().setType(tT))),
Literal(Constant(wrapped))))))
}
}
}

View file

@ -0,0 +1,35 @@
package akka.channels.macros
import akka.channels._
import scala.reflect.runtime.{ universe ru }
import ru.TypeTag
import scala.reflect.macros.Context
import scala.reflect.api.Universe
import akka.actor.Props
object CreateChild {
import Helpers._
def impl[C <: ChannelList: c.WeakTypeTag, Pa <: ChannelList: c.WeakTypeTag, Ch <: ChannelList: c.WeakTypeTag](
c: Context {
type PrefixType = Channels[_, C]
})(factory: c.Expr[Channels[Pa, Ch]]): c.Expr[ChannelRef[Ch]] = {
import c.universe._
if (weakTypeOf[Pa] =:= weakTypeOf[Nothing]) {
c.abort(c.enclosingPosition, "Parent argument must not be Nothing")
}
if (weakTypeOf[Ch] =:= weakTypeOf[Nothing]) {
c.abort(c.enclosingPosition, "channel list must not be Nothing")
}
val missing = missingChannels(c.universe)(weakTypeOf[C], inputChannels(c.universe)(weakTypeOf[Pa]))
if (missing.isEmpty) {
implicit val t = c.TypeTag[Ch](c.weakTypeOf[Ch])
reify(new ChannelRef[Ch](c.prefix.splice.context.actorOf(Props(factory.splice))))
} else {
c.error(c.enclosingPosition, s"This actor cannot support a child requiring channels ${missing mkString ", "}")
reify(???)
}
}
}

View file

@ -0,0 +1,119 @@
package akka.channels.macros
import akka.AkkaException
import scala.util.control.NoStackTrace
import akka.channels._
import scala.reflect.runtime.{ universe ru }
import ru.TypeTag
import scala.reflect.macros.Context
import scala.reflect.api.Universe
object Helpers {
type Recv[T, Ch <: ChannelList] = Function2[T, ChannelRef[Ch], Unit]
case class CheckType[T](tt: TypeTag[T])
case object CheckTypeACK
case class CheckTypeNAK(errors: String)
def error(c: Context, msg: String) = c.error(c.enclosingPosition, msg)
def abort(c: Context, msg: String) = c.abort(c.enclosingPosition, msg)
def checkUnique(u: Universe)(channel: u.Type, list: u.Type): Option[String] = {
val channels = inputChannels(u)(list) groupBy (_.erasure)
val dupes = channels.get(channel.erasure).getOrElse(Nil).filterNot(_ =:= channel)
if (dupes.isEmpty) None
else Some(s"erasure ${channel.erasure} overlaps with declared channels ${dupes mkString ", "}")
}
/**
* check that the original ChannelList is a subtype of the target ChannelList; return a list or error strings
*/
def narrowCheck(u: Universe)(orig: u.Type, target: u.Type): List[String] = {
var errors = List.empty[String]
for (in inputChannels(u)(target)) {
val replies = replyChannels(u)(orig, in)
if (replies.isEmpty) errors ::= s"original ChannelRef does not support input type $in"
else {
val targetReplies = replyChannels(u)(target, in)
val unsatisfied = replies filterNot (r targetReplies exists (r <:< _))
if (unsatisfied.nonEmpty) errors ::= s"reply types ${unsatisfied mkString ", "} not covered for channel $in"
val leftovers = targetReplies filterNot (t replies exists (_ <:< t))
if (leftovers.nonEmpty) errors ::= s"desired reply types ${leftovers mkString ", "} are superfluous for channel $in"
}
}
errors.reverse
}
/**
* get all input channels from a ChannelList or return the given type
*/
final def inputChannels(u: Universe)(list: u.Type): List[u.Type] = {
import u._
val imp = u.mkImporter(ru)
val cl = imp.importType(ru.typeOf[ChannelList])
val tnil = imp.importType(ru.typeOf[TNil])
def rec(l: u.Type, acc: List[u.Type]): List[u.Type] = l match {
case TypeRef(_, _, TypeRef(_, _, in :: _) :: tail :: Nil) rec(tail, if (acc contains in) acc else in :: acc)
case last if (last =:= tnil) acc.reverse else (last :: acc).reverse
}
if (list <:< cl) rec(list, Nil)
else List(list)
}
/**
* 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)
}
/**
* filter from the `required` list of types all which are subtypes of inputs of the ChannelList
*/
final def missingChannels(u: Universe)(channels: u.Type, required: List[u.Type]): List[u.Type] = {
import u._
// making the top-level method recursive blows up the compiler (when compiling the macro itself)
def rec(ch: Type, req: List[Type]): List[Type] = {
ch match {
case TypeRef(_, _, TypeRef(_, _, in :: _) :: tail :: Nil) rec(tail, req filterNot (_ <:< in))
case last req filterNot (_ <:< last)
}
}
rec(channels, required)
}
/**
* convert a list of types List(<T1>, <T2>, ...) into a ChannelList
* ( Channel[<T1>, Nothing] :=: Channel[<T2>, Nothing] :=: ... :=: TNil )
*/
final def toChannels(u: Universe)(list: List[u.Type]): u.Type = {
import u._
def rec(l: List[Type], acc: Type): Type = l match {
case head :: (tail: List[Type])
if (head =:= weakTypeOf[Nothing]) rec(tail, acc)
else
rec(tail,
appliedType(weakTypeOf[:+:[_, _]].typeConstructor, List(
appliedType(weakTypeOf[Tuple2[_, _]].typeConstructor, List(
head,
weakTypeOf[Nothing])),
acc)))
case _ acc
}
rec(list.reverse, weakTypeOf[TNil])
}
}

View file

@ -0,0 +1,24 @@
package akka.channels.macros
import akka.channels._
import scala.reflect.runtime.{ universe ru }
import ru.TypeTag
import scala.reflect.macros.Context
import scala.reflect.api.Universe
object Narrow {
import Helpers._
def impl[C <: ChannelList: c.WeakTypeTag, T <: ChannelList: c.WeakTypeTag](
c: Context {
type PrefixType = ChannelRef[T]
}): c.Expr[ChannelRef[C]] = {
import c.{ universe u }
narrowCheck(u)(u.weakTypeOf[T], u.weakTypeOf[C]) match {
case Nil // okay
case err :: Nil c.error(c.enclosingPosition, err)
case list c.error(c.enclosingPosition, list mkString ("multiple errors:\n - ", "\n - ", ""))
}
u.reify(c.prefix.splice.asInstanceOf[ChannelRef[C]])
}
}

View file

@ -0,0 +1,90 @@
package akka.channels.macros
import akka.channels._
import akka.actor._
import scala.reflect.runtime.{ universe ru }
import ru.TypeTag
import scala.reflect.macros.Context
import scala.reflect.api.Universe
import scala.annotation.tailrec
import scala.concurrent.Future
import scala.util.{ Failure, Success }
import akka.dispatch.ExecutionContexts
object Tell {
import Helpers._
def impl[T <: ChannelList: c.WeakTypeTag, M: c.WeakTypeTag](c: Context {
type PrefixType = ChannelRef[T]
})(msg: c.Expr[M]): c.Expr[Unit] = {
val tT = c.universe.weakTypeOf[T]
val (tS, senderTree, sender) = getSenderChannel(c)
verify(c)(senderTree, c.universe.weakTypeOf[M], tS, tT)
c.universe.reify(c.prefix.splice.actorRef.tell(msg.splice, sender.splice))
}
def opsImpl[T <: ChannelList: c.WeakTypeTag, M: c.WeakTypeTag](c: Context {
type PrefixType = AnyOps[M]
})(channel: c.Expr[ChannelRef[T]]): c.Expr[Unit] = {
val tT = c.universe.weakTypeOf[T]
val (tS, senderTree, sender) = getSenderChannel(c)
verify(c)(senderTree, c.universe.weakTypeOf[M], tS, tT)
c.universe.reify(channel.splice.actorRef.tell(c.prefix.splice.value, sender.splice))
}
def futureImpl[T <: ChannelList: c.WeakTypeTag, M: c.WeakTypeTag](c: Context {
type PrefixType = FutureOps[M]
})(channel: c.Expr[ChannelRef[T]]): c.Expr[Future[M]] = {
val tT = c.universe.weakTypeOf[T]
val (tS, senderTree, sender) = getSenderChannel(c)
verify(c)(senderTree, c.universe.weakTypeOf[M], tS, tT)
c.universe.reify(
{
val s$1 = sender.splice
c.prefix.splice.future.andThen {
case Success(s) channel.splice.actorRef.tell(s, s$1)
case _
}(ExecutionContexts.sameThreadExecutionContext)
})
}
def getSenderChannel(c: Context): (c.universe.Type, c.Tree, c.Expr[ActorRef]) = {
val replyChannel = c.inferImplicitValue(c.typeOf[ChannelRef[_]])
if (!replyChannel.isEmpty) {
import c.universe._
replyChannel.tpe match {
case TypeRef(_, _, param :: Nil)
(param, replyChannel, c.Expr(Select(replyChannel, "actorRef"))(c.universe.weakTypeTag[ActorRef]))
}
} else abort(c, "no implicit sender ChannelRef found")
}
def verify(c: Context)(sender: c.universe.Tree, msgT: c.universe.Type, sndT: c.universe.Type, chT: c.universe.Type)(): Unit = {
def rec(msg: Set[c.universe.Type], checked: Set[c.universe.Type], depth: Int): Unit =
if (msg.nonEmpty) {
val u: c.universe.type = c.universe
val replies = msg map (m m -> replyChannels(u)(chT, m))
val missing = replies collect { case (k, v) if v.size == 0 k }
if (missing.nonEmpty)
error(c, 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)(sndT, m))
val nextMissing = nextSend collect { case (k, v) if v.size == 0 k }
if (nextMissing.nonEmpty)
error(c, s"implicit sender `$sender` does not support messages of the reply types ${nextMissing mkString ", "} (at depth $depth)")
else {
val nextChecked = checked ++ msg
val nextMsg = nextSend.map(_._2).flatten -- nextChecked
rec(nextMsg, nextChecked, depth + 1)
}
}
}
rec(Set(msgT), Set(c.universe.typeOf[Nothing]), 1)
}
}

View file

@ -0,0 +1,15 @@
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package akka
import language.implicitConversions
import akka.actor.ActorRef
import scala.concurrent.Future
package object channels {
implicit def actorRefOps(ref: ActorRef) = new ActorRefOps(ref)
implicit def futureOps[T](f: Future[T]) = new FutureOps(f)
implicit def anyOps[T](x: T) = new AnyOps(x)
}