[erlang-questions] Ideas for a new Erlang

Sven-Olof Nystr|m svenolof@REDACTED
Tue Jul 22 13:45:51 CEST 2008


Sorry for the delayed response. 

Richard A. O'Keefe writes:
 > On 1 Jul 2008, at 6:51 am, Sven-Olof Nystr|m wrote:
 > >
 > > But an Erlang process is always willing to accept messages. Unlike an
 > > Ada process (say) there is no way an Erlang process can prevent
 > > another process from sending it a message. The process can choose not
 > > to look at it, but that's another matter.
 > 
 > I would say that
 >   - a MAILBOX is always willing to accept any message
 >   - a PROCESS is only willing to accept a message when
 >     it is executing a 'receive' construct that will
 >     match it.

Correct. However, the sending process has no way of telling whether
the receiving process has accepted the message or not. In contrast, in
a language with unbuffered communication (such as Ada), the sending
process is blocked until the receiving process has accepted the
message.

 > By the way, the bounded buffer example can obviously be
 > "rescued" thus:
 > 
 > 	producer(Buffer) ->
 > 	    ....
 > 	    Buffer ! {put,self(),Item},
 > 	    receive {ok,Buffer} -> ok end,
 > 	    producer(Buffer).
 > 
 > 	consumer(Buffer) ->
 > 	    Buffer ! {get,self()},
 > 	    receive {ok,Buffer,Item} -> ok end,
 > 	    ....
 > 
 > 	buffer(Pool, Left) ->
 > 	    receive
 > 		{put,Producer,Item} when Left > 0 ->
 > 		    Producer ! {ok,Buffer},
 > 		    buffer(Pool ++ [Item], Left - 1)
 > 	      ; {get,Consumer} when Pool =/= [] ->
 > 		    Consumer ! {ok,Buffer,hd(Pool)},
 > 		    buffer(tl(Pool), Left + 1)
 > 	    end.
 > 
 > Assuming that this is part of a system where only the
 > producer and consumer can refer to Buffer, this is
 > the traditional bounded buffer.  The argument that the
 > mailbox (but NOT the process) is always willing to
 > accept more messages has no force between the senders
 > won't *send* more messages; the buffer's mailbox will
 > have at most one 'put' and and most one 'get' at any
 > one time.  The substantive point, that the *process*
 > (not the mailbox) is willing to accept different
 > messages at different times remains.

Yes, this is what I had in mind. Here you are in effect simulating
unbuffered communication. 

Using channels, the receive in the functions producer/1 and consumer/1
can easily be rewritten using channels. The function buffer/2 needs a
third parameter containing an explicit queue of unprocessed requests.

 > This is a nice illustration of why mailbox length is
 > often not a problem:  the need for a response means
 > that the mailbox is bounded in size by the number of
 > processes that might send messages.  In effect, the
 > responses are a form of flow control.

Unless a process sends more than one message. (Allright, I agree that
normally there would only be one outstanding message per process.)

 > > So your argument is: Concurrent ML tried something similar.
 > > It did not work out.
 > 
 > It worked out in the sense that the design was implementable,
 > was implemented, was used, and is still available in SML/NJ
 > and Mlton.  It also worked out in the sense that *simple*
 > problems don't look too bad.  The point is that things which
 > would still be pretty straightforward in Erlang with its
 > 'receive' quickly become very complex in CML.

I have not tried writing anything in CML, but I have looked through
the documentation and read parts of Reppy's thesis. The language does
not look very attractive, but it doesn't look *that* bad. It would be
interesting to here more about why CML was hard to use for complex
problems.

 > 
 > Interestingly, the Limbo programming language has CML-like
 > channels, but without all the extra stuff in CML.  (For
 > example, CML has both channels and mailboxes.)

