Building a minimal Haskell example of error handling in State Monad
I am twisting my brain into knots trying to figure out how to combine a monad State
with Maybe
.
Let's start with a specific (and deliberately trivial / unnecessary) example in which we use a monad State
to find the sum of a list of numbers:
import Control.Monad.State
list :: [Int]
list = [1,4,5,6,7,0,3,2,1]
adder :: Int
adder = evalState addState list
addState :: State [Int] Int
addState = do
ms <- get
case ms of
[] -> return 0
(x:xs) -> put xs >> fmap (+x) addState
Cool.
Now change it so that it returns Nothing
if the list contains a number 0
. In other words, it evalState addState' list
should return Nothing
(since it list
contains a 0
). I thought it might look something like this ...
addState' :: State [Int] (Maybe Int)
addState' = do
ms <- get
case ms of
[] -> return (Just 0)
(0:xs) -> return Nothing
(x:xs) -> put xs >> fmap (fmap (+x)) addState'
... it works, but I guess there is a better way to do it ...
I have been playing around with StateT
and MaybeT
and I cannot get them to work. I looked at a couple of introns on Monad transformers, but they either didn't cover that particular combo (i.e. State + Maybe), or the examples were too complex to understand.
TL; The DR: . I would appreciate it if someone could show how to write this (admittedly trivial) piece of code using StateT
and MaybeT
(two examples). (I'm guessing it's impossible to write this code without using transformers - is that wrong?)
PS I understand that it StateT
is probably a better fit for this example, but it would be helpful to conceptually see both examples if not too many problems.
Update: As @Brenton Alker pointed out, my first version of the above code doesn't work due to a simple typo (I was missing the apostrophe). In the interest of focusing the question of using StateT
/ MaybeT
, I am correcting the post above. Just wanted to include this note to give context to my post.
source to share
The type I would recommend using:
StateT [Int] Maybe Int
The easiest way to use Maybe
/ MaybeT
is to just call mzero
whenever you want to crash and mplus
whenever you want to recover from a failed calculation. This works even if they overlap in other monad transformers.
Here's an example:
addState' :: StateT [Int] Maybe Int
addState' = do
ms <- get
case ms of
[] -> return 0
(0:xs) -> mzero
(x:xs) -> put xs >> fmap (fmap (+x)) addState
-- This requires generalizing the type of `addState` to:
addState :: Monad m => StateT [Int] m Int
Note that I wrote this so that I didn't use any Maybe
-specific operations. In fact, if you give the compiler type signature output, it will infer this more general type:
addState' :: MonadPlus m => StateT [Int] m Int
This works because it StateT
has the following instance MonadPlus
:
instance MonadPlus m => MonadPlus (StateT s m) where ...
And Maybe
will check the type as an instance MonadPlus
, so the above code works when we specialize m
in Maybe
.
source to share
I believe your solution is mostly correct, you have a few minor problems.
- Your recursive call is
addState
missing a simple - that is. it should beaddState'
(I suspect this is just a problem in pasting the question, given the reported bug). - You claim
adder :: Int
, but in the new version it should beadder :: Maybe Int
- I think it is the type error you are getting.
Unfortunately I don't have the resources to try the transformers version at the moment.
source to share