1

I'm trying to use State Monad in a personal section of code in order to have a more natural code . The problem and the code section are the following ones . I'm trying to ask interactively a user to assign the different results of a die roll to different characteristics .

generateCharacteristics :: IO ()
generateCharacteristics = do
 dieResults <- generateCharacteristicsIntroMessage
 (strength, dieResultStrength) <- askStrengthComp dieResults
 (agility, dieResultAgility) <- askAgilityComp dieResultsStrength
 (constitution, dieResultConstitution) <- askConstitutionComp dieResultAgility 
newtype StrengthValue = StrengthValue Int
newtype AgilityValue = AgilityValue Int
...
askStrengthComp :: [Int] -> IO(StrengthValue, [Int])
-- ask to pick a value from the input list of die rolls, 
-- remove the value from the input list of die rolls and returns 
-- a tuple with StrengthValue and resulting list of die rolls
...
askAgilityComp :: [Int] -> IO(AgilityValue, [Int])
-- Same as askStrengthComp but for Agility
...

As you can see , the dieResults are moved through the 'do' actions, and I want to have a State Monad where

DieRolls -> (StrengthValue, newDieRolls)

So, the state monad can handle internally the action of removing the chosen die roll assigned to the characteristic .

Following amazing Graham Hutton book, I don ́t have clear how to adapt the creation of the functor and applicative functions

type StateMerp = [Int]
newtype STMerp a = SMerp (StateMerp -> (a,StateMerp))
appMerp :: STMerp a -> StateMerp -> (a, StateMerp)
appMerp (SMerp st) x = st x
instance Functor STMerp where
...
instance Applicative STMerp where
...
instance Monad STMerp where
... 

For the sake of simplicity, in the part of State, I have removed IO . Just learning about this monad, so I think what I ́ḿ trying can be done . Could you help me in the creation of the state monad so I can get rid off moving the die rolls time after time ? THanks .

asked Aug 18 at 15:41
5
  • Is there a reason why you don't want to use the already builtin State monad, because we can probably program the dieResults against the State monad Commented Aug 18 at 15:43
  • 1
    What's blocking you from making progress? What have you tried, and what went wrong? Commented Aug 18 at 15:56
  • Hi @willeM_VanOnsem . Thanks for the reply . I'm a newbie with a toy project trying to learn, so no restrictions . The only problem that I had is the lack of experience and knowledge . So I have been trying to adapt tutos of state monad to the situation with no luck . If you could share sole URL about using the builtin state monad, It would be appreciated Commented Aug 18 at 17:42
  • Hi @DanielWagner. My progress has been to add the state constructor and the app function with the types I have created, but I don't fully understand them because when I build the project , I'm having problems with the expected and actual types. Maybe I should detail the error a little bit more . Commented Aug 18 at 17:46
  • Yes. We cannot help you fix an error unless you show us the error (and the code we need to reproduce it). Commented Aug 18 at 17:59

1 Answer 1

1

This is how I rewrote State following the book, Haskell Programming from First Principles (Allan & Moronuki, 2016).

State, runState, Functor

newtype State s a = State { runState :: s -> (a,s) }
instance Functor (State s) where
 fmap :: (a -> b) -> State s a -> State s b
 fmap f (State g) = State (fmap (\(a,s) -> (f a, s)) $ runState (State g))
  • Use 'runState' to get the function '\s -> (a,s)' out of 'State g'.
  • 'fmap' over the function.
  • Wrap the new function in 'State'.
ghci> let s0 = State (\s -> (2,s))
ghci> let s1 = fmap (+1) s0
ghci> runState s1 0
(3,0)

Applicative

instance Applicative (State s) where
 pure :: a -> State s a
 pure a = State $ \s -> (a,s)
 (<*>) :: State s (a -> b) -> State s a -> State s b
 (State sab) <*> (State sa) = State (\s -> let (a, s0) = sa s
 (ab, s1) = sab s
 in (ab a, s1))
  • Create a 'State' with a new function that takes a value 's'.
  • Use that 's' to get 'a' from the applied-to state and 'a->b' from the applied state.
  • Apply the 'a -> b' function to 'a'.
ghci> let sab = State (\s -> ((+1), s))
ghci> let sa = State (\s -> (2, s))
ghci> let s = sab <*> sa
ghci> runState s 0
(3,0)

Monad

instance Monad (State s) where
 return = pure
 (>>=) :: State s a -> (a -> State s b) -> State s b
 (>>=) (State f) g = State (\s -> let (a,s') = f s
 in runState (g a) s')
  • Get the '(a,s')' from the first state function.
  • Use that 'a' to generate a second state from 'g'.
  • Run the second state with the 's'' from the first state.
ghci> let sa = State (\s -> (2, s))
ghci> let g = \a -> State (\s -> (a+1, s))
ghci> let sb = sa >>= g
ghci> runState sb 0
(3,0)

This is how the standard 'State' works. I sense that you might be trying to do something different. Hopefully this will give you a start.

answered Aug 26 at 21:06
Sign up to request clarification or add additional context in comments.

2 Comments

You almost nailed but it's correct enough. The only thing left in my case is to use monad Transformers to mix state and IO . But your example is great . Great help. Thanks.
Right on! The Allen/Moronuki book has a good chapter on monad transformer stacks and MonadIO. ✌️

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.