O'Haskell (was Re: Structs (was RE: Record selectors))

Ulf Wiger etxuwig@REDACTED
Mon Jan 20 11:26:33 CET 2003


On Sun, 19 Jan 2003, Mikael Karlsson wrote:

>Well,
>actually it seems they are "cheating" and just send an
>asynchrounous stop_tone message without expecting any
>return and then immediately set the state to Idle:
>....
>       on_hook' = do
>           if null nr then
>               tele_os.stop_tone myaddr
>           state := idle
>...
>Timber ( or O'Haskell ) actually have both asynchronous
>(Actions)  and synchronous (Requests) methods, so making
>stop_tone a synchronous request would make it more similar
>to the Erlang way. The synchronous calls I have seen seem
>only to access and return internal state variables though.
>I guess in order to avoid deadlock :-). The normal way when
>things take time seems to be to install callbacks.

... and this is where things start falling apart. Complexity
rises enormously in your application if you cannot avoid
this. Some form of selective receive is required for the
model to remain stable, I think.

>The point about selective receive from the message queue is
>quite a good one, but if you want to simulate digital
>circuits which was Chris intention, I think that reactive
>objects are quite appropriate. It is quite difficult to do
>a selective receive of whathever the state changes on the
>pins are.

If anyone was wondering why I was so trigger-happy on this
particular POTS-issue, I can reveal that I'm actually trying
to do some syntax-neutral comparisons between different ways
of programming the POTS example. One of my implementations
(which actually works too) is fairly similar to the
"reactive objects" idea:

First, an event loop:

event_loop(M, S) ->
    receive
        {From, {Event, Arg}} ->
            S1 = M:Event(From, Arg, S),
            event_loop(M, S1);
        {From, Event} when atom(Event) ->
            S1 = M:Event(From, S),
            event_loop(M, S1)
    end.

Then, the actual control logic:


-module(control).
% asynch_0 (asynch model, blocking hardware API)

-compile(export_all).

-record(s, {state = idle}).

start() ->
    asynch_main:event_loop(?MODULE, #s{}).


offhook(lim, #s{state = idle} = S) ->
    lim:start_tone(dial),
    S#s{state = getting_first_digit};
offhook(lim, #s{state = {ringing_B_side, PidA}} = S) ->
    lim:stop_ringing(),
    PidA ! {self(), connect},
    S#s{state = {speech, PidA}};
offhook(From, S) ->
    io:format("Got unknown message in ~p: ~p~n",
              [S#s.state, {From, offhook}]),
    S.

... and so on.

It turns out that this implementation is a bit shorter
than the "original", and does seem to have some advantages.
(My subjective opinion is that it's harder to follow, but
this may be in part because I've been corrupted by Erlang
for so long.)

Note, however, the comment "blocking hardware API". I'm
cheating, and pretending that I can control the hardware
easily, without re-writing my state machine.

The big issues are:

- Can I be sure that the components I use from my state
  machine are always non-blocking and deterministic?
- Am I allowed to block? If, say, I handle all my calls in
  one erlang process, I cannot block the process an wait
  for a message from another node or some hardware. Then,
  I must break up the calls into asynchronous messages, and
  the complexity of my state machine quickly grows into
  unmanageable proportions. Try one non-blocking component,
  then add another one, and see what happens.
- Can I do selective receive (assuming I can block)? If I
  can, my state machine can remain stable even if it uses
  components with blocking semantics.

I don't think the issue is really state-oriented vs
message-oriented programming. There are a few core tenets of
soft real-time programming that you simply have to get
right. Erlang does it pretty well. Most languages don't, and
I think many language designers haven't really thought about
what those core tenets really are.

I note that the comments to the O'Haskell POTS example
claimed that objects in telecom applications must be
reactive. This is only partly true. Tele-/Datacom Signaling
control differs from e.g. control of anti-lock breaks in
that it is often no disaster if a thread remains partially
unresponsive for some milliseconds, or even more. The
specifications talk of e.g. being able to serve 95% of all
calls within certain time limits, but beyond that, it's even
OK to drop some calls now and then. This would be less
than satisfactory for an anti-lock break system.

This is an important difference between "soft" real-time and
"hard"  real-time. Programming of hard real-time
mission-critical systems must be an amazingly difficult
discipline (I've never done it), but some of the
non-negotiable aspects of such programming simply do not
hold as much for "soft"  real-time, and this can be used to
greatly simplify the programming model.

/Uffe

(*) We once ran a stress test on an unused AXD301 field
system. AFAIR, pushing through 140 million calls over a long
weekend, I believe we dropped some 1 or 2 ppm of the calls.
This is pretty cool for telephony, but if we had built a car
and received these figures from a break test, we'd probably
recall the model.

-- 
Ulf Wiger, Senior Specialist,
   / / /   Architecture & Design of Carrier-Class Software
  / / /    Strategic Product & System Management
 / / /     Ericsson Telecom AB, ATM Multiservice Networks




More information about the erlang-questions mailing list