[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Why are byte ports "ports" as such?



   Date: Fri, 14 Apr 2006 09:37:23 -0400
   From: Marc Feeley <feeley@iro.umontreal.ca>

   On 14-Apr-06, at 8:49 AM, John Cowan wrote:

   > Marc Feeley scripsit:
   >
   >> It is a pain to carry those two ports around in the code when the
   >> program needs to communicate bidirectionally with some other entity
   >> (another process, a user at a terminal, a socket, etc).  Moreover the
   >> separation of a conceptually bidirectional channel into distinct
   >> ports (input and output) destroys the conceptual link that they
   >> have.  This hinders program understanding.  For example, with
   >> bidirectional ports (close-port port) will close both sides of the
   >> bidirectional port (i.e. the link between the input and output port
   >> is preserved).  With two unidirectional ports you have to duplicate
   >> some operations (closing ports, changing port settings, ...).
   >
   > I find this rationale convincing (and think it should be added to the
   > SRFI).

   OK.

I find this rational unconvincing: I see no reason why introducing a
third object (say socket, tty, &c.) would hinder program understanding
any more than the bidirectional ports do -- personally, I think
bidirectional ports do more to hinder it.

If we introduce this third object, then we can simplify the model of
input and output ports very much, so that an input port is purely a
source and an output port purely a sink, and we can specify any
options common to the resource that they are both associated with on
the third object; for instance, we could have a (SET-SOCKET-OPTION
socket level option value).  This furthermore simplifies issues about
closing; see below.

Continuing on the socket theme, port I/O doesn't even make sense for
some sockets, such as stream listener sockets and unconnected datagram
sockets.  In these cases it would be absurd to represent the socket as
a port, while it makes very much sense to represent it as a distinct
`socket' object, from which in certain cases (specifically, connected
sockets) one can obtain ports.

   >   However, the socket API does permit closing input and output
   > sides of the socket separately, and there are use cases for this,  
   > so it
   > should be at least permitted though not the default.
   >
   > For example, when you want to send an arbitrary-length undecorated
   > document to a server and retrieve an arbitrary-length undecorated  
   > document
   > back, the simplest way to convey "end of document" is to close the  
   > output
   > side, while leaving the input side open in order to receive the reply.
   >

   That was the intent of the passage:

        When an operation is said to apply to an input port it also applies to a
        bidirectional port.  When an operation is said to apply to an output port
        it also applies to a bidirectional port.

   but I guess it is not clear that this also applies to the port  
   closing operations so I will add a note to that effect.  So yes you can:

       (let ((tty (open-file "/dev/tty"))) ; bidirectional port
         ...
         (close-input-port tty)
         ...
         (close-output-port tty))

   And similarly for any bidirectional port (presumably sockets when  
   they are specified by another SRFI).  For the record Gambit has:

       (open-tcp-client
         (list server-address: "www.apple.com"
               port-number: 80
               eol-encoding: 'cr-lf)))

I think this is actually a bad idea.  It's not entirely clear when the
resource itself gets closed, versus when individual directions are
closed.  CLOSE-PORT, it seems, cannot be defined as

  (define (close-port port)
    (if (input-port? port) (close-input-port port))
    (if (output-port? port) (close-output-port port))),

if it does more than just CLOSE-INPUT-PORT and CLOSE-OUTPUT-PORT;
either that or both CLOSE-INPUT-PORT and CLOSE-OUTPUT-PORT must have
complicated definitions in order to close the resource only if both
directions are closed -- but even then that doesn't work very well
with sockets.  If we were to introduce a third kind of object, and to
disjoin input ports from output ports, then this ambiguity would be
gone.  For instance, if we have a socket, and two ports for the two
directions of communication, then we can have

  (CLOSE-SOCKET socket), to actually close the socket and release any
    resources in the operating system for it;
  (CLOSE-INPUT-PORT inport), to shutdown the socket in the receive
    direction; and
  (CLOSE-OUTPUT-PORT outport), to force any buffered output and such,
    and to shutdown the socket in the send direction.