
On Sat, Nov 27, 2010 at 10:22:39AM +0100, Tim Baumgartner wrote:
Hi Haskellers,
in order to learn Monad Transformers, I'm trying to write a simple calculator that has an internal state of type Calc that is modified after each line the user enters. I started like this:
main :: IO () handleLine :: StateT Calc IO Bool -- return's False in order to exit
main uses runState in order to extract the action it needs from the transformer. Up to this point, it works fine. But in order to separate I/O and parsing, I'd prefer to have another function
processInput :: String -> State Calc (Maybe String)
Separating I/O and parsing like this is a great idea. Unfortunately, from an engineering point of view, making different monad stacks work together can be difficult (as you have discovered). As I see it you have three options (in increasing order of both desirability and difficulty): 1) Give up separating I/O and parsing, and just rewrite processInput to be in the StateT Calc IO monad. 2) Write an adapter function withoutIO :: State s a -> StateT s IO a which makes a State s computation into a StateT s IO computation (which is guaranteed to do no I/O). The implementation of withoutIO will probably involve runState, but it will be able to properly thread the state through from previous computations. 3) Rewrite everything in a "capabilities" style, e.g. instead of the concrete type StateT Calc IO Bool you would have (MonadState Calc m, MonadIO m) => m Bool and so on, which allows for much easier mixing of different concrete monad stacks. Ultimately, (3) seems to me like the "right" way to do this sort of thing. It definitely runs into limitations as well, although many of them can be mitigated by technology found in the Monatron package (which unfortunately at the moment is woefully undocumented). -Brent