Patrick Collins
Patrick Collins

Reputation: 10584

Can't write to a Handle

I'm writing a chat server in Haskell. My executable has the following code:

data Client = Client {idx :: Int, hClient :: Handle} deriving (Show, Eq)

tellClient :: String -> Client -> IO ()
tellClient = flip $ hPutStrLn . hClient

askClient :: Client -> IO (String)
askClient = hGetLine . hClient

which I expect to be able to use to write and read from a file. I'm writing tests against this code with HSpec. I have the following function to build temporary Client objects (which relies on the "temporary") library:

withTempClient :: Int -> (Client -> IO a) -> IO a
withTempClient cIdx f = withTempFile "tests/generated" "clientFile.tmp" axn
  where axn _ handle = f $ Client cIdx handle

I'm using this to test the code above as follows:

main = hspec $  do
  describe "client IO" $ do
    it "can ask and tell" $ withTempClient 1 $ \c -> do
      tellClient "hello" c
      hFlush $ hClient c
      askClient c `shouldReturn` "hello"

But the test fails with the following error:

   uncaught exception: IOException of type EOF (test/generated
   /clientFile11587.tmp: hGetLine: end of file)

I thought that maybe I was doing something wrong in withTempClient, so I added the following test case:

it "opens a handle corectly" $ withTempClient 1 $ \(Client _ h) -> do
  hIsOpen h `shouldReturn` True
  hIsWritable h `shouldReturn` True

but that passed, so I'm not sure what the issue could be. What am I missing?

Upvotes: 3

Views: 155

Answers (1)

gallais
gallais

Reputation: 12103

When you write to a file, the OS keeps track of the position at which you are in said file. You get an EOF error because you're at the end of the file (it was empty to begin with and everything you have written is before the current position).

To fix that you need to use hSeek to reposition yourself at the beginning of the file like so:

hSeek (hClient c) AbsoluteSeek 0

For more details about seeking, cf. this bit of real world haskell.

Upvotes: 4

Related Questions