3

I have computations in this format: s -> a -> s, where s is the type of some state. The result of such a function is also the state of the next evaluation. For example,

appendInt :: String -> Int -> String
appendInt s i = s ++ (show i)

Then, appendInt "Int: " 1 will give "Int: 1", while (appendInt $ appendInt "Int: 1") 2 will give "Int: 12". However, I cannot find a way to put this kind of computation in a State Monad.

A first guess is s -> (s,s), but then a cannot be passed in. Then, I tried (a -> s) -> (s, a -> s), but again it is impossible to get s without a. s -> (a,s) won't work because a is the input instead of output.

How should I wrap this computation, then? Is the State monad appropriate for this?

Carl Dong
  • 1,299
  • 15
  • 26

2 Answers2

7

You can use State just fine, or even better use Writer:

import Control.Monad.Writer
import Control.Monad.State

appendInt :: Int -> Writer String ()
appendInt i = tell $ show i

appendInt' :: Int -> State String ()
appendInt' i = modify (++ show i)

main = do print . execWriter $ do
            tell "Int: "
            appendInt 1
            appendInt 2
          print . flip execState "Int: " $ do
            appendInt' 1
            appendInt' 2
Sam van Herwaarden
  • 2,321
  • 14
  • 27
  • I mean `a`. But then I get confused about when I should use the state `s`. Do I use `s -> (a,s)` and `runState` when I don't want to expose the state, but instead want to "transform" that to another format? – Carl Dong Oct 08 '15 at 14:00
  • @CarlDong `runState` turns the monadic action into a function. `state` turns a function into a monadic action. They're basically converters, they don't any magic. – Bartek Banachewicz Oct 08 '15 at 14:03
  • @CarlDong: There are basically three variants, `runState`, `evalState` and `execState`. You use `runState` when you want to extract both the value contained in the monad, and the monadic context (the state). You use `evalState` when you want only the value, and `execState` when you want only the context. In all cases, they don't give you the output directly, but give you a function that takes an initial context (the `s`) to the desired output. That's why I supply `"Int: "` to `execState`, it's the initial context. – Sam van Herwaarden Oct 08 '15 at 14:36
  • @CarlDong: Additionally, since we're not using the `a`, this basically means we're not doing a monadic computation (subsequent computations are not performed differently based on the output of earlier computations). So instead of using `do` notation we could have written `flip execState "Int: " $ appendInt' 1 *> appendInt'2`, relying on the `Applicative` instance of `State`, rather than the `Monad` instance. – Sam van Herwaarden Oct 08 '15 at 14:48
  • Apparently `>>` also works. Thank you for the explanation, I did not notice `execState` and `evalState` before – Carl Dong Oct 08 '15 at 14:54
  • @CarlDong: Yes `>>` is basically the monadic equivalent of `*>`. Since all `Monad`s are `Applicative`s but not vice versa, you gain some generality by using `*>`, and in some cases using `Applicative` code can result in a performance gain as well. – Sam van Herwaarden Oct 08 '15 at 15:05
3

Sure it is possible, for example like this:

appendIntM :: MonadState String m => Int -> m ()
appendIntM i = modify $ flip appendInt i

Had you made the order of arguments more "conventional", it would just be a composition with modify.

Live On Coliru

Bartek Banachewicz
  • 38,596
  • 7
  • 91
  • 135