Right. So CML channels are unbuffered, while CML mailboxes are
buffered. Limbo channels are also unbuffered.

 > http://www.vitanuova.com/inferno/papers/limbo.html
 > 4.2.5
 > 	chan of <data-type>		is a type.
 > 	ch <-= msg			is a send.
 > 8.2.9
 > 	<- ch				is a receive
 > 	<- ch_array			is multi-receive
 > For example,
 > 	msg = <- ch
 > receives a message from one channel.
 > 	(ix, msg) = <- ch_array
 > receives an (index, message) pair where the index says
 > which element of the channel array the message came from.
 > 8.4.3
 > 	ch <-= msg
 > blocks if no process is reading from ch.
 > 9.8
 > 	alt {... msg = <- ch => stmt;
 > 	     ... ch <- = msg => stmt;
 > 	}
 > is like an Occam ALT.
 > 
 > Let me quote section 12.4 in full.
 > 
 >      12.4 Buffered channels
 > 
 >      Limbo channels are unbuffered; a sender blocks until there is a
 >      receiver.  This example shows a way to make a buffered channel of
 >      strings from an unbuffered channel.  It is written as a module  
 >      whose
 >      bufchan function takes a chan of string and a size as argument, and
 >      returns a new channel; it creates an asynchronous task that accepts
 >      input from the argument channel and saves up to size strings,
 >      meanwhile trying to send them to its user.
 > 
 >      implement Bufchan;
 > 
 >      Bufchan: module {
 >          bufchan: fn(c: chan of string, size: int): chan of string;
 >      };
 > 
 >      xfer(oldchan, newchan: chan of string, size: int) {
 >          temp := array[size] of string;
 >          fp := 0;    # first string in buffer
 >          n := 0;     # number of strings in buffer
 >          dummy := chan of string;
 >          sendch, recvch: chan of string;
 >          s: string;
 > 
 >          for (;;) {
 >              sendch = recvch = dummy;
 >              if (n > 0)    sendch = newchan;
 >              if (n < size) recvch = oldchan;
 >              alt {
 >                  s = <-recvch =>
 >                      temp[(fp+n)%size] = s;
 >                      n++;
 > 
 >                  sendch <- = temp[fp] =>
 >                      temp[fp++] = nil;
 >                      n--;
 >                      if (fp>=size) fp -= size;
 >              }
 >          }
 >      }
 > 
 >      bufchan(oldchan: chan of string, size: int): chan of string {
 >          newchan := chan of string;
 >          spawn xfer(oldchan, newchan, size);
 >          return newchan;
 >      }
 > 
 >      The module is somewhat specialized, but it illustrates useful
 >      programming techniques.  The most interesting occurs in xfer, which
 >      does the work.  The problem xfer faces is that it doesn't want to
 >      receive input when its buffer is full, nor to try to send when it
 >      has nothing to transmit.  The solution here is to use a dummy
 >      channel on which nothing is ever sent or received; in the alt
 >      statement, that channel substitutes for the real input channel when
 >      the buffer is full, and for the output channel when the buffer is
 >      empty.
 > 
 >      [snipped]
 > 
 > This is "real" bounded buffer.  Note the trick: you need three
 > channels, one of them a dummy that will never be used.  If you
 > don't want a particular alternative to really be there, make
 > that one wait for a communication that can never happen.  To me
 > this is much harder to understand than a guarded 'select' in
 > Ada or 'alt' in Occam, because what's _really_ going on is hidden
 > under a layer of what I can only call obfuscation.


Yes, this is a nice example. Also similar to the Ada example you gave
previously. Naturally, in a language without buffered communication,
one of the first things one needs to implement is buffered
communication :-)


 > > So we agree that it's *not* an implementation of bounded buffers????
 > 
 > Why do you harp on this one note?
 > Idunno, you strip things to the bone to make a point concisely,
 > and someone makes a federal case out of your kindness.
 > 
 >  From now on, please do not refer to the stripped down example,
 > but only to the full producer/buffer/consumer example, which contains
 > material not relevant to the state-dependent-reception POINT.

I asked because it seemed like an important example. I promise, I will
not bring it up again :-)

 > >
 > 
 > > Since I came up with the channel concept, I've been looking for some
 > > convincing example of an Erlang program that could be implemented
 > > using selective receive, but was not possible to implement using
 > > channels (or where the solution with channels was more complex).
 > 
 > Try looking in the Erlang sources.  There are plenty of simple cases
 > there, true.  But there are also plenty of cases where many channels
 > would be needed, making the solution with channels look a lot more
 > complex to me.

I have looked at the POTS solution in the Erlang book and the
gen_server module. See my response to Ulf Wiger's post previously in
this list.

 > 
 > It may be that we have different ideas of what counts as a 'more
 > complex' solution.  If this thread is not to degenerate into something
 > out of Dr Suess (was it the West-going something and the East-going
 > something else or was it North and South?) we really need to see
 > some concrete cases FROM THE PROPOSER.

OK.

 > The proposal in the document I was responding to does not contain
 > any multi-receive, and it's not clear that one can be programmed from
 > the operations that are in the document.  I don't know any message-
 > based language that doesn't have some analogue of an Occam 'ALT'/
 > Limbo 'alt'/Ada 'select', and that document doesn't include one,
 > so it's clearly incomplete that way as well.

Actually, I think unbuffered languages (such as the ones you mention)
are very different from buffered languages, and that it is a mistake
to try to transfer experiences from the unbuffered languages to
Erlang.

 > 
 > So we need to see a functionally complete proposal (in order to
 > judge how complex the proposed replacement for 'receive' is in
 > itself) *AND* some examples of rewritten Erlang code to judge
 > how complex the replacement is in use.

OK.


 > For the statement
 > > I've been looking for some
 > > convincing example of an Erlang program that could be implemented
 > > using selective receive, but was not possible to implement using
 > > channels (or where the solution with channels was more complex).
 > to have any force, there must already BE a number of examples which
 > have been so rewritten.  So let's see them.

I'll get back to you on that.



Sven-Olof





More information about the erlang-questions mailing list