【问题标题】:Monad facade for a MonadState in HaskellHaskell 中 MonadState 的 Monad 立面
【发布时间】:2015-10-26 00:31:24
【问题描述】:

背景:我正在创建一个带有有状态 monad 的游戏,用于读取和写入对游戏全局状态的更改。

我想将我的游戏分成多个组件,例如“角色”,为这些组件与全局状态交互提供特定领域的方式。理想情况下,这可能是定义我可以使用的MonadState Character m => m a 形式的特定操作,但每个这样的m a 都会对父级(全局状态)进行更改。

我已经四处寻找状态单子之间的转换,或提供从一个状态单子到另一个状态单子的接口,但具体的语言超出了我的知识范围。我也已经在使用 Lenses,我想知道是否可以用它们做点什么。

编辑

我希望能够做类似的事情 moveCharacter :: MonadState Character m => Int -> Int -> m ()

并让它在内部执行move :: MonadState World m => Int -> Int -> m ()。基本上,将世界细节从角色中抽象出来。

谢谢!

【问题讨论】:

    标签: haskell monad-transformers facade state-monad


    【解决方案1】:

    听起来您正在寻找Zoom,它可以让您将镜头视图上的有状态动作转换为镜头源上的有状态动作。

    【讨论】:

    • 我研究过使用 Zoom,但我不确定它是否适合我上面添加的用例。我不知道如何设置它以便我可以构造 MonadState Character monad,但要与世界上的 monadic 动作相关联。
    • 嗯......你不能完全按照你的要求去做。您希望能够访问的任何状态都必须至少根据类型签名可用——您不能谎报您需要访问的状态!如果您的操作需要访问世界状态,那么,天哪,类型将不得不说它可以访问世界状态。
    • 人力资源部。这就说得通了。我想要实现的是防止特定于字符的代码能够对父状态进行未经授权的更改。我的另一种方法可能是创建一些数据类型,其中父状态更改是数据构造函数(如data STDiff = Move Int Int | ...
    【解决方案2】:

    你实际上需要两个转换函数:一个是从世界中提取角色,另一个是用修改后的角色修改世界。你可以像这样把它们放在一起:

    extractCharacter :: World -> Character
    extractCharacter = error "Tried to call placeholder function"
    
    replaceCharacter :: World -> Character -> World
    replaceCharacter = error "Tried to call placeholder function"
    
    runCharacterSubroutine :: (Functor m) =>
      StateT Character m a -> StateT World m a
    runCharacterSubroutine act = StateT $ \w ->
      fmap (\(a,c') -> (a,replaceCharacter w c')) $
      runStateT act (extractCharacter w)
    

    在您的游戏中,您可能想要一些更复杂的东西,但这只是在extractCharacterreplaceCharacter 中添加一个额外的参数

    请注意,我提供的函数仅在 StateT 位于 monad 转换器堆栈的顶部时才有效。如果不是:您将不得不使用mmorph

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-04-22
      • 1970-01-01
      • 2018-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-12
      相关资源
      最近更新 更多