
-- type TCPPortNumber = Word16 type IPAddress = Word32 data TCPPort = MkTCPPort IPAddress TCPPortNumber data TCPSocket = ...
openTCP :: TCPPort -> IO TCPSocket closeTCP :: TCPSocket -> IO () listenTCP :: TCPPortNumber -> (TCPConnection -> IO ()) -> IO () -- not sure about listenTCP
getRemoteAddressTCP :: TCPSocket -> IO TCPPort getLocalAddressTCP :: TCPSocket -> IO TCPPort
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 received by waittime -- set waittime to 0 to not wait but return only available bytes in buffer.
receiveWaitForeverTCP :: TCPSocket -> Integer -> IO (Maybe [Word8]) -- Same, with infinite wait-time. -- only useful if it can be interrupted by another thread
sendDataTCP :: TCPSocket -> [Word8] -> IO () sendEndTCP :: TCPSocket -> IO () --
OK, this is just off-the-cuff and probably has many flaws, and I offer it merely for discussion, not as a proposal. It does, however, represent the kind of basic functionality I'd look for, or abstract, if writing a TCP 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 hGetLine hGetContents ... instance Seekable FileHandle where { ... } instance IO FileHandle where { ... } instance IO Stream where { ... }
If programmers want to abstract away the differences between different types of network connections, well, this is Haskell so they can always write classes like this:
class (Monad m) => Closable s m | s -> m where close :: s -> m ()
class (Monad m) => Source s m d | s -> m d where read :: s -> Integer -> Integer -> m (Maybe [d]) readWaitForever :: s -> Integer -> m (Maybe [d])
class (Monad m) => Sink s m d | s -> m d where writeData :: s -> [d] -> m () writeEnd :: s -> m ()
write s Nothing = writeEnd s write s (Just d) = 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