解决这个问题的最简单方法是使用MonadRandom
库,它为随机计算引入了一个新的 monad。因此,让我们使用随机数进行计算:
-- normal comonadic computation
type CoKleisli w a b = w a -> b
-- randomised comonadic computation
type RCoKleisli w a b = w a -> Rand b
现在,如何应用这个东西?这很容易extend
:
halfApply :: Comonad w => (w a -> Rand b) -> (w a -> w (Rand b))
halfApply = extend
但这并不完全奏效:它为我们提供了一个随机值容器,而我们想要一个随机值容器。换句话说,我们需要找到可以做的事情w (Rand b) -> Rand (w b)
。而事实上确实存在这样一个函数:sequenceA
! 正如文档所述,如果我们应用sequenceA
a w (Rand b)
,它将运行每个Rand
计算,然后累积结果以获得 a Rand (w b)
——这正是我们想要的!所以:
fullApply :: (Comonad w, Traversible w, Applicative f)
=> (w a -> f b) -> (w a -> f (w b))
fullApply c = sequenceA . extend c
从上面的类型签名中可以看出,这实际上适用于任何Applicative
(因为我们只需要每个应用计算都可以依次运行),但必须w
是Traversible
(因此我们可以遍历 中的每个值w
)。
(关于这类事情的更多信息,我推荐这篇博文,以及它的第二部分。如果你想看到上述技术的实际效果,我推荐我自己的概率元胞自动机库,当时它仍然使用共单原子而不是我自己的类型类。)
这样就回答了你问题的一半;也就是说,如何使用comonads获得概率行为。下半场是:
……而且它绝不能将两个矮人放在同一个地方……</p>
这我不太确定,但一种解决方案可能是将您的 comonadic 计算分为三个阶段:
- 将每个 dwarf 概率转换为 diff,说明该 dwarf 是向左移动、向右移动还是停留。为此操作键入:
mkDiffs :: X Dwarf -> Rand (X DwarfDiff)
- 执行每个差异,但保持原始的矮人位置。此操作的类型:
execDiffs :: X DwarfDiff -> X (DwarfDiff, [DwarfDiffed])
。
- 解决矮人相撞的情况。此操作的类型:
resolve :: X (Dwarf, [DwarfDiffed]) -> Rand (X Dwarf)
。
上面使用的类型:
data Dwarf = Dwarf | NoDwarf
data DwarfDiff = MoveLeft | MoveRight | DontMove | NoDiff
data DwarfDiffed = MovedFromLeft | MovedFromRight | NothingMoved
我正在谈论的示例:
myDwarfs = X [NoDwarf ,Dwarf ,NoDwarf ,Dwarf ,Dwarf ,Dwarf ] 0
mkDiffs myDwarfs
= X [NoDiff ,MoveRight ,NoDiff ,MoveLeft ,MoveRight ,DontMove ] 0
execDiffs (mkDiffs myDwarfs)
= X [(NoDiff,[NothingMoved]),(MoveRight,[NothingMoved]),(NoDiff,[MovedFromRight,MovedFromLeft]),(MoveLeft,[NothingMoved]),(MoveRight,[NothingMoved]),(DontMove,[MovedFromLeft])] 0
resolve (execDiffs (mkDiffs myDwarfs))
= X [NoDwarf ,NoDwarf ,Dwarf ,Dwarf ,Dwarf , Dwarf ] 0
如您所见,上述解决方案非常复杂。我有一个替代建议:不要使用comonads 来解决这个问题!当您需要根据其上下文更新一个值时, Comonads非常有用,但在同时更新多个值时却很糟糕。问题是像你这样的comonads是拉链X
,它将数据结构存储为单个“聚焦”值加上周围的“上下文”。正如我所说,这对于根据上下文更新焦点值非常有用,但是如果您需要更新多个值,则必须将计算硬塞到这个值+上下文模型中……正如我们在上面看到的,这可能非常棘手. 因此,comonads 可能不是此应用程序的最佳选择。