Saturday, November 9, 2013

Moving Forward: Type Safety & Actors

Type safety is a topic that comes up very frequently when talking to C++ developers about libcppa - and actors in general. Indeed, the major concern about libcppa at the C++Now 2013 conference was that it does not provide a type-safe messaging interface. I'm not going to discuss pros and cons of dynamic and static typing. Both approaches do have their benefits. In libcppa, you can use atoms to "create" new message types on the fly and then pattern match on the receiver side. In this way, it is very lightweight to introduce new message types to a system, as you don't have to maintain header files for your message types. On the flip side, "wrong" message types are detected at runtime. When sending arguments in the wrong order, your pattern on the receiver side will not match. Naturally, most C++ developers expect the compiler to detect those kind of bugs.

Version 0.8 introduced strongly-typed actors to give developers a choice. Unfortunately, libcppa is now a two-class society. Even worse, it is indeed rather easy to get an untyped reference to a strongly-typed actor. But let's step back and take a look at the issues individually.
  1. Second-class Typed Actors: Typed actors are not allowed to use guard expressions. This in itself is not a problem. The problem is how libcppa signals system events. For example, when monitoring an actor, you'll receive a "down" message consisting of the atom 'DOWN' and the exit reason as uint32. Since typed actors are not allowed to use guards, the only thing they could possibly do is to define a handler for all messages with an atom as first element followed by an uint32. There are 1064 possible atoms. As a consequence, libcppa needs to introduce message types for system messages. This will break code and I would prefer not to, but it's better to break some code that's easy to repair than to have a broken design.
  2. Separated APIs: To spawn a typed actor, we use functions that return the behavior. For untyped actors, we can use void functions that call 'become' internally. Even worse, 'become' now can throw - when called from a typed actor. It's probably for the best to move 'become' from the namespace cppa to the class event_based_actor and always use function that return a behavior.
  3. Type-safety Not Enforced: As you might know if you've ever used libcppa: An actor can get a handle to itself by using 'self' and a handle to any actor that sent a message to it by calling 'self->last_sender()'. This handle is of course untyped. So, not only can a typed actor get an untyped handle to itself, each actor it is communicating with has an untyped handle to it. The only way to enforce type safety is to either remove 'self' completely from the API or to change its type to something not convertible to actor_ptr, so that you can only access member functions like 'trap_exits'. The member function 'last_sender()' then also would have to go or to return only a logical address that you can use for monitoring and linking, but not for message passing.
  4. Missing Features: This is not a design issue, but libcppa 0.9 will of course implement proper remote communication for typed actors.
libcppa started as a DSL for Erlang-style actor programming in C++. With version 0.9, libcppa will (hopefully) evolve into a unique actor system that supports untyped and typed actors side by side. Stay tuned. :)