some small fixes to typed-channels.rst
This commit is contained in:
parent
8a259897f8
commit
4861ae4269
1 changed files with 73 additions and 34 deletions
|
|
@ -88,7 +88,9 @@ Terminology
|
||||||
.. describe:: trait Channels[P <: ChannelList, C <: ChannelList]
|
.. describe:: trait Channels[P <: ChannelList, C <: ChannelList]
|
||||||
|
|
||||||
A mixin for the :class:`Actor` trait which is parameterized in the channel
|
A mixin for the :class:`Actor` trait which is parameterized in the channel
|
||||||
requirements this actor has for its parent (P) and its selfChannel (C).
|
requirements this actor has for its parentChannel (P) and its selfChannel (C)
|
||||||
|
(corresponding to ``context.parent`` and ``self`` for untyped Actors,
|
||||||
|
respectively).
|
||||||
|
|
||||||
.. describe:: selfChannel
|
.. describe:: selfChannel
|
||||||
|
|
||||||
|
|
@ -96,6 +98,11 @@ Terminology
|
||||||
``ChannelRef[C]``. This is the same type of channel reference which is
|
``ChannelRef[C]``. This is the same type of channel reference which is
|
||||||
obtained by creating an instance of this actor.
|
obtained by creating an instance of this actor.
|
||||||
|
|
||||||
|
.. describe:: parentChannel
|
||||||
|
|
||||||
|
An ``Actor with Channels[P, C]`` has a ``parentChannel`` of type
|
||||||
|
``ChannelRef[P]``.
|
||||||
|
|
||||||
.. describe:: type ReplyChannels[T <: ChannelList] <: ChannelList
|
.. describe:: type ReplyChannels[T <: ChannelList] <: ChannelList
|
||||||
|
|
||||||
Within an ``Actor with Channels[_, _]`` which takes a fully generic channel,
|
Within an ``Actor with Channels[_, _]`` which takes a fully generic channel,
|
||||||
|
|
@ -104,7 +111,7 @@ Terminology
|
||||||
channel uses the ReplyChannels type to abstractly refer to this unknown set
|
channel uses the ReplyChannels type to abstractly refer to this unknown set
|
||||||
of channels in order to forward a reply from a ``ChannelRef[T]`` back to the
|
of channels in order to forward a reply from a ``ChannelRef[T]`` back to the
|
||||||
original sender. This operation’s type-safety is ensured at the sender’s site
|
original sender. This operation’s type-safety is ensured at the sender’s site
|
||||||
by way of the ping-pong analysis described above.
|
by way of the ping-pong analysis described below.
|
||||||
|
|
||||||
.. describe:: class WrappedMessage[T <: ChannelList, LUB]
|
.. describe:: class WrappedMessage[T <: ChannelList, LUB]
|
||||||
|
|
||||||
|
|
@ -128,12 +135,15 @@ with the target channel’s reply types. In this case we want to demonstrate jus
|
||||||
the syntax of sending, hence the dummy sender which accepts everything and
|
the syntax of sending, hence the dummy sender which accepts everything and
|
||||||
replies never.
|
replies never.
|
||||||
|
|
||||||
Presupposing three channel references of chainable types, an input value ``a``
|
Presupposing three channel references of chainable types (and a fourth one for
|
||||||
and a Future holding such a value, we demonstrate the two basic operations
|
demonstrating multiple reply type), an input value ``a`` and a Future holding
|
||||||
which are well known from untyped actors: tell/! and ask/?. The type of the
|
such a value, we demonstrate the two basic operations which are well known from
|
||||||
Future returned by the ask operation may seem surprising at first, but as the
|
untyped actors: tell/! and ask/?. The type of the Future returned by the ask
|
||||||
last line demonstrates, it is built in a way which makes building actor chains
|
operation on ``channelA2`` may seem surprising at first, but keeping track of
|
||||||
very simple. What the last line does is the following:
|
all possible reply types is necessary to enable sending of replies to other
|
||||||
|
actors which do support all possibilities. This is especially handy in
|
||||||
|
situations like the one demonstrated on the last line. What the last line does
|
||||||
|
is the following:
|
||||||
|
|
||||||
* it asks channelA, which returns a Future
|
* it asks channelA, which returns a Future
|
||||||
|
|
||||||
|
|
@ -209,7 +219,7 @@ executing the replying message send, etc. And as always, the ``snd`` reference
|
||||||
may be used more than once, and even stored away for later. It must not leave
|
may be used more than once, and even stored away for later. It must not leave
|
||||||
the actor within it was created, however, because that would defeat the
|
the actor within it was created, however, because that would defeat the
|
||||||
ping-pong check; this is the reason for the curious name of the fabricated
|
ping-pong check; this is the reason for the curious name of the fabricated
|
||||||
reply type ``UnknownDoNotWriteMeDown``, if you find yourself declaring that
|
reply type ``UnknownDoNotWriteMeDown``; if you find yourself declaring that
|
||||||
type as part of a message or similar you know that you are cheating.
|
type as part of a message or similar you know that you are cheating.
|
||||||
|
|
||||||
Declaration of Subchannels
|
Declaration of Subchannels
|
||||||
|
|
@ -222,7 +232,7 @@ It can be convenient to carve out subchannels for special treatment like so:
|
||||||
This means that all ``Command`` requests will be positively answered while all
|
This means that all ``Command`` requests will be positively answered while all
|
||||||
others may or may not be lucky. This dispatching between the two declarations
|
others may or may not be lucky. This dispatching between the two declarations
|
||||||
does not depend on their order but is solely done based on which type is more
|
does not depend on their order but is solely done based on which type is more
|
||||||
specific.
|
specific—but see the restrictions imposed by JVM type erasure below.
|
||||||
|
|
||||||
Forwarding Messages
|
Forwarding Messages
|
||||||
-------------------
|
-------------------
|
||||||
|
|
@ -253,8 +263,8 @@ implementing its control channel:
|
||||||
|
|
||||||
This shows all elements of the toolkit in action: calling ``channel[T1]`` again
|
This shows all elements of the toolkit in action: calling ``channel[T1]`` again
|
||||||
during the lifetime of the actor will alter its behavior on that channel. In
|
during the lifetime of the actor will alter its behavior on that channel. In
|
||||||
this case a latch or gate is modeled which when closed will permit the message
|
this case a latch or gate is modeled which when closed will permit the messages
|
||||||
flow through and when not will drop the messages to the floor.
|
to flow through and when not will drop the messages to the floor.
|
||||||
|
|
||||||
Creating Actors with Channels
|
Creating Actors with Channels
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
@ -277,15 +287,40 @@ such a child. The parent’s job then is to create the child, make it available
|
||||||
to the outside via properly typed messages and collect the statistics coming in
|
to the outside via properly typed messages and collect the statistics coming in
|
||||||
from the child.
|
from the child.
|
||||||
|
|
||||||
|
Stepping Outside of Type-Safety
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
In much the same was as Scala’s type system can be circumvented by using
|
||||||
|
``.asInstanceOf[_]`` typed channels can also be circumvented. Casting them to
|
||||||
|
alter the type arguments would be an obvious way of doing that, but there are
|
||||||
|
less obvious ways which are therefore enumerated here:
|
||||||
|
|
||||||
|
* explicitly constructing :class:`ChannelRef` instances by hand allows using
|
||||||
|
arbitrary types as arguments
|
||||||
|
|
||||||
|
* sending to the ``actorRef`` member of the :class:`ChannelRef`; this is a
|
||||||
|
normal untyped actor reference without any compile-time checks, which is the
|
||||||
|
reason for choosing visibly different operator names for typed and untyped
|
||||||
|
message send operations
|
||||||
|
|
||||||
|
* using the ``context.parent`` reference instead of ``parentChannel``
|
||||||
|
|
||||||
|
* using the untyped ``sender`` reference instead of the second argument to a
|
||||||
|
channel’s behavior function
|
||||||
|
|
||||||
|
Sending unforeseen messages will be flagged as a type error as long as none of
|
||||||
|
these techniques are used within an application.
|
||||||
|
|
||||||
Implementation Restrictions
|
Implementation Restrictions
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
The erasure-based dispatch of incoming messages requires all channels which are
|
As described below, incoming messages are dispatched to declared channels based
|
||||||
declared to have unique JVM type representations, i.e. it is not possible to
|
on their runtime class information. This erasure-based dispatch of messages
|
||||||
have two channel declarations with types ``List[A]`` and ``List[B]`` because
|
requires all declared channels to have unique JVM type representations, i.e. it
|
||||||
both would at runtime only be known as ``List[_]``.
|
is not possible to have two channel declarations with types ``List[A]`` and
|
||||||
|
``List[B]`` because both would at runtime only be known as ``List[_]``.
|
||||||
|
|
||||||
The specific dispatch mechanism also require the declaration of all channels or
|
The specific dispatch mechanism also requires the declaration of all channels or
|
||||||
subchannels during the actor’s construction, independent of whether they shall
|
subchannels during the actor’s construction, independent of whether they shall
|
||||||
later change behavior or not. Changing behavior for a subchannel is only
|
later change behavior or not. Changing behavior for a subchannel is only
|
||||||
possible if that subchannel was declared up-front.
|
possible if that subchannel was declared up-front.
|
||||||
|
|
@ -314,15 +349,18 @@ especially easy to see that those two channels will probably not be related,
|
||||||
their types will not be derived from a meaningful common supertype; instead the
|
their types will not be derived from a meaningful common supertype; instead the
|
||||||
least upper bound will probably be :class:`AnyRef`. If a typed channel
|
least upper bound will probably be :class:`AnyRef`. If a typed channel
|
||||||
reference only had the capability to express a single type, this type would
|
reference only had the capability to express a single type, this type would
|
||||||
then be no restriction anymore.
|
then be no restriction anymore. This loss of type safety caused by the need of
|
||||||
|
handling multiple disjoint sets of types is called “type pollution”, the term
|
||||||
|
was coined by Prof. Philip Wadler.
|
||||||
|
|
||||||
One solution to this is to never expose references describe more than one
|
One solution to this is to never expose references describing more than one
|
||||||
channel at a time. But where would these references come from? It would be very
|
channel at a time. But where would these references come from? It would be very
|
||||||
difficult to make this construction process type-safe, and it would also be an
|
difficult to make this construction process type-safe, and it would also be an
|
||||||
inconvenient restriction, since message ordering guarantees only apply for the
|
inconvenient restriction, since message ordering guarantees only apply for the
|
||||||
same sender–receive pair, and if there are relations between the messages sent
|
same sender–receive pair: if there are relations between the messages sent
|
||||||
on multiple channels those would need more boilerplate code to realize than if
|
on multiple channels then implementing this mixed-channel communication would
|
||||||
all interaction were possible through a single reference.
|
incur programmatic and runtime overhead compared to just sending to the same
|
||||||
|
untyped reference.
|
||||||
|
|
||||||
The other solution thus is to express multiple channel types by a single
|
The other solution thus is to express multiple channel types by a single
|
||||||
channel reference, which requires the implementation of type lists and
|
channel reference, which requires the implementation of type lists and
|
||||||
|
|
@ -334,8 +372,8 @@ map. The implementation chosen uses type lists like this:
|
||||||
|
|
||||||
This type expresses two channels: type ``A`` may stimulate replies of type
|
This type expresses two channels: type ``A`` may stimulate replies of type
|
||||||
``B``, while type ``C`` may evoke replies of type ``D``. The type operator
|
``B``, while type ``C`` may evoke replies of type ``D``. The type operator
|
||||||
``:+:`` is a binary type which form a list of these channel definitions, and
|
``:+:`` is a binary type constructor which forms a list of these channel
|
||||||
like every good list it ends with an empty terminator ``TNil``.
|
definitions, and like every good list it ends with an empty tail ``TNil``.
|
||||||
|
|
||||||
The Reply Problem
|
The Reply Problem
|
||||||
-----------------
|
-----------------
|
||||||
|
|
@ -393,12 +431,13 @@ The Parent Problem
|
||||||
There is one other actor reference which is available to every actor: its
|
There is one other actor reference which is available to every actor: its
|
||||||
parent. Since the child–parent relationship is established permanently when the
|
parent. Since the child–parent relationship is established permanently when the
|
||||||
child is created by the parent, this problem is easily solvable by encoding the
|
child is created by the parent, this problem is easily solvable by encoding the
|
||||||
requirements of the child for its parent channel in its type signature having
|
requirements of the child for its parent channel in its type signature and
|
||||||
the typed variant of ``actorOf`` verify this against the ``selfChannel``.
|
having the typed variant of ``actorOf`` verify this against the
|
||||||
|
``selfChannel``.
|
||||||
|
|
||||||
Anecdotally, since the guardian actor does not care at all about message sent
|
Anecdotally, since the guardian actor does not care at all about messages sent
|
||||||
to it, top-level type channel actors must declare their parent channel to be
|
to it, top-level actors with typed channels must declare their parent channel
|
||||||
empty.
|
to be empty.
|
||||||
|
|
||||||
The Exposure/Restriction Problem
|
The Exposure/Restriction Problem
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
@ -423,9 +462,9 @@ be possible but its implementation would be forbiddingly complex.
|
||||||
Therefore this topic gained traction as macros became available: being able to
|
Therefore this topic gained traction as macros became available: being able to
|
||||||
write down type calculations using standard collections and their
|
write down type calculations using standard collections and their
|
||||||
transformations reduces the implementation to a handful of lines. The “narrow”
|
transformations reduces the implementation to a handful of lines. The “narrow”
|
||||||
operation implemented this way allows all narrowing of input channels and
|
operation implemented this way allows narrowing of input channels and
|
||||||
widening of output channels down to ``(Nothing, Any)`` (which is to say:
|
widening of output channels down to ``(Nothing, Any)`` (which is to say that
|
||||||
removal).
|
channels may be narrowed or just plain removed from a channel list).
|
||||||
|
|
||||||
The Forwarding Problem
|
The Forwarding Problem
|
||||||
----------------------
|
----------------------
|
||||||
|
|
@ -446,7 +485,7 @@ final recipient would reply to the forwarded request, that sender reference
|
||||||
would belong to a different channel and there is no single location in the
|
would belong to a different channel and there is no single location in the
|
||||||
source code where all these pieces are known at compile time.
|
source code where all these pieces are known at compile time.
|
||||||
|
|
||||||
The solution to this problem is not to allow forwarding in the normal untyped
|
The solution to this problem is to not allow forwarding in the normal untyped
|
||||||
:class:`ActorRef` sense. Replies must always be sent by the recipient of the
|
:class:`ActorRef` sense. Replies must always be sent by the recipient of the
|
||||||
original message in order for the type checks at the sender site to be
|
original message in order for the type checks at the sender site to be
|
||||||
effective. Since forwarding is an important communication pattern among actors,
|
effective. Since forwarding is an important communication pattern among actors,
|
||||||
|
|
@ -464,7 +503,7 @@ contained in the message, which due to the erasure of generic type information
|
||||||
is an incomplete image of the true channel types. Those full types exist only
|
is an incomplete image of the true channel types. Those full types exist only
|
||||||
at compile-time and reifying them into TypeTags at runtime for every message
|
at compile-time and reifying them into TypeTags at runtime for every message
|
||||||
send would be prohibitively expensive. This means that channels which erase to
|
send would be prohibitively expensive. This means that channels which erase to
|
||||||
the same JVM type cannot coexist within the same actor, message would not be
|
the same JVM type cannot coexist within the same actor, messages would not be
|
||||||
routable reliably in that case.
|
routable reliably in that case.
|
||||||
|
|
||||||
The Actor Lookup Problem
|
The Actor Lookup Problem
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue