
I'm, analysing the IO-system of the GHC and need more information about it. More precisely: How switches the run-time-system between (normal) evaluation and performing of IO-actions?
I read the rts-paper, but didn't find any information about this.
Can somebody give a short review, or are ther any other papers available?
The runtime system mostly has no concept of IO vs. non-IO evaluation. The IO monad is implemented as the following type: newtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #) which means that an IO action is simply a function from the world state to a pair of a new world state and a value. In other words, it's a straightfoward state monad, except that we use some optimised representations to eliminate some of the overhead of passing the state around. Cheers, Simon

The runtime system mostly has no concept of IO vs. non-IO evaluation. The IO monad is implemented as the following type:
newtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #)
which means that an IO action is simply a function from the world state to a pair of a new world state and a value. In other words, it's a straightfoward state monad, except that we use some optimised representations to eliminate some of the overhead of passing the state around.
Cheers, Simon
Ok so far, but when is the IO action performed? Look at this small programm: module Main(main) where main = echoTwice echo = getChar >>= putChar echoTwice = echo >> echo while executing: the program reads first two chararcters and then it writes the characters, but in my opinion it would be right to read one character, then write one characater, then read, then write. What's the reason for it? ---------------------------------- David Sabel JWGU Frankfurt

As Simon explained to me recently, that is the problem with this type of lazy IO: the sequence of operations is undefined. There is no requirement that the read consume only the characters that have already been requested; in fact, that would likely be horribly inefficient. Plus, it is quite possible that the underlying operating system is consuming both characters before it feeds the first one to GHC. So, this type of IO should only be used when you don't care about the precise order of operations. On Monday 16 September 2002 18:06, David Sabel wrote:
The runtime system mostly has no concept of IO vs. non-IO evaluation. The IO monad is implemented as the following type:
newtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #)
which means that an IO action is simply a function from the world state to a pair of a new world state and a value. In other words, it's a straightfoward state monad, except that we use some optimised representations to eliminate some of the overhead of passing the state around.
Cheers, Simon
Ok so far, but when is the IO action performed?
Look at this small programm:
module Main(main) where
main = echoTwice
echo = getChar >>= putChar
echoTwice = echo >> echo
while executing: the program reads first two chararcters and then it writes the characters, but in my opinion it would be right to read one character, then write one characater, then read, then write. What's the reason for it?
---------------------------------- David Sabel JWGU Frankfurt
_______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
-- ----------------------------------- Seth Kurtzberg M. I. S. Corp. 1-480-661-1849

David Sabel wrote:
The runtime system mostly has no concept of IO vs. non-IO evaluation. The IO monad is implemented as the following type:
newtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #)
which means that an IO action is simply a function from the world state to a pair of a new world state and a value. In other words, it's a straightfoward state monad, except that we use some optimised representations to eliminate some of the overhead of passing the state around.
Cheers, Simon
Ok so far, but when is the IO action performed?
Look at this small programm:
module Main(main) where
main = echoTwice
echo = getChar >>= putChar
echoTwice = echo >> echo
while executing: the program reads first two chararcters and then it writes the characters, but in my opinion it would be right to read one character, then write one characater, then read, then write. What's the reason for it?
Buffering.
If stdin/stdout correspond to a terminal, they are initially
line-buffered; otherwise, they are initially fully (block) buffered.
Also, if you're reading from a terminal, the terminal driver might
perform line buffering also (although, AFAICT, the run-time explicitly
turns it off).
You can change the buffering of stdin/stdout using IO.hSetBuffering.
The terminal buffering can be controlled using
PosixTTY.setTerminalAttributes.
BTW, this isn't a Haskell issue; you would see the same behaviour from
a program written in C.
--
Glynn Clements

"Glynn Clements"
David Sabel wrote:
...
Look at this small programm:
module Main(main) where
main = echoTwice
echo = getChar >>= putChar
echoTwice = echo >> echo
while executing: the program reads first two chararcters and then it
writes
the characters, but in my opinion it would be right to read one character, then write one characater, then read, then write. What's the reason for it?
Buffering.
A deficiency of the current implementation of the IO system, I'd say. Reading from stdin ought to force a stdout (and stderr) buffer flush. The GHC IO libs used to do this. This is constant source of confusion for users & turning off buffering alltogether is a poor substitute for having the IO system do the Right Thing under the hood. --sigbjorn

