这段Haskell代码正确吗?如果可以,为什么?

时间:2019-02-17 11:32:55

标签: haskell

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

1 个答案:

答案 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)

在这种情况下,假设runStateid函数,那是正确的,

(>>=) :: 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数据构造函数中。