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 .
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))
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))
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')
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.