The simplest thing could be strict ByteString IO:
import qualified Data.ByteString.Char8 as B main = do input <- B.readFile "file.txt" B.writeFile "file.txt" $ B.map toUpper input
As you can see, this is the same code, but with some features replaced by ByteString versions.
Lazy io
The problem you are facing is that some Haskell IO features use "Lazy IO", which has amazing semantics. In almost every program, I would avoid lazy I / O.
These days, people are looking for replacements for Lazy IOs such as Conduit and the like, and lazy IO is seen as an ugly hack that, unfortunately, is stuck in the standard library.
source share