[Haskell-beginners] Learning StateT
Brent Yorgey
byorgey at seas.upenn.edu
Sat Nov 27 08:35:30 EST 2010
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
More information about the Beginners
mailing list