
Dimitri DeFigueiredo wrote:
Imagine I need to read a .CSV file which may or may not contain column titles on its first line. I'm not interested in the column titles, I just want the rest of the file. I am provided a library function to read the contents of the file (using a "callback"). The library author provided this function in the IO monad.
withCSV :: FilePath -> (Handle -> IO r) -> IO r withCSV path action = do putStrLn "opening file" h <- openFile path ReadWriteMode r <- action h hClose h putStrLn "file closed" return r
The problem arises because I also want to use the ReaderT monad transformer. My environment information will tell me whether or not to disregard the first (i.e. column title) line.
For this particular example, the answer is easy: If you have the IO monads, you have the Reader monad, and the State/Writer as well. This is just an IORef.
getFileContents :: IO String getFileContents = do ref <- newIORef False withCSV "data.csv" (myReadFile ref) where myReadFile :: IORef Bool -> Handle -> IO String myReadFile ref handle = do header <- readIORef ref -- ask --- OOOPPSss!!! FAIL! Can't ask. case header of False -> return "" True -> hGetLine handle -- skip first line text <- hGetContents handle return text
Using just IO, you can get State, Reader, Writer, and Exception (and in a modular way!) As to you general question, I will and do advocating abandoning monad transformers altogether. The paper on extensible effects demonstrated a new solution to the MonadCatchIO problem that solved the long-standing issue of discarding state upon exception. See Sec 6 of http://okmij.org/ftp/Haskell/extensible/more.pdf