Lennart, Fergus,
re this stdin issue you're missing the point I was trying to make. It
really only bites badly when you have concurrency. I enclose the relevant
message, which gives an example of the extra expressiveness non-constant
stdin etc buys you -- I don't know which bit you disagree with, so the best
I can do is simply repeat it.
Simon
| > > I don't honestly see what having these handles as constant *gain* you,
| > > so why then have them as such, if not having them constant gives you
| > > extra expressiveness?
| >
| > But, unless I'm missing something, making them non-constant doesn't
| > give you any extra expressiveness.
| >
| > Remember that the fact they are constant does not imply that the
| > file they are connected to is constant. Even if they are effectively
| > thread-local, i.e if the file that stdin and stdout are connected to
| > depends on which thread you're in, stdin and stdout themselves can
| > still be constants, can't they?
| Exactly!
|
| I regard stdin, stdout, and stderr as names for abstract versions of
| 0, 1, and 2 (does my background in C programming on Unix show? :-).
| These are just handles, the can be reconnected to anything. If you
| argue that you should be able to change the meaning of stdin I could
| argue "This piece of code I've got uses the constant 5, this is not
| what I want, I need to change the value of 5 locally when that code
| runs. Give me the machinery to do that!"
To: [EMAIL PROTECTED] (Kevin Hammond)
cc: [EMAIL PROTECTED], simonpj
Subject: Re: stdin as a constant
Date: Thu, 16 Jan 1997 11:07:19 -0800
From: Simon L Peyton Jones <simonpj>
Content-Type: text
Content-Length: 1859
| What you want is allowed. Although stdin *is* a constant, it's simply a
| constant that refers to a handle. The handle information (file pointer,
| attached device etc.) needn't be constant. It's entirely consistent to
| provide, say:
|
| reconnect :: Handle -> Handle -> IO Handle
|
| to connect one handle to the same device as another (as long as you're
| careful in the impl. about duplicating file pointers, which can get you into
| trouble -- new files are much easier to deal with!). This was actually one
| of the extensions that was considered fairly carefully in the I/O design.
This doesn't do what Sigbjorn is suggesting.
Suppose I import a module written by someone else which exports
the function toUpper:
-- toUpper reads stdin, converts all characters to upper
-- case, and sends them to stdout
toUpper :: IO ()
Now I import another module which exports sort:
-- sort reads stdin, divides it into lines, sorts the lines
-- and sends them to stdout
sort :: IO ()
Now suppose I want to arrange to connect toUpper and sort together.
I might hope to do it like this:
module Main where
import M1( toUpper )
import M2( sort )
main = do
(in,out) <- pipe;
forkIO (withStdOut in toUpper);
withStdIn out sort
The new functions are
pipe :: IO (Handle,Handle)
withStdOut, withStdIn :: Handle -> IO () -> IO ()
The point here is that toUpper and sort each work with *different* mappings
of stdin, stdout.
This example uses concurrency, which Haskell doesn't have.
In the absence of concurrency you can hack it like this:
main = do
(in,out) <- pipe;
old_stdout <- setStdOut in;
toUpper;
setStdOut old_stdout;
setStdIn out;
sort
But it's a hack, isn't it. And it doesn't extend to concurrency.
But this may help explain why I'm not so steamed up as I am about argv!
Simon