review comments

- some API docs
- require names for top-level actors
- allow names for children
- flag error when no channels declared
This commit is contained in:
Roland 2013-01-31 18:59:48 +01:00
parent c362e8168f
commit 86ded1fb0b
9 changed files with 109 additions and 60 deletions

View file

@ -17,9 +17,10 @@ object ChannelExt extends ExtensionKey[ChannelExtension]
class ChannelExtension(system: ExtendedActorSystem) extends Extension {
// kick-start the universe (needed due to thread safety issues in runtime mirror)
// FIXME: kick-start the universe (needed due to thread safety issues in runtime mirror)
// see https://issues.scala-lang.org/browse/SI-6240
private val t = typeTag[(Int, Int) :+: TNil]
def actorOf[Ch <: ChannelList](factory: Actor with Channels[TNil, Ch]): ChannelRef[Ch] =
new ChannelRef[Ch](system.actorOf(Props(factory)))
def actorOf[Ch <: ChannelList](factory: Actor with Channels[TNil, Ch], name: String): ChannelRef[Ch] =
new ChannelRef[Ch](system.actorOf(Props(factory), name))
}

View file

@ -6,27 +6,38 @@ 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
/**
* A channel reference, holding a type list of all channels supported by the
* underlying actor. This actors reference can be obtained as the `actorRef`
* member.
*/
class ChannelRef[+T <: ChannelList](val actorRef: ActorRef) extends AnyVal {
/**
* Send a message over this channel, tell semantics, returning the message.
*/
def <-!-[M](msg: M): M = macro macros.Tell.impl[T, M]
/**
* Eventually send the value contained in the future over this channel,
* tell semantics, returning a Future which is completed after sending
* with the value which was sent (Future.andThen semantics).
*/
def <-!-[M](future: Future[M]): Future[M] = macro macros.Tell.futureImpl[T, M]
/**
* Send a message over this channel, ask semantics, returning a Future
* which will be completed with the reply message or a TimeoutException.
* If the message is a Future itself, eventually send the Futures value.
*/
def <-?-[M](msg: M): Future[_] = macro macros.Ask.impl[ChannelList, Any, T, M]
/**
* Narrow this ChannelRef by removing channels or narrowing input types or
* widening output types.
*/
def narrow[C <: ChannelList]: ChannelRef[C] = macro macros.Narrow.impl[C, T]
}

View file

