haskell Wiki(此处为https://wiki.haskell.org/State_Monad)说,状态monad绑定运算符的定义如下:
(>>=) :: State s a -> (a -> State s b) -> State s b
(act1 >>= fact2) s = runState act2 is
where (iv,is) = runState act1 s
act2 = fact2 iv
但是对我来说似乎不正确,因为bind运算符的结果是一个包装在构造函数中的函数,因此无法应用(我说的是这种模式:(act1 >>= fact2) s
)
答案 0 :(得分:7)
简而言之::State
对象本身不会不封装状态,而是封装状态的 change 。< / p>
实际上,State
类型定义为:
newtype State s a = State { runState :: s -> (a, s) }
其中runState
是一个函数,其状态为s
,并返回结果a
和新状态。
绑定运算符(>>=) :: State s a -> (a -> State s b) -> State s b
基本上将状态更改“链接”在一起。因此,它需要一个状态更改函数f1 :: s -> (a, s)
和一个函数f2 :: a -> State s b
,并因此创建一个封装在g :: s -> (b, s)
构造函数中的函数State
。第二个函数f2
因此需要一个a
并返回这种状态改变函数。
因此可以将bind运算符定义为:
(State f1) >>= f2 = State $ \i -> let (y, s) = f1 i in runState (f2 y) s
在这里,我们将i
设置为初始状态,因此我们将首先通过i
状态更改器“链接” f1
。然后返回一个2元组:y
是该调用的“结果”,而s
是新状态,然后将结果和新状态传递给f2
。请注意,这里我们根本不做任何状态更改,我们只构造了{em>可以做的State
对象。因此,我们推迟了真正的链接。
如果State
的定义如上,则该段代码与该定义不匹配,它像@HTWN says那样将其定义为:
type State s a = s -> (a, s)
在这种情况下,假设runState
是id
函数,那是正确的,
(>>=) :: State s a -> (a -> State s b) -> State s b
(>>=) act1 fact2 = f
where f s = act2 is
where (iv,is) = act1 s
act2 = fact2 iv
为了使其与我们的State
类型兼容,我们添加了一些逻辑来解包并将其包装在State
数据构造函数中:
(>>=) :: State s a -> (a -> State s b) -> State s b
(>>=) act1 fact2 = State f
where f s = runState act2 is
where (iv,is) = runState act1 s
act2 = fact2 iv
那确实是正确的。主要错误是没有将其包装在State
数据构造函数中。