[Haskell] Re: Top Level TWI's again was Re: Re: Parameterized Show

Aaron Denney wnoise at ofb.net
Tue Nov 23 14:20:29 EST 2004

On 2004-11-23, Benjamin Franksen <benjamin.franksen at bessy.de> wrote:
> On Tuesday 23 November 2004 00:10, Aaron Denney wrote:
>> On 2004-11-22, Benjamin Franksen <benjamin.franksen at bessy.de> wrote:
>> > On Monday 22 November 2004 09:38, Adrian Hey wrote:
>> >> You have yet to
>> >> explain how you propose to deal with stdout etc..
>> >
>> > I see absolutely no reason why stdxxx must or should be top-level mutable
>> > objects. They can and should be treated in the same way as environment
>> > and command line arguments, i.e.
>> >
>> > getArgs :: IO [String]
>> > getEnv :: String -> IO String
>> > getStdin, getStdout, getStderr :: IO Handle
>> >
>> > Note that (just like environment and command line arguments) these
>> > handles may refer to completely different things on different program
>> > runs.
>> Er, no.  The handles can be considered as the same but _pointing_ to
>> different things on different runs.
> I wrote "may refer to", not "are", so yes.

They're wrappers around the integers 0, 1, and 2.  The handles could
have been implemented to be the same, at each invocation.  (I expect
they are in most implementations).  If we had to make them ourselves,
they could be done as:

stdin = makeHandle 0
stdout = makeHandle 1
stderr = makeHandle 2

in absolutely pure Haskell, only the things that manipulate them need
be in the IO monad.  They're not the external state in the world to
which they point -- just ways of instructing the OS.  I don't see how
sprinkling "stdin <- getStdin" in every IO routine helps at all.  The
means of instructing the OS is a constant.

Unlike the case with IORefs, we have an extra argument that keeps the
compiler from aliasing these together.

The arguments and environment really are different at each invocation,
not merely referring to different things.  If you add a redirection,
the "argument space" and "environment space" at invocation (barring
relocation) would be the same.  But since there's only one, it's
easier to provide access functions with the argument of where to
look already applied.

>> Keeping them outside the IO monad, and only accessing them inside --
>> i.e. the current situation -- would be fine.
> I beg to differ. Note, I do not claim they are unsafe.

If it's not unsafe, and it makes for simpler (hence easier to
understand, create, debug, and modify) in what sense is it not fine?

>> They're not mutable in any sense.
> Well, a variable in C is not mutable in exactly the same sense: It always 
> refers (="points") to the same piece of memory, whatever value was written to 
> it. Where does that lead us?

A slightly different sense, but I won't quibble much.

It would lead us to being able to have TWIs, only readable or writeable
in the IO Monad.  Many people don't think that would be such a bad
thing.  But because of the semantics we expect from IORefs, we can't
get them without destroying other properties we want.

a = unsafePerformIO $ newIORef Nothing

Respecting referential integrity would give us the wrong semantics.
Adding labels would force the compiler to keep two differently labeled
things seperate, but it would fall down for things having the same

a = unsafePerformIO $ newLabeledIORef "a" Nothing
b = unsafePerformIO $ newLabeledIORef "a" Nothing

If we look at this, we could legitimately expect them to either be
unified, or not be unified, but we would want consistency.  Doing this
uniformly seems a tough burden on compiler writers.

In contrast with IO Handles, there the OS does all the work.  If
"makeHandle" were exposed to us, It really wouldn't matter whether

handle1 and stdout

handle1 = makeHandle 1
stdout = makeHandle 1

were beta-reduced or not.  Either way, the OS naturally handles how we
refer to stdout.

(One caveat here -- buffering implemented by the compiler & runtime
would make a difference.)

Aaron Denney

More information about the Haskell mailing list