RE: Raw I/O library proposal, second (more pragmatic) draft

fileRead :: File -> FileOffset -> Integer -> Buffer -> IO () fileGet :: File -> FileOffset -> Integer -> IO ImmutableBuffer
Should fileRead return the number of bytes read, in case of asking for more bytes than are in the file? With fileGet you can simply look at the size of the return buffer.
Good point.
-- | Flushes the buffer to the operating system for an output -- buffer, or discards buffered data for an input buffer. flush :: s -> IO ()
Those are two very different things, aren't they? The first pushes data along the pipe (rather than discarding it). The second discards data, equivalent to calling streamGetAvailable and ignoring the result.
It's not quite the same as calling streamGetAvailable. flush would discard data that is buffered on the Haskell side, whereas streamGetAvailable might grab any data that is buffered in the OS. I'm not sure whether this is useful or not, but it's exactly what hFlush does currently.
-- | Flushes the buffered data as far as possible, even to the -- physical media if it can. It returns 'True' if the data -- has definitely been flushed as far as it can go: to the -- disk for a disk file, to the screen for a terminal, and so on. sync :: s -> IO Bool
This one seems to be output only. Would it ever be meaningful to call sync on an input-stream?
You're right. If flush was moved to OutputStream, then it would make sense to move sync there too.
class InputStream s where class OutputStream s where
Presumably you wanted Stream superclasses for these?
Not necessarily - there's no need for it, other than to reduce the size of contexts on types.
streamGet :: s -> IO Word8
What does this return after the last byte has been read? What does this return for an empty file?
Exactly as hGetChar, that is it throws an exception at the end of the file.
Parameterise InputStream over the element type too, so we can combine InputStream and TextInputStream? NO: uses multiparam type classes.
Unless of course your stream type is a type constructor:
class InputStream s where streamGet :: s m e -> m e ...
I can see the advantage in abstracting over the element type (in fact, I think I'll explore that). What would be the motivation for abstracting over the monad type? IMHO, it has to be done consistently or not at all. That is, *all* our IO code should be abstracted over the monad, and I don't think that's a good idea. Cheers, Simon

On Thu, 7 Aug 2003, Simon Marlow wrote:
It's not quite the same as calling streamGetAvailable. flush would discard data that is buffered on the Haskell side, whereas streamGetAvailable might grab any data that is buffered in the OS.
I'm not sure whether this is useful or not, but it's exactly what hFlush does currently.
Here's the code for hFlush from the GHC 6.0 sources: --------------------------------------------------------------------------- -- hFlush -- The action `hFlush hdl' causes any items buffered for output -- in handle `hdl' to be sent immediately to the operating -- system. hFlush :: Handle -> IO () hFlush handle = wantWritableHandle "hFlush" handle $ \ handle_ -> do buf <- readIORef (haBuffer handle_) if bufferIsWritable buf && not (bufferEmpty buf) then do flushed_buf <- flushWriteBuffer (haFD handle_) (haIsStream handle_) buf writeIORef (haBuffer handle_) flushed_buf else return () I'm not sure what it does to input buffers, but it doesn't seem like it's designed to do anything to them. -- Ben

On Thu, Aug 07, 2003 at 10:13:12AM +0100, Simon Marlow wrote:
Unless of course your stream type is a type constructor:
class InputStream s where streamGet :: s m e -> m e ...
I can see the advantage in abstracting over the element type (in fact, I think I'll explore that).
What would be the motivation for abstracting over the monad type? IMHO, it has to be done consistently or not at all. That is, *all* our IO code should be abstracted over the monad, and I don't think that's a good idea.
ArrayStreams could be implemented in the state monad. Not sure how useful this is, but I imagine people would use it if it were available. John -- --------------------------------------------------------------------------- John Meacham - California Institute of Technology, Alum. - john@foo.net ---------------------------------------------------------------------------

In article
<3429668D0E777A499EE74A7952C382D1A5E237@EUR-MSG-01.europe.corp.microsoft
.com>,
"Simon Marlow"
What would be the motivation for abstracting over the monad type?
It might be useful to use streams with other monads, such as lifted IO monads, or simple state monads such as "s -> (s,a)". The idea is that your stream classes might be useful in other contexts.
IMHO, it has to be done consistently or not at all. That is, *all* our IO code should be abstracted over the monad,
Not necessarily. It could just be the three classes that are general, and the types are specific to whatever monad. There's no doubt that opening a file is an IO-based action, it could never safely be done in the Maybe monad. The classes, however, are patterns that might apply to many situations. The downside is that all the stream types have to have a dummy monad parameter, which might be considered slightly ugly. It's a trade-off, and given that we're constrained by Haskell 98's lack of multiparameter type-classes, I can see arguments on both sides of it. I also think the data-structure idea might be worth examining, it seems to me it's basically making the class context dictionary explicit. But it seems Haskell libraries don't do a lot of that for some reason. Probably the big downside is that it's hard to extend later -- your fileOutputStream function has to return a structure containing everything that can be done with the stream. You can't add new functionality to a FileOutputStream later on, as you could if it were an opaque type that were a member of a class. -- Ashley Yakeley, Seattle WA
participants (4)
-
Ashley Yakeley
-
Ben Rudiak-Gould
-
John Meacham
-
Simon Marlow