Haskell - 我如何 运行 状态单子列表?

Haskell - How would I run a list of state monads?

我对 Haskell 有点陌生,我在使用 State monad 时遇到了一些麻烦。

我创建了以下类型。 Stat a 有一个 monoid、functor、applicative 和 monad 实例为其创建。

我程序中的 "main" 类型是生物,它有很多参数:

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

一个生物会发生很多事情。我选择用状态 monad 来表示那些东西:

anyPossibleChange :: State Creature Creature

这可能是对生物造成的伤害,增加生物的力量,基本上任何东西。任何事情的可能性让我认为 State monad 在这里是一个不错的选择。我将接受原始状态的生物,进行一些修改,然后 return 元组中的原始状态和新状态。

原始状态可能是:

Creature {
    strength = Stat [] 10,
    dexterity = Stat [] 10
}

结束状态可能是:

Creature {
    strength = Stat [StatModifier Enhancement 2] 10,
    dexterity = Stat [StatModifier Enhancement 4, StatModifier Morale 2] 10
}

我想建立一个生物需要经历的所有变化的列表,然后 运行 生物经历所有这些变化。

这是我想要的签名,但我在想出一个实现时遇到了麻烦。我愿意接受它的不同。

applyChanges :: Creature -> [State Creature Creature] ->  Creature

我觉得我应该可以用弃牌来做到这一点,可能 FoldM 但我的大脑被这些类型所困扰。

什么是好的实施?

State Creature Creature 是这种计算的错误类型。如您所见,您可以摆脱它,但它不必要地使事情复杂化,因为您实际上根本不关心状态变量!您只需使用它来存储函数的原始输入...然后将其丢弃在 applyChanges.

一种更容易使用的类型是 Creature -> Creature,然后如果您有要应用的此类函数的列表,您只需将它们全部组合起来,您可以使用折叠来完成:

applyChanges :: [Creature -> Creature] -> Creature -> Creature
applyChanges = foldr (.) id