Desugared version of the nested example:
stack4 :: StateT Int ( StateT String ( StateT String Identity ) ) ( Int, String, String )
stack4 = modify (+10) >>= \_ ->
(lift $ modify ( ++ " world" )) >>= \_ ->
(lift . lift $ modify ( ++ " word" )) >>= \_ ->
get >>= \a ->
lift get >>= \b ->
(lift . lift $ get) >>= \c ->
return (a,b,c)
In applicative style:
import Control.Applicative
stack5 :: StateT Int ( StateT String ( StateT String Identity ) ) ( Int, String, String )
stack5 = modify (+10) *>
(lift $ modify ( ++ " world" )) *>
(lift . lift $ modify ( ++ " word" )) *>
((,,) <$> get <*> lift get <*> (lift . lift $ get))
Also, Lambdabot can perform automatic desugaring, see this question.
As for the need for runIdentity
, there's nothing mysterious about that. You have to unwrap every layer of a monad stack to get to the value inside, and Identity
happens to be in the stack. Now, the State
monad can be implemented in terms of StateT
and Identity
, but in that case the user is presented an "unified view" which hides the inner machinery. If you check the source code for runState
in the transformers
package, you'll see that it calls runIdentity
internally.