[erlang-questions] Ideas for a new Erlang

Ulf Wiger ulf@REDACTED
Sat Aug 2 13:29:54 CEST 2008


2008/8/1 Sven-Olof Nystr|m <svenolof@REDACTED>:
> Ulf Wiger writes:
>  > > Ulf Wiger writes:
>  > >  > You should also take a look at the presentation that I referred to.
>
> Two general comments on the slides:
>
>  1) I don't think the comparison with the select operation of Unix is
>    valid. Even without selecive receive, it is easy enough to set up
>    an Erlang process to wait until one out of a number of things
>    happen.

The key is to be able to be specific about whose messages you
want to deal with. Erlang's selective receive lets you do that, as
does Unix select().


>  2) One of the problem you are discussing is how to describe
>    concurrent activities within one state machine. UML's mechanism
>    for expressing composite state seems to be close to what is
>    needed here.

Not as far as I understand them. The composite state machines allow
you to build very complex receive logic, but the message reception
semantics is still FIFO, run-to-completion.

There is, however, an attribute in UML - 'deferrable event', which allows
you to specify that an event should be buffered, rather than discarded,
if there is no matching handler in the current state. Not all UML-based
tools support this attribute, and it receives very little attention in the
specification.

The key issue is that if you collect messages from different senders in
a single mailbox or message queue, there will be accidental ordering.
If your program logic strictly handles messages FIFO, it imposes a
requirement that you have to be able to deal with all possible
serializations of events - which invariably means that you have to
perform some form of buffering in your application.

Buffering channels address this problem, of course.
The reason why I suggested that you look at the POTS example
wasn't that I didn't think you could solve it with channels; it was
to give you an *interesting* example in order to assess whether
a channel-based solution would be more or less elegant than the
traditional erlang version.


> OK, I've looked at Nordlander's thesis.
>
> In OHaskell, there are two ways of specifying that an object may
> accept communication; keywords "action" and "request". The second is
> similar to what in the Erlang world is referred to as synchronous
> communication. It seems to me that using "request" you would get the
> same behaviour as send/request pairs in Erlang.

Yes, but only when it can be determined at compile-time that the
'server' end cannot block. That's obviously not the case in the POTS
example, where the server end is likely another computer.



>  > >  > The OHaskell example assumed that functions like start_tone()
>  > >  > were atomic and non-blocking - they are not.
>
> What would go wrong if the implementation of start_tone() and similar
> functions was non-blocking?

That means that your control thread has to yield and possibly accept
other events while waiting for the start_tone_reply msg. If, for example,
the next event is on_hook, a stop_tone() should be issued, but first,
we need to wait for the start_tone_reply.

Since the order of start_tone_reply and on_hook is accidental, we
are free to process them in any order we choose. Just picking the
first message from the pile is a very bad choice.


> Making start_tone() synchronous is straight-forward using channels.

Yes. The interesting question is whether using channels leads to
more or less elegant code, given that it avoids complexity explosion.

>
>  > > I am not sure what you mean by synchronous and unsynchronous. In

>  > The last example in the presentation implements a filtering
>  > event loop, which might be similar to a solution using
>  > channels.
>
> I was wondering about that.
>
> The filtered event loop (page 24) in your presentation makes
> assumptions on how states are represented (as tuples of sub-states?)
> and what messages look like (containing indexes into the state
> tuples?):
>
> event_loop(M, S, Recv) ->
>  receive
>    {From, Event} when element(From, Recv) == [] ->
>      dispatch(From, Event, M, S);
>    {From, Ref, Event} when element(From, Recv) == Ref ->
>      dispatch(From, Event, M, S);
>    {From, Ref, Event} when element(From, Recv) == [] ->
>      dispatch(From, Event, M, S)
> end.
>
> Here, one Erlang process implements several concurrent activities.
> However, it's unclear how these activities are represented. The
> preceeding slides give some hint to what is going on, but not the
> whole story.

It's the same as in the first event-based program, except I've added
an argument where the callback module can let the event loop
know which messages to ignore.

> (From looking at the code, I get the impression that event_loop
> actually makes heavy use of selective receive, but whether this is the
> case or not depends of course on the context.)

Yes, the event loop makes use of Erlang's selective receive. The
application logic does not. It simply specifies which senders, or
which unique message tag to look for next.

To be clear, the application logic cannot get away from performing
some form of selective message processing in this problem. It
either instruments the logic that picks messages out of the buffer(s),
or implements a stack so that it can handle messages out of order.


>  > Take the case of admission control. It is triggered by the main
>  > call control state machine, and the continuation depends on
>  > the result of the request. It's prudent to let a separate process
>  > deal with the DIAMETER specifics (which may involve failure
>  > detection and retransmission/rerouting of the request), but
>  > the main state machine must still await the result. If it
>  > sends the request and doesn't selectively wait for the reply,
>  > other signals may arrive that conflict with the outstanding
>  > request.
>
> I'm not sure I get this. Waiting for a reply to a particular request
> using channels is straight-forward. I can see that it might be hairy
> to break down a side into several processes, but it still seems to be
> a viable approach.

You can solve it using channels, given that you have some way to
select which channels to listen to at any given point in time. If you
don't, you are right back at the global state-event matrix.

This is what UML does in the absense of deferrable event. It uses
channels (ports), but message handling is FIFO.


>  >         If I implement a wrapper around the DIAMETER
>  > request-response pair used for admission control, it doesn't affect
>  > the state machine any more than if it were an atomic check of a
>  > locally stored value.
>
> I agree with the last sentence, but I don't see how this makes it
> harder to break down a side into processes. After all, regardless of
> how the result is computed, you still need to wait for it.

You will still have some form of coordinating state machine.
You can break down certain things into processes, but in order to
avoid complexity explosion in the main state machine, there has to
be a way for it to specify in which order it wants to handle events that
have no defined ordering relationship.

You can do that using channels. You can also do it using Erlang's
selective receive. Either way you decide to solve it, you have to
address that problem, or your code will explode. Once you've contained
your state-event matrix, you are free to look at the finer points, such as
"do channels offer a more elegant solution to this problem than Erlang's
selective receive?" I haven't taken a stance in that regard, since I haven't
written such code with channels. ROK seems to think that it doesn't give
an improvement, based on his experience with ML.

BR,
Ulf W



More information about the erlang-questions mailing list