MissingH bracketCD (aka bracketCWD) bug -- this is the infamous lazy io, right?

I got bitten by a bug (well, I call it bug) in bracketCD from HSH/MissingH demonstrated by the following code bracketCD is very useful for sysadminny one-offs, I use it all the time, but..... I suspect that unless people are very careful, this behavior will affect other users of bracketCD, in potentially very subtle and tricky ways. 1) -- is there a more elegant way to deal with lazy io than the hack below? (putStrLn the last character) 2) -- should MissingH function bracketCD be fixed, and if so how? import System.FilePath.Find (find) import System.Directory (getDirectoryContents, getCurrentDirectory, setCurrentDirectory) import System.Path (bracketCWD) -- from MissingH -- the fixed function, a more restrictive type than bracketCWD which lets you do any io, not just showables myBracketCWD :: Show a => FilePath -> IO a -> IO a myBracketCWD fp action = do oldcwd <- getCurrentDirectory setCurrentDirectory fp a <- action putStrLn $ show . last . show $ a -- force evaluation setCurrentDirectory oldcwd return a -- rather than listing "/", lists dir this module is in tWrong = bracketCWD "/" $ return . take 2 =<< listCurrentWithFind -- this lists filesystem root tRight = myBracketCWD "/" $ return . take 2 =<< listCurrentWithFind listCurrentWithFind = System.FilePath.Find.find (return True) (return True) "." -- for some reason, bracketCWD does the right thing with get tGetContents = myBracketCWD "/" $ return . take 2 =<< getDirectoryContents "." Actual code for bracketCWD in MissingH: -- this is from MissingH, seems there's a bug when attempt to wrap a find command bracketCWD :: FilePath -> IO a -> IO a bracketCWD fp action = do oldcwd <- getCurrentDirectory setCurrentDirectory fp finally action (setCurrentDiurectory oldcwd)

Excerpts from Thomas Hartman's message of Mon Mar 23 09:08:41 +0100 2009:
I got bitten by a bug (well, I call it bug) in bracketCD from HSH/MissingH demonstrated by the following code
bracketCD is very useful for sysadminny one-offs, I use it all the time, but..... I suspect that unless people are very careful, this behavior will affect other users of bracketCD, in potentially very subtle and tricky ways.
1) -- is there a more elegant way to deal with lazy io than the hack below? (putStrLn the last character)
Yes without changing the bracketCD function you can use a strict 'return' function to help you avoid leaks. return' :: (Monad m, NFData sa) -> sa -> m sa return' x = rnf x `seq` return x
2) -- should MissingH function bracketCD be fixed, and if so how?
Not completely while staying in the full 'IO' monad. Have a look at the strict-io package [1] that goes in this direction. myBracketCWD :: (NFData sa, Show sa) => FilePath -> SIO sa -> IO sa myBracketCWD fp action = do oldcwd <- getCurrentDirectory setCurrentDirectory fp a <- SIO.run action rnf a `seq` setCurrentDirectory oldcwd return a The same function could be more deeply in the 'SIO' monad by returning in the 'SIO' monad. However this would require to first wrap getCurrentDirectory and setCurrentDirectory in the 'SIO' monad. Best regards, [1]: http://hackage.haskell.org/cgi-bin/hackage-scripts/package/strict-io -- Nicolas Pouillard
participants (2)
-
Nicolas Pouillard
-
Thomas Hartman