This is a contrieved example using Reader Transformer:
{-# LANGUAGE UnicodeSyntax #-}
import Control.Monad.Reader
import Data.Char
conv ∷ Int → ReaderT Char Maybe String
conv n = do
yn ← ask
if yn == 'y'
then return $ chr n : " with Yes"
else lift Nothing
-- runReaderT (conv 98) 'y' Just "b with Yes"
-- runReaderT (conv 98) '#' Nothing
inspect ∷ ReaderT Char Maybe String → Bool
-- EDITED: as per suggestions, the correct type is monadic:
inspect ∷ ReaderT Char Maybe String → ReaderT Char Maybe Bool
inspect is supposed to check whether the value inside is Nothing. Can it be done, or I am having "design issues"?
ReaderT is exactly
newtype ReaderT r m a = ReaderT { runReaderT :: r -> m a }
-- a function that reads the environment^ |
-- and returns an m a^
For ReaderT r Maybe a it's a function that reads the environment and returns a Maybe a. You can make a function that reads the environment and checks if the result is Nothing by composing this function with another function that checks whether the result is Nothing. To check whether a Maybe a is nothing, we can use isJust, and to pack the resulting Bool back into a Maybe Bool we'd use return.
inspect :: ReaderT r Maybe a -> ReaderT r Maybe Bool
inspect (ReaderT f) = ReaderT $ return . isJust . f
transformers provides a function, mapReaderT that lets us manipulate the computation inside a ReaderT
mapReaderT :: (m a -> n b) -> ReaderT r m a -> ReaderT r n b
mapReaderT f m = ReaderT $ f . runReaderT m
mapReaderT just composes the function provided as its first argument with the function inside the ReaderT (runReaderT unwraps the function inside a ReaderT). You can use mapReaderT to write inspect more elegantly.
inspect :: ReaderT r Maybe a -> ReaderT r Maybe Bool
inspect = mapReaderT (return . isJust)
Related
I am currently playing with the Bryan O'Sullivan's resource-pool library and have a question regarding extending the withResource function.
I want to change the signature of the withResource function from (MonadBaseControl IO m) => Pool a -> (a -> m b) -> m b to (MonadBaseControl IO m) => Pool a -> (a -> m (Bool, b)) -> m b.
What I want to achieve is, that the action should return (Bool, b) tuple, where the boolean value indicates if the borrowed resource should
be put back into the pool or destroyed.
Now my current implementation looks like this:
withResource :: forall m a b. (MonadBaseControl IO m) => Pool a -> (a -> m (Bool, b)) -> m b
{-# SPECIALIZE withResource :: Pool a -> (a -> IO (Bool,b)) -> IO b #-}
withResource pool act = fmap snd result
where
result :: m (Bool, b)
result = control $ \runInIO -> mask $ \restore -> do
resource <- takeResource pool
ret <- restore (runInIO (act resource)) `onException`
destroyResource pool resource
void . runInIO $ do
(keep, _) <- restoreM ret :: m (Bool, b)
if keep
then liftBaseWith . const $ putResource pool resource
else liftBaseWith . const $ destroyResource pool resource
return ret
And I have a feeling, that this is not how it is supposed to look like...
Maybe I am not using the MonadBaseControl API right.
What do you guys think of this and how can I improve it to be more idiomatic?
I have a feeling that there is a fundamental problem with this approach. For monads for which StM M a is equal/isomorphic to a it will work. But for other monads there will be a problem. Let's consider MaybeT IO. An action of type a -> MaybeT IO (Bool, b) can fail, so there will be no Bool value produced. And the code in
void . runInIO $ do
(keep, _) <- restoreM ret :: m (Bool, b)
...
won't be executed, the control flow will stop at restoreM. And for ListT IO it'll be even worse, as putResource and destroyResource will be executed multiple times. Consider this sample program, which is a simplified version of your function:
{-# LANGUAGE FlexibleContexts, ScopedTypeVariables, RankNTypes, TupleSections #-}
import Control.Monad
import Control.Monad.Trans.Control
import Control.Monad.Trans.List
foo :: forall m b . (MonadBaseControl IO m) => m (Bool, b) -> m b
foo act = fmap snd result
where
result :: m (Bool, b)
result = control $ \runInIO -> do
ret <- runInIO act
void . runInIO $ do
(keep, _) <- restoreM ret :: m (Bool, b)
if keep
then liftBaseWith . const $ putStrLn "return"
else liftBaseWith . const $ putStrLn "destroy"
return ret
main :: IO ()
main = void . runListT $ foo f
where
f = msum $ map (return . (, ())) [ False, True, False, True ]
It'll print
destroy
return
destroy
return
And for an empty list, nothing gets printed, which means no cleanup would be called in your function.
I have to say I'm not sure how to achieve your goal in a better way. I'd try to explore in the direction of signature
withResource :: forall m a b. (MonadBaseControl IO m)
=> Pool a -> (a -> IO () -> m b) -> m b
where the IO () argument would be a function, that when executed, invalidates the current resource and marks it to be destroyed. (Or, for better convenience, replace IO () with lifted m ()). Then internally, as it's IO-based, I'd just create a helper MVar that'd be reset by calling
the function, and at the end, based on the value, either return or destroy the resource.
Monad transformers are tricky, and I'm not sure (= don't have good intuition) which one should go on top.
StateT s (ExceptT e m)
This says:
Start with m
Add exceptions to that
Add state to that
Now, 'adding exceptions' means your actions can terminate in two ways: either with a normal return value, or with an exception.
'Adding state' means that an extra bit of state output gets included in the normal return values.
So in StateT s (ExceptT e m), you only get a result state if there is no exception.
On the other hand,
ExceptT e (StateT s m)
says:
Start with m
Add state to that
Add exceptions to that
'Adding state' means that an extra bit of state output gets included in the return values of m.
But now, your added exceptions get added as an alternative return value inside the StateT monad. So you always get a state output, and then you may get a normal return value or you may get an exception along with it.
I answer this myself, but other answers are welcome!
Consider the example:
#!/usr/bin/env stack
-- stack runghc --package mtl
{-# LANGUAGE FlexibleContexts #-}
module Main (main) where
import Control.Applicative
import Control.Monad.State
import Control.Monad.Error
import Control.Monad.Trans.Except
import Data.Functor.Identity
test1 :: (MonadState Int m, MonadError String m) => m Bool
test1 = do
put 1
throwError "foobar"
put 2
return False
test2 :: (Alternative m, MonadState Int m, MonadError String m) => m Bool
test2 = do
put 4
test1 <|> return True
runStateExceptT :: Monad m => s -> ExceptT e (StateT s m) a -> m (Either e a, s)
runStateExceptT s = flip runStateT s . runExceptT
runExceptStateT :: Monad m => s -> StateT s (ExceptT e m) a -> m (Either e (a, s))
runExceptStateT s = runExceptT . flip runStateT s
main :: IO ()
main = do
print $ runIdentity . runStateExceptT 3 $ test1
print $ runIdentity . runExceptStateT 3 $ test1
print $ runIdentity . runStateExceptT 3 $ test2
print $ runIdentity . runExceptStateT 3 $ test2
It will print:
(Left "foobar",1)
Left "foobar"
(Right True,1)
Right (True,4)
With ExceptT outside, you'll still get the state which was at the moment of "throwing an error". This is probably what you want.
Remember that this combination resembles imperative programming a lot. One should think about exception safety practicies, i.e. must be careful about when to throwError!
Why does this function have the type:
deleteAllMp4sExcluding :: [Char] -> IO (IO ())
instead of deleteAllMp4sExcluding :: [Char] -> IO ()
Also, how could I rewrite this so that it would have a simpler definition?
Here is the function definition:
import System.FilePath.Glob
import qualified Data.String.Utils as S
deleteAllMp4sExcluding videoFileName =
let dirGlob = globDir [compile "*"] "."
f = filter (\s -> S.endswith ".mp4" s && (/=) videoFileName s) . head . fst
lst = f <$> dirGlob
in mapM_ removeFile <$> lst
<$> when applied to IOs has type (a -> b) -> IO a -> IO b. So since mapM_ removeFile has type [FilePath] -> IO (), b in this case is IO (), so the result type becomes IO (IO ()).
To avoid nesting like this, you should not use <$> when the function you're trying to apply produces an IO value. Rather you should use >>= or, if you don't want to change the order of the operands, =<<.
Riffing on sepp2k's answer, this is an excellent example to show the difference between Functor and Monad.
The standard Haskell definition of Monad goes something like this (simplified):
class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
However, this is not the only way the class could have been defined. An alternative runs like this:
class Functor m => Monad m where
return :: a -> m a
join :: m (m a) -> m a
Given that, you can define >>= in terms of fmap and join:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
ma >>= f = join (f <$> ma)
We'll look at this in a simplified sketch of the problem you're running into. What you're doing can be schematized like this:
ma :: IO a
f :: a -> IO b
f <$> ma :: IO (IO b)
Now you're stuck because you need an IO b, and the Functor class has no operation that will get you there from IO (IO b). The only way to get where you want is to dip into Monad, and the join operation is precisely what solves it:
join (f <$> ma) :: IO b
But by the join/<$> definition of >>=, this is the same as:
ma >>= f :: IO a
Note that the Control.Monad library comes with a version of join (written in terms of return and (>>=)); you could put that in your function to get the result you want. But the better thing to do is to recognize that what you're trying to do is fundamentally monadic, and thus that <$> is not the right tool for the job. You're feeding the result of one action to another; that intrinsically requires you to use Monad.
module Main (main) where
import Control.Monad.Reader
p1 :: String -> IO ()
p1 = putStrLn . ("Apple "++)
p2 :: String -> IO ()
p2 = putStrLn . ("Pear "++)
main :: IO ()
main = do
p1 "x"
p2 "y"
r "z"
r :: String -> IO ()
r = do
p1
p2
It prints:
Apple x
Pear y
Pear z
Why?
The problem is in r. Given the following definition of Reader monad:
instance Monad ((->) e) where
return = const
f >>= g = \x -> g (f x) x
We can simplify r:
r = p1 >> p2
= (>>=) p1 (\_ -> p2)
= (\f g x -> g (f x) x) p1 (\_ -> p2)
= \x -> (\_ -> p2) (p1 x) x
= \x -> p2 x
This also shows that Reader's (>>) is just const with a bit more specific type.
If you want to distribute the environment and then execute both actions, you have to bind the result of applying p1 to the environment, for example:
r = do a1 <- p1
a2 <- p2
return (a1 >> a2)
Or using Applicative:
r = (>>) <$> p1 <*> p2
Expanding on the Reader part, Control.Monad.Reader provides three variants of Reader.
the implicit (->) e, which is what the function r uses
the monad transformer ReaderT e m, a newtype wrapper for functions of type e -> m a
the explicit Reader e, defined in terms of ReaderT as ReaderT e Identity
Without any further information, the implicit (->) e will be used. Why?
The overall type of do block is given by the last expression, which is also constrained to be of the form Monad m => m a for some m and a.
Looking back at r, it's clear that the do block has a type String -> IO () as given by the type of r and also p2. It also requires String -> IO () to be Monad m => m a. Now, unifying these two types:
m = (->) String
a = IO ()
This matches (->) e monad instance by choosing e = String.
Being a monad transformer, ReaderT takes care of the inner plumbing to make sure the actions of the inner monad are properly sequenced and executed. To select ReaderT, it is necessary to explicitly mention it (usually in a type signature, but functions which fix the type to be ReaderT, such as runReaderT, also work):
r :: ReaderT String IO ()
r = do ? p1
? p2
r' :: String -> IO ()
r' = runReaderT r
This comes with another problem, p1 and p2 have a type String -> IO (), which doesn't match the required ReaderT String IO ().
The ad-hoc solution (tailored exactly for this situation), is just to apply
ReaderT :: (e -> m a) -> ReaderT e m a
To obtain something more general, MonadIO type class can lift IO actions into the transformer and MonadReader type class allows accessing the environment. These two type classes work as long as there is IO (or ReaderT respectively) somewhere in the transformer stack.
lift' :: (MonadIO m, MonadReader a m) => (a -> IO b) -> m b
lift' f = do
env <- ask -- get environment
let io = f env -- apply f to get the IO action
liftIO io -- lift IO action into transformer stack
Or more concisely:
lift' f = ask >>= liftIO . f
Regarding your question in comments, you can implement the relevant instances in this way:
newtype ReaderT e m a = ReaderT { runReaderT :: e -> m a }
instance Monad m => Monad (ReaderT e m) where
return = ReaderT . const . return
-- The transformers package defines it as "lift . return".
-- These two definitions are equivalent, though.
m >>= f = ReaderT $ \e -> do
a <- runReaderT m e
runReaderT (f a) e
instance Monad m => MonadReader e (ReaderT e m) where
ask = ReaderT return
local f m = ReaderT $ runReaderT m . f
reader f = ReaderT (return . f)
The actual typeclass can be found in the mtl package (package, type class), the newtype and Monad instance in transformers package (package, type class).
As for making a e -> m a Monad instance, you are out of luck. Monad requires a type constructor of kind * -> *, which means we are attempting to do something like this (in pseudo-code):
instance Monad m => Monad (/\a -> e -> m a) where
-- ...
where /\ stands for type-level lambda. However, the closest thing we can get to a type level lambda is a type synonym (which must be fully applied before we can make type class instances, so no luck here) or a type family (which cannot be used as an argument to type class either). Using something like (->) e . m leads to newtype again.
Let's first rewrite the body of
r :: String -> IO ()
r = do
p1
p2
using (>>),
r = p1 >> p2
so p1 must have type m a for some Monad m, and p2 must have type m b for the same m.
Now,
p1, p2 :: String -> IO ()
and the top-level type constructor in that is the function arrow (->). Therefore the Monad used in r must be
(->) String
The Monad instance for (->) e [aka the reader monad], is
instance Monad ((->) e) where
-- return :: a -> (e -> a)
return = const
-- (>>=) :: (e -> a) -> (a -> (e -> b)) -> (e -> b)
f >>= g = \x -> g (f x) x
and consequently,
p1 >> p2 = p1 >>= \_ -> p2
= \x -> (\_ -> p2) (p1 x) x -- apply (\_ -> p2) to (p1 x)
= \x -> p2 x -- eta-reduce
= p2
so that was just a complicated way to write
r = p2
For r you used (->) String (IO ()) which is a Monad ((->) String) that returns a value of type IO ().
You did NOT use a ReaderT or any monad transformer. You used a monad that returns a different monad. It accidentally compiled and ran, almost doing what you expected.
You need to use runReaderT and lift (or liftIO) to achieve the r that I think you are trying to make.
You left off the argument when you invoke p1 and p2 in r. What you wrote is then interpreted as pointfree notation, so only the second IO action gets an argument. This works:
r :: String -> IO ()
r x = do
p1 x
p2 x
To understand why this is happening, consider that what you originally wrote is equivalent to
r = p1 >> p2
The compiler interprets that as something like
r x = (p1 >> p2) x
Which isn't what you want.
I am attempting to create a stack of monad transformers and am having trouble getting the correct type signatures for my functions. (I'm still pretty new to Haskell)
The stack combines multiple StateT transformers since I have multiple states I need to keep track of (two of which could be tupled, but I'll get to that in a second) and a WriterT for logging.
Here's what I have so far:
module Pass1 where
import Control.Monad.Identity
import Control.Monad.State
import Control.Monad.Writer
import Data.Maybe
import qualified Data.Map as Map
import Types
data Msg = Error String
| Warning String
type Pass1 a = WriterT [Msg] (StateT Int (StateT [Line] (StateT [Address] Identity))) a
runPass1 addrs instrs msgs = runIdentity (runStateT (runStateT (runStateT (runWriterT msgs) 1) instrs) addrs)
--popLine :: (MonadState s m) => m (Maybe s)
--popLine :: (Monad m) => StateT [Line] m (Maybe Line)
popLine :: (MonadState s m) => m (Maybe Line)
popLine = do
ls <- get
case ls of
x:xs -> do
put xs
return $ Just x
[] -> return Nothing
incLineNum :: (Num s, MonadState s m) => m ()
incLineNum = do
ln <- get
put $ ln + 1
curLineNum :: (MonadState s m) => m s
curLineNum = do
ln <- get
return ln
evalr = do l <- popLine
--incLineNum
return l
I would like the popLine to mess with the [Line] state and the xLineNum functions to affect the Int state. evalr is the computation which will be passed to runPass1.
Whenever I load the code I run into errors which are generally of the following variety:
Pass1.hs:23:14:
No instance for (MonadState [t] m)
arising from a use of `get' at Pass1.hs:23:14-16
Possible fix: add an instance declaration for (MonadState [t] m)
In a stmt of a 'do' expression: ls <- get
In the expression:
do ls <- get
case ls of {
x : xs -> do ...
[] -> return Nothing }
In the definition of `popLine':
popLine = do ls <- get
case ls of {
x : xs -> ...
[] -> return Nothing }
Pass1.hs:22:0:
Couldn't match expected type `s' against inferred type `[Line]'
`s' is a rigid type variable bound by
the type signature for `popLine' at Pass1.hs:21:23
When using functional dependencies to combine
MonadState [Line] m,
arising from a use of `get' at Pass1.hs:23:14-16
MonadState s m,
arising from the type signature for `popLine'
at Pass1.hs:(22,0)-(28,31)
When generalising the type(s) for `popLine'
Pass1.hs:23:14:
Could not deduce (MonadState [Line] m)
from the context (MonadState s m)
arising from a use of `get' at Pass1.hs:23:14-16
Possible fix:
add (MonadState [Line] m) to the context of
the type signature for `popLine'
or add an instance declaration for (MonadState [Line] m)
In a stmt of a 'do' expression: ls <- get
In the expression:
do ls <- get
case ls of {
x : xs -> do ...
[] -> return Nothing }
In the definition of `popLine':
popLine = do ls <- get
case ls of {
x : xs -> ...
[] -> return Nothing }
None of the signatures seem to be correct, but popLine is the first function so it's the only one that immediately causes an error.
I try adding what it suggests in the type signature (eg: popLine :: (MonadState [Line] m) => ... but then it errors like so:
Pass1.hs:21:0:
Non type-variable argument in the constraint: MonadState [Line] m
(Use -XFlexibleContexts to permit this)
In the type signature for `popLine':
popLine :: (MonadState [Line] m) => m (Maybe Line)
I always seem to get this message whenever I try to do something that isn't a type variable. It seems to like (MonadState s m) ok and error on something else, but when I try it with a [a] instead of s it errors similar to the above. (Initially the [Line] and Int were tupled in a single state, but I was getting this error so I thought I'd try to put them in separate states).
GHC 6.10.4, Kubuntu
So, can anyone tell me what is going on and give an explanation / show me the right type signatures, or does anyone know of a good reference on this stuff (the only thing that has helped so far was "Monad Transformers Step by Step", but that just uses one aux state function and one StateT)?
Many Thanks in advance.
Edit
Here's the compiling code incorporating JFT's and Edward's suggestions:
{-# LANGUAGE GeneralizedNewtypeDeriving #-} -- needed for: deriving (Functor,Monad)
{-# LANGUAGE MultiParamTypeClasses #-} -- needed for: MonadState instance
{-# LANGUAGE FlexibleContexts #-} -- needed for: (MonadState PassState m) => ...
module Pass1 where
import Control.Monad.State
import Control.Monad.Writer
import Data.Maybe
import Types
type Lines = [Line]
type Addresses = [Address]
type LineNum = Int
type Messages = [Msg]
data Msg = Error String
| Warning String
data PassState = PassState { passLineNum :: LineNum
, passLines :: Lines
, passAddresses :: Addresses
}
newtype Pass1 a = Pass1 { unPass1 :: WriterT Messages (State PassState) a
}
deriving (Functor,Monad)
instance MonadState PassState Pass1 where
get = Pass1 . lift $ get
put s = Pass1 . lift $ put s
runPass1 :: PassState -> Pass1 a -> ((a, Messages), PassState)
runPass1 state = flip runState state .
runWriterT .
unPass1
curLineNum :: (MonadState PassState m) => m LineNum
curLineNum = do
state <- get
return $ passLineNum state
nextLine :: (MonadState PassState m) => m (Maybe Line)
nextLine = do
state <- get
let c = passLineNum state
let l = passLines state
case l of
x:xs -> do
put state { passLines = xs, passLineNum = (c+1) }
return $ Just x
_ -> return Nothing
evalr :: Pass1 (Maybe Line,LineNum)
evalr = do
l <- nextLine
c <- curLineNum
--tell $ Warning "hello"
return (l,c)
I combined incLineNum and popLine into nextLine I still need to get the Writer monad portion to work, but think I know where to go from here. Thanks, guys.
There was many issues with your code snippet. I fixed your snippet adding explanation as to what was broken and added some style advice if you care.
module Pass1_JFT where
import Control.Monad.Identity
import Control.Monad.State
import Control.Monad.Writer
import Data.Maybe
import qualified Data.Map as Map
{- replacing your import Types with simple definitions -}
--import Types
type Line = String
type Address = String
type LineNumber = Int
{-
Not part of your question but my 2 cents here...
Say that you want to changes the collection for your states if you don't
use a type alias you'll have to hunt everwhere you used it. Instead just
change these definitions if required
-}
type Lines = [Line]
type Addresses = [Address]
type Messages = [Msg]
data Msg = Error String
| Warning String
{-
What is that Int in StateT Int? Name it easier to read, reason about
and to change. Declarative FTW let's use LineNumber instead
-}
--type Pass1 a = WriterT [Msg] (StateT Int (StateT [Line] (StateT [Address] Identity))) a
{-
Let's use a "real" type so instances can be derived.
Since Pass1 is not a monad transfer i.e. not defined as Pass1 m a,
no point using StateT for the deepest StateT i.e. StateT [Address] Identity
so let's just use a State [Address]
-}
newtype Pass1 a = Pass1 {
unPass1 :: WriterT Messages (StateT LineNumber (StateT Lines (State Addresses))) a
}
deriving (Functor,Monad)
--runIdentity (runStateT (runStateT (runStateT (runWriterT msgs) 1) instrs) addrs)
{-
Let's peel that stack from the outermost (lefmost in the declaration)
up to the innermost was Identity in your original declaration.
Note that runWriterT does NOT take a starting state...
The first parameter for runStateT (and runState) is not the initial state
but the monad... so let's flip!
-}
runPass1' :: Addresses -> Lines -> Messages -> Pass1 a -> ((((a, Messages), LineNumber), Lines), Addresses)
runPass1' addrs instrs msgs = flip runState addrs .
flip runStateT instrs .
flip runStateT 1 .
runWriterT . -- then get process the WriterT (the second outermost)
unPass1 -- let's peel the outside Pass1
{-
now that last function does NOT do what you want since you want to provide
an initial log to append to with the WriterT.
Since it is a monad transformer we'll do some trick here
-}
-- I keep the runStateT convention for the order of the arguments: Monad then state
runWriterT' :: (Monad m,Monoid w) => WriterT w m a -> w -> m (a,w)
runWriterT' writer log = do
(result,log') <- runWriterT writer
-- let's use the monoid generic append in case you change container...
return (result,log `mappend` log')
runPass1 :: Addresses -> Lines -> Messages -> Pass1 a -> ((((a, Messages), LineNumber), Lines), Addresses)
runPass1 addrs instrs msgs = flip runState addrs .
flip runStateT instrs .
flip runStateT 1 .
flip runWriterT' msgs . -- then get process the WriterT (the second outermost)
unPass1 -- let's peel the outside Pass1
{-
Do you intend to call popLine directly from a Pass1 stack?
If so you need to "teach" Pass1 to be a "MonadState Lines"
To do so let's derive Pass1 (that's why we declared it with newtype!)
-}
instance MonadState Lines Pass1 where
-- we need to dig inside the stack and "lift" the proper get
get = Pass1 . lift . lift $ get
put s = Pass1 . lift . lift $ put s
{-
Better keep thing generic but we now could have written:
popLine :: Pass1 (Maybe Line)
-}
popLine :: (MonadState Lines m) => m (Maybe Line)
popLine = do
ls <- get
case ls of
x:xs -> do
put xs
return $ Just x
[] -> return Nothing
{-
Ok now I get the Int => LineNumber....
we could make Pass1 and instance of MonadState LineNumber but LineNumber
should not be messed with so instead I'd code the incLine directly
and would provide a MonadReader instance for consulation if required
check ":t incLineNum and :t curLineNum"
-}
incLineNum = Pass1 . lift $ modify (+1)
curLineNum = Pass1 $ lift get
evalr = do l <- popLine
incLineNum
return l
There it is a long winded response but monad and monad stack as you see are challenging at first. I fixed the code but I encourage you to play and inspect the types of the various functions to understand what is going on and to compare against your original. Haskell's type inference means that usually type annotations are superfluous (unless to remove ambiguity). In general the type we'd give to function is less generic that was is infer so it is better not to type annotate. Type annotation is definitively a good debugging technique though ;)
Cheers
P.S. Real World Haskell chapter on Monad Transformer is excellent:
http://book.realworldhaskell.org/read/monad-transformers.html
In general you'll find that code winds up much clearer using one StateT with a larger composite structure for all of the bits of state that you need. One good reason is that when you come up with a piece of state you forgot you can always grow the structure by one field, and you can use the record sugar to write out single field updates or turn to something like the fclabels or data-accessor packages to manipulate state.
data PassState = PassState { passLine :: Int, passLines :: [Line] }
popLine :: MonadState PassState m => m (Maybe Line).
popLine = do
state <- get
case passLines state of
x:xs -> do
put state { passLines = xs }
return (Just x)
_ -> return Nothing