2

我正在尝试创建一些看起来很像 State monad 的东西,但也带有一个谓词列表和伴随状态的转换函数。我设想的计算的基本步骤如下:

Foo (state, [(pred, t)]) >>= f. 适用fs,屈服s'。然后将每个谓词应用于s'. 对于每个匹配的谓词,依次将关联的转换函数应用于状态。例如假设[(p1, t1), (p2, t2), (p3, t3)],fs。如果在f syield之后s'p1 s'并且p3 s'两者都 return True,您将执行t1 s'yielding s'',然后执行t3 s''yielding s''',即计算结果。

这里有很多活动部件,我觉得正确的方法是在 StateT 变压器或 State monad 之上构建它,但我不知道从哪里开始。

我觉得这好像不是很清楚。非常感谢任何能使这一点更清楚的澄清。

4

1 回答 1

8

我不认为你可以制作你想要的单子。正如我在与 jozefg 的讨论中提到的,我们有两个单子定律说

f >=> return = f
return >=> f = f

这意味着在绑定位置不会发生任何“有趣”的事情。特别是,我们不能在每次绑定时运行状态转换函数,因为 thenf >=> return将运行该转换函数而f不会运行,并且这些定律将被打破。

但是,这并不能阻止我们执行代表我们运行状态转换的单子操作。因此,我将勾勒出如何设计一个跟踪此类转换并按需运行它们的 monad 的想法。如果您希望 API 有用,您肯定需要充实一些 API。s基本思想是,我们将存储一个s和一个转换表,而不仅仅是一个as 状态。首先,一些样板。

{-# LANGUAGE FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses #-}
import Control.Arrow
import Control.Applicative
import Control.Monad.State

现在,让我们只处理s -> s过渡。您可以随心所欲地实现它们——包括查看谓词和转换列表并挑选出您想要运行的那些,如果那是您的一杯茶。但这与正确完成其余的想法是正交的。我们将定义我们的新类型,并给它一个Monad实例,该实例只是分派到底层类型。

newtype TStateT s m a = TStateT { unTStateT :: StateT (s, s -> s) m a }
    deriving (Functor, Applicative, Monad)

MonadState实例比仅使用 有点棘手deriving,但仍然非常简单。大概在公开场合我们想假装它只是s状态的一部分,所以我们需要集中注意力。我们还将给出runStateT模拟,并选择一个合理的初始转换函数。(稍后我们将提供修改此选择的方法。)

instance Monad m => MonadState s (TStateT s m) where
    state f = TStateT (state (\(s, t) -> let (v, s') = f s in (v, (s', t))))

runTStateT :: Functor m => TStateT s m a -> s -> m (a, s)
runTStateT m s = second fst <$> runStateT (unTStateT m) (s, id)

现在是有趣的一点。的超能力TStateT是它有一些可以随时运行的转换。因此,让我们提供一种运行它们的方法和一种修改转换表的方法。

step :: Monad m => TStateT s m ()
step = TStateT (gets snd) >>= modify

modifyTransitions :: Monad m => ((s -> s) -> (s -> s)) -> TStateT s m ()
modifyTransitions = TStateT . modify . second

这几乎就是一切!

于 2013-10-21T02:38:04.980 回答