了解状态单子

时间:2019-08-06 10:36:51

标签: haskell functional-programming

我正在“为哈萨克斯坦学到伟大的东西!”这本书中了解州立单子。由Miran Lipovaca撰写。 对于以下monad实例:

instance Monad (State s) where 
   return x = State $ \s -> (x,s)
   (State h) >>= f = State $ \s -> let (a, newState) = h s
                                       (Stage g) = f a
                                   in g newState

我在理解>>=函数的定义时遇到了麻烦。我不确定h是有状态计算(即采用状态并返回具有更新状态的结果的函数)还是状态。我猜想它必须是有状态的计算,因为它已应用于lambda函数中的类型s的状态以产生结果(a, newState)

但是根据状态s a的类型声明:

newtype State s a = State { runState :: s -> (a,s) }

状态的类型为s,结果的类型为a。因此,对于monad实例,s中的instance Monad (State s) where是状态类型还是实际上是有状态计算?任何见解都会受到赞赏。

1 个答案:

答案 0 :(得分:7)

State对象存储状态。它存储“状态改变”。实际上,它存储了一个函数runState :: s -> (a, s)。这里的s是状态的类型,a是状态的类型。

该函数因此将状态作为输入,并返回2元组(a, s)。这里的第一项是“输出”,第二项是“新状态”。新状态可能与旧状态相同,但是新状态因此有机会更改状态(否则使用State并不是很有用)。

我们可以将State更改对象和状态更改对象(a -> State s b)的“工厂”绑定到新的State更改对象中。因此,我们构造了一个具有初始状态s0的函数。我们首先将其运行到runState对象的State,然后检索一个2元组(a, s1)。然后,我们可以使用此a来构建State s b对象,然后通过该s1对象的runState运行(改变的状态)State

因此,更详细的实现是:

instance Monad (State s) where 
   return x = State $ \s -> (x,s)
   (State h) >>= f = State g
       where g s0 = (b, s2) -- result of second runState
                 where (a, s1) = h s0 -- run through first runState
                       -- create second state with the output of the first
                       State f' = f a
                       (b, s2) = f' s1 -- run through second runState

请注意,这里我们实际上从未拥有状态值。我们只构造一个新的函数,该函数将在该状态值上工作。

从原理上讲,我们可以看到绑定操作符如下:

  s0
\    /
 |  |
 |  |
 ||||
  |\_________
  |          '
  |           s1
  v         \    /
  a ----->   |  |
             |  |
             ||||
              |\_______
              |        '
              v        s2
              b

因此,这里的第一个runState处于初始状态s0,将返回一个as1。使用a,我们构造了一个新的runState,然后可以进一步处理状态s1,并返回一个b和新的状态s2