I'm somewhat new to Haskell and I'm having some trouble with the State monad.
I have created the following types.
Stat a
has a monoid, functor, applicative and monad instance created for it.
The "main" type in my program is creature and it has many arguments:
data Creature = Creature {
strength :: Stat Integer,
dexterity :: Stat Integer,
...
}
data Stat a = Stat {
modifiers :: [StatModifier],
stat :: a
}
data StatModifier = StatModifier {
modifierType :: ModifierType,
value :: Integer
}
data ModifierType =
Enhancement
| Morale
| ...
There are many things that can happen to a creature. I chose to represent those things with the state monad:
anyPossibleChange :: State Creature Creature
This could be damage being done to the creature, an increase to the creature's strength, basically anything. The possibility of anything made me think the State monad was a good choice here. I will accept a creature in it's original state, perform some modification, and return the original state and the new state in a tuple.
An original state might be:
Creature {
strength = Stat [] 10,
dexterity = Stat [] 10
}
An end state might be:
Creature {
strength = Stat [StatModifier Enhancement 2] 10,
dexterity = Stat [StatModifier Enhancement 4, StatModifier Morale 2] 10
}
I would like to build a list of all the changes a creature needs to go through and then run the creature through all of those changes.
This is the signature I had in mind, but I am having trouble coming up with an implementation. I am open to it being different.
applyChanges :: Creature -> [State Creature Creature] -> Creature
I feel like I should be able to do this with a fold, possibly FoldM
but my brain is getting hung up around the types.
What would a good implementation be?
Creature
aMonoid
? – BergiapplyChanges
function does not accept a creature in its original state – BergiCreature -> Creature
, and then your functionapplyChanges :: [Creature -> Creature] -> Creature -> Creature
is very easy to implement:applyChanges = foldr (.) id
– amalloyState
may be premature in some sense, but I'd do it anyway, because I can immediately think of reasons you'd want to. 1. If you want to get not only the final state but a list of intermediate states as well. You could do this withscanl
, but if you start withState
it's really natural. 2. You want to add some other effects. If you start withState
, you need only switch toStateT
on the relevant underlying action type. – dfeuerState Creature a
, where the state value is what you actually want as a result (via execState), and not the monadic value you'd get from evalState, right? That certainly makes some sense to me, butState Creature Creature
seems too specific, requiring each transformation to end withget
to move theCreature
state into the monadic value.State Creature ()
looks more reasonable, as a target for composing a list of stateful transformations. – amalloy