On Sep 11, 2012, at 3:01 PM, Jasper St. Pierre <jstpierre@mecheye.net> wrote:

Additionally, IRC daemons may play tricks and send out "replies" that
doesn't associate with any request you've sent, as a means to have a
client perform a specific action; for instance, bouncers usually send
join replies without any associated requests method of forcing a
client to open a buffer for a particular channel, and certain networks
also use this same technique to force users on specific channels all
the time (and they will also make a PART request on this particular
channel fail).

It's not exactly that they're "forcing" users (or clients) to do anything. They're just notifying the user that they're in a particular channel.  In other words, the "forcing" is all done server-side, not in the protocol itself.

These two things mean that you cannot have a Deferred-based API for
IRC, where each request correlates to one response, and instead need
to have a reactionary system where you cast a NAMES out to the wind,
and just implement some sane behavior on every RPL_NAMREPLY you get,
independent of what triggered it.

This is actually a better way to structure applications in general, even for much more sanely structured protocols than IRC.

One subtle point about Twisted is that while Deferreds make it a lot easier to manage sequential work-flow in an event driven system, it is sequential work-flow itself that is the problem in many cases.  You should always try to write your protocols so they can react to any valid input at any time, not rigidly structure everything so that your state is always shoehorned into a request/response structure so all your state is encapsulated in stack frames in closures that are added as callbacks on Deferreds.

If I were designing a new chat protocol today, I'd definitely make sure it had a "you joined a conversation" message that did not have to be a response to any particular "I'd like to join a conversation" message :-).

-glyph