In bash I can compose gzip and gunzip and get a command that outputs whatever I put into it.
> echo "test" | gzip | gunzip
test
I want to implement a function in Haskell that implements the same composition as above, using the input/output streams of the commands to compose them. My attempt is this:
module GZipID where
import System.Process
import System.IO
compress :: String -> IO String
compress input = do
(Just hin, Just hout, _, _) <- createProcess (proc "gzip" []) { std_in = CreatePipe
, std_out = CreatePipe
}
hPutStr hin input
(_, Just hfin, _, _) <- createProcess (proc "gunzip" []) { std_in = UseHandle hout
, std_out = CreatePipe
}
hGetContents hfin
However, when I load this up into GHCi and feed it "test" as input, it never terminates. There is some part of the process management that I don't seemt to understand. I tried to flush hin after hPutStr hin, but that changed nothing. Do I perhaps need to coerce the output to move through the hout pipe into gunzip?
The problem is that the
hinhandle is never EOF'd/closed, sogzipkeeps waiting for more input and never terminates, sogunzipnever terminates either, sohGetContentsnever stops waiting for results to come back, sohinnever goes out of scope (which would close it).You could still get lazy strings through though with the right buffering settings, but that's fiddly and unsafe, let's not go there.
The solution I'd suggest is to close the handle manually:
Of course, a better option would be to use a library that dispenses with the manual handle handling. Conduit should be able to do this nicely.