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.

+3


source to share


2 answers


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

.

+8


source


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 be addState'

    (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 be adder :: 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.

+1


source







All Articles