
Roman Cheplyaka wrote:
Not really. You are passing 'ref' into myReadFile by hand here. If you are willing to pass parameters by hand, there is no need in IORef at all; you could just as well pass header directly.
I confess in embellishing the problem and tacitly upgrading Reader to the State. The original problem seemed too simple. In penance, I show the solution to the original problem, using exactly the signature of the original poster, and using exactly his code for myReadFile, (but with the lifted type, which is what he wanted, it seems).
data ColumnHeaders = FirstLine | None getFileContents :: ReaderT ColumnHeaders IO String getFileContents = do header <- ask -- can ask it here lift $ withCSV "data.csv" (\handle -> runReaderT (myReadFile handle) header) where
myReadFile :: Handle -> ReaderT ColumnHeaders IO String myReadFile handle = do header <- ask -- Now, we can ask it, alright case header of None -> return "" FirstLine -> lift $ hGetLine handle -- skip first line text <- lift $ hGetContents handle return text
The idea is simple: since withCSV wants its callback to be IO rather than ReaderT IO, it means the withCSV is not capable of altering the environment of the callback. Therefore, the myReadFile is executed in the same environment, regardless of the Handle. Once we understand this, the solution is trivial. So, to answer the question of the original poster: it seems we can do what he wants withCSV he got. There is no need to ask withCSV author for anything.
I agree that *given a ReaderT* (or implicit params, or your implicit configurations), you can emulate StateT/WriterT using IORefs (but only one way of stacking w.r.t. exceptions).
Actually, it is easy to get other ways of `stacking with respect to exceptions.' Once we get persistent state (which is what IORef provide), we can always discard that state by writing an appropriate exception handler. I guess I can make another stab at monad transformers and say that thinking in terms of stacks is not always productive.