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