diff --git a/akka-actor-tests/src/test/scala/akka/actor/actor/ChannelSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/actor/ChannelSpec.scala new file mode 100644 index 0000000000..61c898f8be --- /dev/null +++ b/akka-actor-tests/src/test/scala/akka/actor/actor/ChannelSpec.scala @@ -0,0 +1,20 @@ +/** + * Copyright (C) 2009-2011 Scalable Solutions AB + */ + +package akka.actor + +import org.scalatest.WordSpec +import org.scalatest.matchers.MustMatchers +import akka.dispatch._ + +class ChannelSpec extends WordSpec with MustMatchers { + "A Channel" must { + "be contravariant" in { + val ap = new ActorPromise(1000) + val p: Promise[Any] = ap + val c: Channel[Any] = ap + val cs: Channel[String] = c + } + } +} diff --git a/akka-actor/src/main/scala/akka/actor/Channel.scala b/akka-actor/src/main/scala/akka/actor/Channel.scala index 80134f2df8..2896883f2b 100644 --- a/akka-actor/src/main/scala/akka/actor/Channel.scala +++ b/akka-actor/src/main/scala/akka/actor/Channel.scala @@ -8,14 +8,11 @@ package akka.actor * Abstraction for unification of sender and senderFuture for later reply. * Can be stored away and used at a later point in time. * - * Channel cannot be contravariant because of Future providing its value in - * covariant position. - * * The possible reply channel which can be passed into ! and safe_! is always * untyped, as there is no way to utilize its real static type without * requiring runtime-costly manifests. */ -trait Channel[T] { +trait Channel[-T] { /** * Scala API.

@@ -105,7 +102,7 @@ trait Channel[T] { * i.e. ! is not guaranteed to fail (e.g. NullChannel would be a * counter-example). */ -trait AvailableChannel[T] { self: Channel[T] ⇒ +trait AvailableChannel[-T] { self: Channel[T] ⇒ def safe_!(msg: T)(implicit channel: UntypedChannel = NullChannel): Boolean = { if (isUsable) { try { diff --git a/akka-docs/scala/actors.rst b/akka-docs/scala/actors.rst index 2d0f105e0e..3019272e7f 100644 --- a/akka-docs/scala/actors.rst +++ b/akka-docs/scala/actors.rst @@ -266,7 +266,7 @@ Reply using the channel ^^^^^^^^^^^^^^^^^^^^^^^ If you want to have a handle to an object to whom you can reply to the message, you can use the ``Channel`` abstraction. -Simply call ``self.channel`` and then you can forward that to others, store it away or otherwise until you want to reply, which you do by ``Channel ! response``: +Simply call ``self.channel`` and then you can forward that to others, store it away or otherwise until you want to reply, which you do by ``channel ! response``: .. code-block:: scala @@ -274,6 +274,17 @@ Simply call ``self.channel`` and then you can forward that to others, store it a val result = process(request) self.channel ! result +The :class:`Channel` trait is contravariant in the expected message type. Since +``self.channel`` is subtype of ``Channel[Any]``, you may specialise your return +channel to allow the compiler to check your replies:: + + class MyActor extends Actor { + def doIt(channel: Channel[String], x: Any) = { channel ! x.toString } + def receive = { + case x => doIt(self.channel, x) + } + } + .. code-block:: scala case request =>