Hiding Messages In API Functions

73 views
Skip to first unread message

ingo.schramm

unread,
Jun 29, 2010, 8:07:11 AM6/29/10
to Akka User List
After upgrading to 0.9.1 and the new Actor/ActorRef paradigm I
realized that I cannot simply call functions on my actor instance
anymore without sending messages. This is probably a good situation
since it improves the encapsulation of the actors. On the other hand,
having lots and lots of message case classes and such stuff in your
code soon renders it unreadable in real life projects.

Now I adopted a pattern from Erlang. I create a companion object for
my actor class and add functions hiding all the message stuff. In
Erlang it is considered bad style to send messages to a gen_server
directly. This is always done via API functions in the module
implementing the gen_server behavior.

Doing so in Akka works for me and the code is cleaner.

Cheers,
Ingo

Jonas Bonér

unread,
Jul 2, 2010, 5:08:26 AM7/2/10
to akka...@googlegroups.com
This is a great idea. I have used that myself earlier on.
Would be great if you had a little sample or so showcasing the pattern
that we could put on the wiki.
Thanks for contributing with good ideas.

/Jonas

> --
> You received this message because you are subscribed to the Google Groups "Akka User List" group.
> To post to this group, send email to akka...@googlegroups.com.
> To unsubscribe from this group, send email to akka-user+...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/akka-user?hl=en.
>
>

--
Jonas Bonér

work: http://jayway.com
code: http://akkasource.com
blog: http://jonasboner.com
twitter: @jboner

oschulz

unread,
Jul 2, 2010, 8:50:16 AM7/2/10
to Akka User List
I've done something similar on my current project, and it's worked out
quite
well so far (currently with Scala Actor's, but probably with Akka
Actors
very soon for better remoting).

I really do like the Actor model, but I wanted to have something that
would provide more compile-time checking and also provide lots of
comfort when working with the REPL and it's tab-expansion, even
with remote Actors (where you have no access to the Actors
member functions since you only get a proxy).

So I tried to marry the Erlang style of encapsulating the messaging
in API functions with Scala's traits.

The idea is to have something I called server profiles. Servers being
actors with a fixed set of accepted messages, like Erlang gen_server's
and Akka actors. The profile specifies the contract between server
and client and also includes functions encapsulating the messaging.
The profile consists of a trait which contains the "Server-API"
functions
and a companion object containing the message types.

So the server profiles are something like interface specifications
for Actors.


Here's an example, simulating three types of devices, which each
support a specific operation, and a Server, which implements
two of these:


trait FooDevice extends Profile
{ def foo(v: Int) = as[Int] {srv !? FooDevice.Foo(v)} }

object FooDevice { case class Foo(v: Int) }


trait BarDevice extends Profile
{ def bar(v: Int) = as[Int] {srv !? BarDevice.Bar(v)} }

object BarDevice { case class Bar(v: Int) }


trait BazDevice extends Profile
{ def baz(v: Int) = as[Int] {srv !? BazDevice.Baz(v) } }

object BazDevice { case class Baz(v: Int) }


class FooBarServer extends Server with FooDevice with BarDevice {
import FooDevice._, BarDevice._

var i = 0
override def init() = { i = 2}

def serve = {
case Foo(v) => reply(v * i)
case Bar(v) => reply(v + i)
}
}


val a = new FooBarActor
a.start

val p = new ServerProxy(a) with FooDevice with BarDevice
p.foo(4) == 8 // -> true
p.bar(4) == 6 // -> true

new ServerProxy(a) with BazDevice // -> IllegalArgumentException


So if you want to use the Actor/ActorRef in a convenient, type-safe
way, you just wrap it in a ServerProxy (that might not be the perfect
name for it anymore) which you extend with the profiles you expect the
server to support. Of course, a real world server-profile will often
specify several messages and encapsulating functions.

That the server really does support (i.e. extends) the profiles is
currently checked once at runtime when you create the proxy,
but all usage of the proxy can be checked at compile-time.
All servers support a "GetProfiles" message, implemented via
reflection
on the server side, which the ServerProxy's constructor uses.
That the Server's "serve" function really matches all of it's
profile's
messages is the responsibility of it's developer.