Sigbjorn Finne wrote:
Look at this small programm:
module Main(main) where
main = echoTwice
echo = getChar >>= putChar
echoTwice = echo >> echo
while executing: the program reads first two chararcters and then it writes the characters, but in my opinion it would be right to read one character, then write one characater, then read, then write. What's the reason for it?
Buffering.
A deficiency of the current implementation of the IO system, I'd say. Reading from stdin ought to force a stdout (and stderr) buffer flush. The GHC IO libs used to do this.
The equivalent ANSI C I/O functions typically flush either all buffered output streams or all line-buffered output streams whenever a read operation is passed to the underlying system call (but not when it can be satisified from the input stream's buffer). However, that won't make any noticeable difference unless the user explicitly puts the input stream into unbuffered mode. If the input stream is buffered, getChar doesn't (and shouldn't) return anything until Return (Enter, NewLine, ...) is pressed.
This is constant source of confusion for users & turning off buffering alltogether is a poor substitute for having the IO system do the Right Thing under the hood.
The "right" thing is a subjective notion. GHC already has some "magic"
behaviour in this regard; i.e. using:
hSetBuffering stdin NoBuffering
automatically changes the terminal settings (e.g. clearing the ICANON
flag) if stdin is associated with a terminal.
Even then, this won't "do the right thing" if the terminal itself (as
opposed to the driver) is line-buffered; nothing will.
A user who wants unbuffered input will probably argue that "the right
thing" is to make stdin unbuffered by default (along with clearing the
ICANON flag). OTOH, a user who wants line-buffered input (i.e. the
ability to use BackSpace/Ctrl-W/Ctrl-U) will probably disagree.
--
Glynn Clements

In addition, not all the target environments have the same capabilities. I/O may be from/to a file system, and so in systems with multiple file system types you can't even rely on a consistent set of capabilities at O/S granularity. This is a big problem for me as I'm implementing my database engine, which must force writes to disk for transactional semantics. The problem exists (as someone mentioned already) at the operating system level in many cases. Even if a consensus could be obtained about what the "Right Thing" is (not impossible at least in principle :) ) it is probably not possible to implement ANY policy consistently in all current target environments. On Monday 16 September 2002 21:41, Glynn Clements wrote:
Sigbjorn Finne wrote:
Look at this small programm:
module Main(main) where
main = echoTwice
echo = getChar >>= putChar
echoTwice = echo >> echo
while executing: the program reads first two chararcters and then it writes the characters, but in my opinion it would be right to read one character, then write one characater, then read, then write. What's the reason for it?
Buffering.
A deficiency of the current implementation of the IO system, I'd say. Reading from stdin ought to force a stdout (and stderr) buffer flush. The GHC IO libs used to do this.
The equivalent ANSI C I/O functions typically flush either all buffered output streams or all line-buffered output streams whenever a read operation is passed to the underlying system call (but not when it can be satisified from the input stream's buffer).
However, that won't make any noticeable difference unless the user explicitly puts the input stream into unbuffered mode. If the input stream is buffered, getChar doesn't (and shouldn't) return anything until Return (Enter, NewLine, ...) is pressed.
This is constant source of confusion for users & turning off buffering alltogether is a poor substitute for having the IO system do the Right Thing under the hood.
The "right" thing is a subjective notion. GHC already has some "magic" behaviour in this regard; i.e. using:
hSetBuffering stdin NoBuffering
automatically changes the terminal settings (e.g. clearing the ICANON flag) if stdin is associated with a terminal.
Even then, this won't "do the right thing" if the terminal itself (as opposed to the driver) is line-buffered; nothing will.
A user who wants unbuffered input will probably argue that "the right thing" is to make stdin unbuffered by default (along with clearing the ICANON flag). OTOH, a user who wants line-buffered input (i.e. the ability to use BackSpace/Ctrl-W/Ctrl-U) will probably disagree.
-- ----------------------------------- Seth Kurtzberg M. I. S. Corp. 1-480-661-1849

"Glynn Clements"
...chop, chop...
A deficiency of the current implementation of the IO system, I'd say. Reading from stdin ought to force a stdout (and stderr) buffer flush. The GHC IO libs used to do this.
The equivalent ANSI C I/O functions typically flush either all buffered output streams or all line-buffered output streams whenever a read operation is passed to the underlying system call (but not when it can be satisified from the input stream's buffer).
I'd settle for that kind of indiscriminate flushing -- as is, trivial I/O examples such as main = do putStr "What is your name? " ls <- getLine putStrLn ("Hello " ++ ls ++ "!") fail to behave as expected. --sigbjorn

I'm, analysing the IO-system of the GHC and need more information about it. More precisely: How switches the run-time-system between (normal) evaluation and performing of IO-actions?
I read the rts-paper, but didn't find any information about this.
Can somebody give a short review, or are ther any other papers available?
The runtime system mostly has no concept of IO vs. non-IO evaluation. The IO monad is implemented as the following type:
newtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #)
which means that an IO action is simply a function from the world state to a pair of a new world state and a value. In other words, it's a straightfoward state monad, except that we use some optimised representations to eliminate some of the overhead of passing the state around.
Cheers, Simon
In the GHC users guide Chapter 7.2.12 is the single primitve value realWorld# of the state of the world provided. This value is also used in the implementation of unsafePerformIO (GHC.IOBase), where it is applied as the argument to the IO action, and so on (after performing the action it's discarded). My question is now: Is the same value (realWorld#) applied while using normal monadic - not unsafe - IO, more precisely is a programm main::IO () executing the expression (main realWorld#)? David
participants (6)
-
David Sabel
-
David Sabel
-
Glynn Clements
-
Seth Kurtzberg
-
Sigbjorn Finne
-
Simon Marlow