我试图创建一些看起来很像State monad的东西,但也带有一个谓词列表和伴随状态的转换函数。计算的基本步骤如下:
Foo (state, [(pred, t)]) >>= f
。将f
应用于s
,产生s'
。然后将每个谓词应用于s'
。对于匹配的每个谓词,将关联的转换函数按顺序应用于状态。例如。假设[(p1, t1), (p2, t2), (p3, t3)]
,f
和s
。如果在f s
产生s'
后,p1 s'
和p3 s'
都返回True
,那么您将执行t1 s'
产生s''
,然后执行t3 s''
产生s'''
,是计算的结果。
这里有许多活动部件,我觉得正确的方法是在StateT变压器或State monad之上构建它,但是我无法弄清楚从哪里开始。
我觉得这不是很清楚。任何澄清都会使这个更清楚,我们非常感激。
答案 0 :(得分:8)
我认为你不能制作你要求的单子。正如我在与jozefg的讨论中提到的那样,我们有两个monad法则说
f >=> return = f
return >=> f = f
这意味着在绑定位置不会发生任何“有趣”的事情。特别是,我们不能在每个绑定中运行状态转换函数,因为f >=> return
将运行该转换函数而f
不会运行,并且这些规则将被破坏。
但是,这并不能阻止我们代表我们进行状态转换的monadic动作。因此,我将概述如何设计跟踪此类过渡并按需运行它们的monad。如果你希望它有用,你肯定需要充实API。基本思想是,我们将存储s
和转换表,而不仅仅是s
状态。首先,一些样板。
{-# 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
这就是一切!