I Hate IO
Simon Marlow
simonmar@microsoft.com
Fri, 10 Aug 2001 14:26:33 +0100
> --
> type TCPPortNumber =3D Word16
> type IPAddress =3D Word32
> data TCPPort =3D MkTCPPort IPAddress TCPPortNumber
> data TCPSocket =3D ...
>=20
> openTCP :: TCPPort -> IO TCPSocket
> closeTCP :: TCPSocket -> IO ()
> listenTCP :: TCPPortNumber -> (TCPConnection -> IO ()) -> IO ()
> -- not sure about listenTCP
>=20
> getRemoteAddressTCP :: TCPSocket -> IO TCPPort
> getLocalAddressTCP :: TCPSocket -> IO TCPPort
>=20
> receiveTCP :: TCPSocket -> Integer -> Integer -> IO (Maybe [Word8])
> -- "receiveTCP socket waittime arraylength"
> -- return "Nothing" if received end with no octets pending
> -- return "Just array" where array is as many octets up to length as=20
> received by waittime
> -- set waittime to 0 to not wait but return only available bytes in=20
> buffer.
>=20
> receiveWaitForeverTCP :: TCPSocket -> Integer -> IO (Maybe [Word8])
> -- Same, with infinite wait-time.
> -- only useful if it can be interrupted by another thread
>=20
> sendDataTCP :: TCPSocket -> [Word8] -> IO ()
> sendEndTCP :: TCPSocket -> IO ()
> --
>=20
> OK, this is just off-the-cuff and probably has many flaws,=20
> and I offer it=20
> merely for discussion, not as a proposal. It does, however,=20
> represent the=20
> kind of basic functionality I'd look for, or abstract, if=20
> writing a TCP=20
> application.
There's lots of useful shared functionality between file I/O and socket
I/O. For example: buffering/flushing and lazy I/O (getContents) are
shared for files and sockets in GHC, I wouldn't want to have to provide
N separate implementations of these things for the various different
types of streams and files.
But there is a big difference when it comes to seeking, which is why we
have hIsSeekable.
I guess it just comes down to how much there is in common between I/O on
files and I/O on streams. I think I like using the same API for both.
But it might be nice to abstract the interface a little, something like
class Seekable h where
hSeek :: h -> SeekMode -> Integer -> IO ()
class IO h where
hPutStr=20
hGetLine
hGetContents
...
instance Seekable FileHandle where { ... }
instance IO FileHandle where { ... }
instance IO Stream where { ... }
> If programmers want to abstract away the differences between=20
> different=20
> types of network connections, well, this is Haskell so they=20
> can always=20
> write classes like this:
>
> class (Monad m) =3D> Closable s m | s -> m where
> close :: s -> m ()
>=20
> class (Monad m) =3D> Source s m d | s -> m d where
> read :: s -> Integer -> Integer -> m (Maybe [d])
> readWaitForever :: s -> Integer -> m (Maybe [d])
>=20
> class (Monad m) =3D> Sink s m d | s -> m d where
> writeData :: s -> [d] -> m ()
> writeEnd :: s -> m ()
>=20
> write s Nothing =3D writeEnd s
> write s (Just d) =3D writeData s d
Does doing I/O in anything other than the IO monad make sense?
Why isn't writeEnd the same as close?
Is there anything that is always a Source but not a Sink (or vice
versa)? Why separate these two classes?
Cheers,
SImon