@ -51,6 +51,13 @@ trait Channels[P <: ChannelList, C <: ChannelList] { this: Actor ⇒
*/
def createChild[Pa <: ChannelList, Ch <: ChannelList](factory: Actor with Channels[Pa, Ch]): ChannelRef[Ch] = macro macros.CreateChild.impl[C, Pa, Ch]
/**
* Create a child actor with properly typed ChannelRef and the given name,
* verifying that this actor can handle everything which the child tries to
* send via its `parent` ChannelRef.
*/
def createChild[Pa <: ChannelList, Ch <: ChannelList](factory: Actor with Channels[Pa, Ch], name: String): ChannelRef[Ch] = macro macros.CreateChild.implName[C, Pa, Ch]
/**
* Properly typed ChannelRef for the context.parent.
*/
@ -131,6 +138,8 @@ trait Channels[P <: ChannelList, C <: ChannelList] { this: Actor ⇒
private def verifyCompleteness() {
val channels = inputChannels(ru)(channelListTypeTag.tpe)
if (channels.isEmpty)
throw ActorInitializationException("Actor with Channels cannot have no channels")
val classes = channels groupBy (e channelListTypeTag.mirror.runtimeClass(e.widen))
val missing = classes.keySet -- behavior.keySet
if (missing.nonEmpty) {
@ -162,10 +171,7 @@ trait Channels[P <: ChannelList, C <: ChannelList] { this: Actor ⇒
case c: CheckType[_] true
case _
val msgClass = x.getClass
index find (_ isAssignableFrom msgClass) match {
case None false
case Some(cls) true
}
index exists (_ isAssignableFrom msgClass)
}
}
}

View file

@ -12,6 +12,7 @@ import scala.concurrent.{ ExecutionContext, Future }
import scala.reflect.runtime.{ universe ru }
import scala.util.Success
import akka.dispatch.ExecutionContexts
import scala.util.control.NoStackTrace
sealed trait ChannelList
sealed trait TNil extends ChannelList
@ -26,8 +27,23 @@ sealed trait ReplyChannels[T <: ChannelList] extends ChannelList
*/
sealed trait UnknownDoNotWriteMeDown
/**
* This exception is used to signal errors when trying to `.narrow` an
* ActorRef into a ChannelRef: if the actor finds the requested channel types
* incompatible with its selfChannel, it will return errors in the same format
* as would occur during compilation of a `ChannelRef.narrow` operation.
*/
case class NarrowingException(message: String) extends akka.AkkaException(message) with NoStackTrace
class ActorRefOps(val ref: ActorRef) extends AnyVal {
import macros.Helpers._
/**
* Send a query to the actor and check whether it supports the requested
* channel types; the normal timeout semantics of the `ask` pattern apply.
* The Future will be completed either with the desired ChannelRef or with
* an exception (TimeoutException or NarrowingException).
*/
def narrow[C <: ChannelList](implicit timeout: Timeout, ec: ExecutionContext, tt: ru.TypeTag[C]): Future[ChannelRef[C]] = {
import Channels._
ref ? CheckType(tt) map {

View file

@ -22,20 +22,35 @@ object CreateChild {
import c.universe._
if (weakTypeOf[ParentChannels] =:= weakTypeOf[Nothing]) {
c.abort(c.enclosingPosition, "Parent argument must not be Nothing")
}
if (weakTypeOf[ChildChannels] =:= weakTypeOf[Nothing]) {
c.abort(c.enclosingPosition, "channel list must not be Nothing")
}
verify(c)(weakTypeOf[ParentChannels], weakTypeOf[ChildChannels], weakTypeOf[MyChannels])
val missing = missingChannels(c.universe)(weakTypeOf[MyChannels], inputChannels(c.universe)(weakTypeOf[ParentChannels]))
if (missing.isEmpty) {
implicit val t = c.TypeTag[ChildChannels](c.weakTypeOf[ChildChannels])
reify(new ChannelRef[ChildChannels](c.prefix.splice.context.actorOf(Props(factory.splice))))
} else {
c.abort(c.enclosingPosition, s"This actor cannot support a child requiring channels ${missing mkString ", "}")
}
implicit val t = c.TypeTag[ChildChannels](c.weakTypeOf[ChildChannels])
reify(new ChannelRef[ChildChannels](c.prefix.splice.context.actorOf(Props(factory.splice))))
}
def implName[MyChannels <: ChannelList: c.WeakTypeTag, ParentChannels <: ChannelList: c.WeakTypeTag, ChildChannels <: ChannelList: c.WeakTypeTag](
c: Context {
type PrefixType = Actor with Channels[_, MyChannels]
})(factory: c.Expr[Actor with Channels[ParentChannels, ChildChannels]], name: c.Expr[String]): c.Expr[ChannelRef[ChildChannels]] = {
import c.universe._
verify(c)(weakTypeOf[ParentChannels], weakTypeOf[ChildChannels], weakTypeOf[MyChannels])
implicit val t = c.TypeTag[ChildChannels](c.weakTypeOf[ChildChannels])
reify(new ChannelRef[ChildChannels](c.prefix.splice.context.actorOf(Props(factory.splice), name.splice)))
}
def verify(c: Context)(parent: c.Type, child: c.Type, mine: c.Type): Unit = {
import c.universe._
val nothing = weakTypeOf[Nothing]
if (parent =:= nothing) abort(c, "Parent argument must not be Nothing")
if (child =:= nothing) abort(c, "channel list must not be Nothing")
val missing = missingChannels(c.universe)(mine, inputChannels(c.universe)(parent))
if (missing.nonEmpty)
abort(c, s"This actor cannot support a child requiring channels ${missing mkString ", "}")
}
}