This should fit very well with the new Akka ActorRefs and with
remote actors.

Servers can also extend other servers via inheritance, adding
more profiles and supporting them via

def serve = super.serve orElse { ... }

In case anyone interested, the implementation is really simple,
about 160 line of code, and a lot of that is the implementation
of "Server" on top of Scala actors - most of which Akka already
provides.


Cheers,

Oliver

ingo.schramm

unread,
Jul 2, 2010, 10:45:09 AM7/2/10
to Akka User List
Excellent to use traits.

What I had in mind for Akka is like this:

---

import se.scalablesolutions.akka.actor._
import se.scalablesolutions.akka.actor.Actor._

case class Something()

class Server extends Actor {

self.id = Server.id

def receive = {
case Server.MSG => reply(Something)
}

}


object Server {

case object Msg

val id = "SERVER"
val MSG = Msg

def call: Option[Something] = {
?(id) !! MSG
}

def ?(name: String): ActorRef = {
val as = ActorRegistry.actorsFor(name)
if (as.length != 1) throw new Exception("Actor not found in
ActorRegistry: "+ name)
else as(0)
}

}

---

Then you should be able to use it like this:

actorOf[Server].start
Server.call

Sure, this only works for singleton servers. If you have multiple
instances you have to pass the ActorRef to the call() function but
this is also common in Erlang.

Cheers,
Ingo

Jonas Bonér

unread,
Jul 2, 2010, 2:05:04 PM7/2/10
to akka...@googlegroups.com
Thanks.

This is an interesting pattern.

I'm wondering what advantage it gives over the type-safe POJO style
Actors in Akka?

class POJO {
def foo(bar: Bar): Baz = {
...// use Bar directly, return Baz
}
}

val pojo = ActiveObject.newInstance(classOf[POJO]) // pojo is now an actor
val baz: Baz = pojo.foo(new Bar) // invoking foo is sending a message async

/Jonas

oschulz

unread,
Jul 3, 2010, 6:08:03 PM7/3/10
to Akka User List
> I'm wondering what advantage it gives over the type-safe POJO style
> Actors in Akka?

I've read the Akka Active Objects web-page, but have no usage
experience so far. So I beg pardon if the following should
be completely wrong ... ;-)

One important difference is certainly that in the "Actor Profiles"
case, the emphasis is still on actors and messages - they would
play an important role in the design of a project (which messages,
etc.).
In the "Active POJO" case, you never get to see the messages,
it's all done under the hood (correct?) - so the designer would
probably think more in a classic object oriented fashion, than
in a communication based computing one, I guess.

With the active objects, you have a bit less code especially
if you don't use interfaces anyway to abstract implementation
for all objects. On the other hand, the message centric approach
might make interfacing with other systems more natural, since the
whole design emphasises communication.

One advantage of the Actor Profiles is that you can still use
the messages directly at any time. That's nice for writing
actors who have to wait for a reply without blocking their
thread - they can send a request message to another actor,
which uses reply. The response is then handled in the client's
receive when it arrives. Or, you can use the Actor Profile's API
(using !!! or so internally) and get a Future back - but using that
will block the thread. With the Active Objects, you would
need different code on the _serving_ actors side for both
scenarios - a method returning it's result via client
callback in the first, and via normal value return in the
second case. (I hope I got this right ...)

So with Actor Profiles, the client chooses whether to
use bang-query and bang-result-back or !!/!!!. With
Active Objects, it's defined by the serving Active
Object's API.

Hmm, what else - Active Objects need runtime code generation,
right? Might that be a problem in very restrictive
environments? Probably not a problem for most people.


Cheers,

Oliver

Jonas Bonér

unread,
Jul 4, 2010, 5:46:29 AM7/4/10
to akka...@googlegroups.com

Thanks for looking into it.
I think your analysis is accurate.

In short, ActiveObject requires less code to write and understand while Profiles is much more flexible.

I think both has its place. We write up about this design pattern on the wiki. Thanks.

--
Jonas Bonér
http://jayway.com
http://akkasource.com
twitter: jboner



Cheers,

Oliver

--
You received this message because you are subscribed to the Google Groups "Ak...

Reply all
Reply to author
Forward
0 new messages