Yang Bo,
thanks for experimenting with and sharing this: when macros started to
materialize two years ago I had the same thought and proposed (internally) to
investigate what I called messageflow at the time (inspired by our dataflow
module which has been treated in the same fashion, going from CPS plugin to
async/await macros). This model makes for a nice and direct way of formulating
temporary intermediate states within actors, typically encountered while
waiting for another actor to reply. Interestingly, the original work on the
Actor model (in particular by Gul Agha) already contains this as a primitive of
the actor language.
On the other hand I have learned a few more things over the past year that are
related to this topic as well. Perhaps the most concise write-up is this issue
on the Reactive Streams repository. What you are doing is that the actor is not
technically blocked (in the sense of parking a thread), but it is still
logically blocked, since it cannot handle any message other than the reply that
is being awaited—everything else would violate the Actor model in that
concurrent entry into the behavior is not allowed: handling a message must
determine the behavior that applies to the next message, and this process is
applied strictly in sequence. We discussed this topic also with Akara
Sucharitakul and Justin du Coeur on this list a few months ago (search for the
Aggregator Pattern), and what we concluded was that mixing a normal actor (with
the current behavior as the message entry point) with an `await` scheme (that
adds secondary message entry points) would lead to confusing behavior in that
e.g. a Cancel message would not have the intended effect:
def receive = {
case DoWork =>
val r = otherActor ? Query await 5.seconds // this uses become() under
the hood
sender() ! Reply(transform(r))
case Cancel =>
context.stop(self)
}
Now what you are showing is a slight variation on this theme in that your actor
does not actually have the normal `receive` entry point for processing. If I
read it correctly, then your macro only applies to actors that model ephemeral
workflows in a single-shot fashion, with full control on which message is
expected next. That is a very interesting spin, and it might actually work! I
would love to see how something like the following would pan out:
class OrderFlow(item: Item, client: Client, inventory: ActorRef, shipping:
ActorRef, billing: ActorRef) extends Workflow {
implicit val timeout = Timeout(300.millis)
flow {
// .await(duration) returns an Option[T]
val reserved = inventory ? ReserveItem(item) await 500.millis getOrElse
ReservationTimeout match {
case ItemReserved(reserved) => reserved
case other =>
context.parent ! OrderFailed(other)
flow.stop() // e.g. throw ActorKilledException
}
val shipped = shipping ? ShipItem(reserved, client) await 500.millis
getOrElse ShippingTimeout
val status = if (shipped.wasSuccessful) {
val billed = billing ? InvoiceItem(reserved, client) await 500.millis
getOrElse InvoiceTimeout
if (!billed.wasSuccessful) {
shipping ! RollbackShipItem(shipped)
BillingFailed
} else {
OrderSuccess
}
} else {
ShippingFailed
}
context.parent ! status
}
}
This would be a specialized actor with a `final override def receive` that can
only be used via the flow{} DSL. The core of this proposal is that an actor is
either unrestricted (with receive/become) or a single workflow, but never a mix
of the two.
Regards,
Roland
27 apr 2014 kl. 22:06 skrev 杨博 <[email protected]>:
>
>
> 在 2014年4月27日星期日UTC+8上午2时15分31秒,Justin du coeur写道:
> On Fri, Apr 25, 2014 at 4:00 PM, 杨博 <[email protected]> wrote:
> Does this project interest you or scare you?
>
> Somewhere in-between, I'd say -- I'm intrigued but cautious. The concept is
> *quite* interesting, and for some state machines I can imagine it being
> enormously useful. That said, while it's obvious how it works for a single
> entry point, I'm not clear on how it would work with one of my typical
> Actors, which often respond quite differently to half a dozen different
> messages. If this is so deeply become-based, it *sounds* like it might have
> problems when those are interleaved.
> I guess I won't resolve this problem at any time. Yes, I can resolve it by
> dispatching different message to different Futures in one actor. But IMHO,
> it's not the best design model with Akka. I suggest you forward different
> messages to different actors, and use Future in those actors.
>
>
> But the generated code is so voluminous, I can't quickly figure out if
> that's true. (The bulk of the generated code is a mild concern as well,
> although not a dramatic one if the size of the generated code is roughly
> linear with the original.)
>
> How well does the Future mechanism play with more heterogeneous Actors, with
> a bunch of different entry points?
>
> --
> >>>>>>>>>> Read the docs: http://akka.io/docs/
> >>>>>>>>>> Check the FAQ:
> >>>>>>>>>> http://doc.akka.io/docs/akka/current/additional/faq.html
> >>>>>>>>>> Search the archives: https://groups.google.com/group/akka-user
> ---
> You received this message because you are subscribed to the Google Groups
> "Akka User List" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> To post to this group, send email to [email protected].
> Visit this group at http://groups.google.com/group/akka-user.
> For more options, visit https://groups.google.com/d/optout.
Dr. Roland Kuhn
Akka Tech Lead
Typesafe – Reactive apps on the JVM.
twitter: @rolandkuhn
--
>>>>>>>>>> Read the docs: http://akka.io/docs/
>>>>>>>>>> Check the FAQ:
>>>>>>>>>> http://doc.akka.io/docs/akka/current/additional/faq.html
>>>>>>>>>> Search the archives: https://groups.google.com/group/akka-user
---
You received this message because you are subscribed to the Google Groups "Akka
User List" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/akka-user.
For more options, visit https://groups.google.com/d/optout.