std.stream vs std.stdio (Was: Re: [OT] Programming language WATs)

H. S. Teoh hsteoh at quickfur.ath.cx
Tue Feb 7 07:57:53 PST 2012


[Changed subject to match where discussion is going.]

On Mon, Feb 06, 2012 at 11:27:36PM -0800, Jonathan M Davis wrote:
> On Monday, February 06, 2012 23:01:06 H. S. Teoh wrote:
[...]
> > I don't see what's the discrepancy between formatting and streams.
> > As far as I'm concerned, writeln() is essentially taking a bunch of
> > objects, converting them to string representations, and writing said
> > strings sequentially to some output channel (i.e., output stream).
> 
> In C++, you end up with stuff like
> 
> cout << "point: [" << x << ", " << y "]" << endl;
> 
> That's already much uglier than
> 
> printf("point: [%f, %f]\n", x, y);
> 
> But what if you want to do something like %02f? With printf, that's easy:
> 
> printf("point: [%02f, %02f]\n", x, y);
> 
> With streams, it's hideous. I'd have to go look it up to give you an example. 
> It involves setting flags in the stream and the like, and it's horrible. In my 
> experience, no one uses it.

But this is really just the result of a misguided implementation.
Nothing about the concept of streams requires you to use C++'s very ugly
scheme of setting stream manipulators just to get formatting right.
Things like writeln() or writefln() are not inherently tied to stream or
non-stream; they are just convenience functions for converting types
into string representations. What you do with the converted result is
orthogonal to the formatting itself.

Nothing stops us, for example, from implementing writefln() such that it
writes its output to a stream object. Formatted output in itself has
nothing to do with whether the target is a stream or not. Nothing stops
the implementation from using a temporary buffer for, say, formatting
field widths, etc.. The result still written somewhere -- and my whole
point is that, that "somewhere" is essentially just an output stream.
You don't need random access, you don't need mmap(), all you need is a
sink to send the characters to. Right now it has to be some kind of
File, but my point is that it doesn't *have* to be. Any output stream
would do.


[...]
> The long term plan is to create a std.stream which then provides a
> range-based interface for streams. Then you can do with it whatever
> you can do with any input stream - though there's still a lot that you
> would lose out on, since it can't be a forward range. Exactly how that
> will interact with std.stdio, I don't know. That will depend on the
> API that's finally decided up.
>
> Regardless, my point was that while it might make sense to have a
> stream over a file, it doesn't make sense for a stream to _be_ a file,
> any more than it makes sense for a container to _be_ a range. You have
> a range over the container. The container itself isn't a range. So,
> std.stream's File type is poorly named IMHO. But it's going away in
> the long run, so it ultimately doesn't really matter what it was
> named.

Correct, it should be the other way round: a _file_ is a kind of stream,
but a stream is not a kind of file (e.g. it could be a socket, a char
array, a procedural source/sink of characters). It's pure and simple OO.
In my mind, it's as simple as:

	class Stream {
		...
	}

	class File : Stream {
		...
		/* additional methods for random access */
	}

	void writeln(T...)(OutputStream os, T args) { ... }
	void writeln(T...)(T args) { writeln(stdout, args); }
	void writefln(T...)(OutputStream os, string fmt, T args) { ... }
	void writefln(T...)(string fmt, T args) {
		writefln(stdout, fmt, args);
	}

Of course, this is just the first level of refinement. To be truly
generic, we could define popFront() on input streams and put() on output
streams, and similarly for arrays or even arbitrary objects, then we can
use writeln(), writefln(), etc., on all of these types freely.


> > > In any case, std.stream is rather old and outdated and will be
> > > replaced at some point with a range-based API. And its replacement
> > > may interact with std.stdio better.
> > 
> > [...]
> > 
> > Yikes! So I should just avoid using it altogether then? How come
> > it's not marked deprecated?
> 
> Because it doesn't have a replacement yet. [...]
> It's like std.xml. You can use it, but it's not going to be around in
> the long term, and we don't have a replacement for it yet. We're just
> certain that the current version is unacceptable and needs to be
> replaced. It would probably be a good idea to put a warning of some
> kind in the documentation, but for the moment, it's not going away.
[...]

I say it *definitely* should be clearly marked as "to be deprecated" in
the docs. Otherwise unknowing newbies will invest lots of time to learn
the API, only to discover months down the road that all of that was for
nought. That will definitely turn away a lot of people.


T

-- 
Lawyer: (n.) An innocence-vending machine, the effectiveness of which
depends on how much money is inserted.


More information about the Digitalmars-d mailing list