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

Re: current input & output ports




On Thu, 16 Jun 2005, Taylor Campbell wrote:

> One thing that has always bothered me about Scheme's I/O
> system is the seemingly random presence of 'current
> ports.'  I haven't been able to find a rationale for the
> existence of the mechanism, and exactly what the 'current
> {input,output} port' is meant to be used for has never
> been clear to me.

I think that the input and output ports are the strongest
argument for dynamic variables in a Lisp.  Having adopted
Lexical scope, the scheme RnRS authors didn't want to muddy
the waters for just two variables, so they made them global
instead.

But this is a problem. If as an implementor you take them at
their word and implement a purely lexically-scoped language,
you'll find that the globals "do the wrong thing" in the
presence of call/cc, so you'll wind up making them dynamic
anyway and hiding the details under obfuscating
constructions like CALL-WITH-INPUT-PORT, etc.

> Not only is the mechanism itself vague,
> but it also introduces unnecessary complexities in the
> interface: because the port argument to many operations is
> optional (which I believe Mike doesn't like anyway), the
> port must go last, which skews the possibility of other
> optional arguments and which is contrary to the very
> common convention of putting the important aggregate datum
> in the first argument.

That "very common convention" you mentioned is a direct
consequence of single-argument type-based method dispatch,
which is the OO mechanism that first appeared in Smalltalk,
was popularized by C++ and now inherited by Java, C#, et al.
The convention did not exist yet when scheme adopted its
contrary convention.

Note that Lispy conventions of OO (as typified by CLOS)
mostly use multiple-argument method dispatch for their generic
functions, which eliminates the "pride of place" constraint
you mentioned, but which also creates problems with optional
arguments! *sigh*.

Perhaps what we want is keyword arguments?

> I recognize that it can be useful to store different kinds
> of 'current ports' in the dynamic environment, such as a
> port for general noise output, a port for error output, or
> ports for terminal interaction.  But I don't think that
> the 'current input & output ports,' as outlined by R5RS &
> the current SRFI document, suit any of these roles very
> well, and I see no reason to make a special case for two
> vaguely specified ports so that the argument conventions
> of all of the port operations are skewed with respect to
> optionality and inconsistent with their stream I/O
> counterparts.

You're talking about the way function calls are written, but
the ugliness here (at least in Schemes that don't support
dynamic variables) goes way *way* deeper than that.

I think the reason current-input-port and
current-output-port have remained so vague and
underspecified is because as dynamic structures in a
lexically-scoped language, they are contrary to the design
of the rest of the language.  To specify them clearly and
precisely, you'd have to specify a lot of behavior that
scheme simply does not otherwise use.

> Additionally, the procedures CURRENT-INPUT-PORT, CURRENT-OUTPUT-PORT,
> CALL-WITH-CURRENT-INPUT-PORT, & CALL-WITH-CURRENT-OUTPUT-PORT, would
> all be removed.  The current error output port could still remain, and
> I'd also recommend possibly adding a current noise output port, but it
> is of little significance.


I have a very similar proposal that differs in a few
particulars and would break marginally less (but still most)
code while still cleaning up the design.

Let the current input and output ports be global _AND_IMMUTABLE_.
   (Then the threading and dynamic-variable problems go away)

Add a third global, immutable port, the current error port or
   current debug port.

Specify that they correspond to C's "stdin/stdout/stderr", and
   while in a REPL, correspond to things read and written from
   the console, but allow for scheme "scripts" to be invoked
   with input redirected from or output redirected to pipes.

With these ports immutable, syntax that alters them in the
   scope of procedures called from those forms
   (CALL-WITH_FOO, etc) becomes nonsensical and must be
   removed.

With these procedures removed, the port must be passed explicitly
   in the lexical environment just like any other variable.
   Procedures that take "optional" port arguments should therefore
   be changed to take "mandatory" port arguments.

Library syntax for forms that write explicitly to the standard
   immutable ports would then become trivial to define.

> I also have some suggestions for how to take advantage of
> the new possibilities for making some arguments optional
> introduced by the elimination of the current input &
> output ports, but that can be left for another mail.

Just to make sure you know, there's nothing stopping you from
having multiple optional arguments in the same call...  right?

				Bear