了解国家Monad

时间:2014-08-23 17:45:02

标签: haskell

查看Learn You a HaskellState Monad的定义:

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

我不明白右下角的h sg newState类型。

你能解释一下他们的类型和发生的事情吗?

3 个答案:

答案 0 :(得分:7)

State s a是函数的命名---“状态变换函数”

s -> (a, s)

换句话说,它需要输入状态s并修改该状态,同时返回结果a。这形成了一个真正的“纯国家”框架。如果我们的状态是一个整数,我们可以编写一个更新该整数并返回新值的函数---这就像一个唯一的数字源。

upd :: Int -> (Int, Int)
upd s = let s' = s + 1 in (s', s')

此处,as最终属于同一类型。


现在这一切都很好,除非我们想要获得两个新数字,我们遇到了麻烦。为此,我们必须以某种方式运行upd两次。

最终结果将是另一个状态变压器功能,所以我们正在寻找一个“状态变压器变压器”。我会称它为撰写:

compose :: (s -> (a, s))         -- the initial state transformer
        -> (a -> (s -> (b, s)))  -- a new state transformer, built using the "result"
                                 -- of the previous one
        -> (s -> (b, s))         -- the result state transformer

这看起来有点毛茸茸,但说实话,写这个功能相当容易。这些类型可以指导您找到答案:

compose f f' = \s -> let (a, s')  = f s
                         (b, s'') = f' a s'
                     in  (b, s'')

您会注意到s - 类型变量[s, s', s'']“向下流动”,表示状态从第一次计算移动到第二次计算结果。

我们可以使用compose构建一个使用upd获取两个唯一数字的函数

twoUnique :: Int -> ((Int, Int), Int)
twoUnique = compose upd (\a s -> let (a', s') = upd s in ((a, a'), s'))

这些是State的基础知识。唯一的区别是我们认识到compose函数内部存在一个共同模式,我们将其提取出来。该模式看起来像

(>>=) :: State s a     -> (a -> State s b   ) -> State s b
(>>=) :: (s -> (a, s)) -> (a -> (s -> (b, s)) -> (s -> (b, s))

它也以同样的方式实现。我们只需要“包裹”和“展开”State位 - 这就是StaterunState

的目的
State    :: (s -> (a, s)) -> State s a
runState :: State s a     -> (s -> (a, s))

现在我们可以compose并将其与(>>=)

进行比较
compose f f'       =         \s -> let (a, s')  = f s
                                       (b, s'') =           f' a  s'
                                   in  (b, s'')

(>>=) (State f) f' = State $ \s -> let (a, s')  = f s
                                       (b, s'') = runState (f' a) s'
                                   in  (b, s'')

答案 1 :(得分:2)

State Monad肯定会在您第一次看到它时感到困惑。首先要理解的是它的数据声明,即

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

因此State包含类型为s -> (a,s)的函数。我们可以将其视为一种作用于某种生成器并返回值的元组和新生成器的函数。这就是随机数在Haskell中的工作方式,例如:s是生成器,而a是函数的结果,它将生成器作为输入并输出随机数a(比如说,Int类型,但它可以很容易地成为任何其他类型。)

现在让我们来谈谈实例声明。回想一下(>>=)的类型是

Monad m => m a -> (a -> m b) -> m b

我们特别注意f应该有a -> m b类型。在这种情况下,mState s,因此f的类型应为a -> State s b。所以现在我们可以分解实例声明

(State h) >>= f = State $ \s -> let (a, newState) = h s  
                                    (State g) = f a  
                                in  g newState

由于f的类型为a -> State s b,因此State g的类型必须为State s b(即g :: s -> (b,s)),因为h具有类型s -> (a,s),我们必须newState :: s。因此,绑定表达式的结果是g newState,其类型为(b, s)

为了进一步阅读,here是一篇很棒的文章,帮助我在第一次遇到它时理解了State Monad。

答案 2 :(得分:1)

根据LYAH State monad的定义:

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

这意味着State数据构造函数的参数是函数,它接受一个状态并产生a和一个新状态。因此,上例中的h是一个函数,h s计算anewState

Hoogle我们看到(>>=)的定义是

(>>=) :: Monad m => m a -> (a -> m b) -> m b

这意味着f也是aState s b的函数。因此,赋予f参数a是有意义的,结果是State。就像h一样,g是状态构造函数的参数,它接受一个状态(在本例中为newstate)并返回一对(a,newState2)

询问(>>=)实际做什么可能更有启发性:它将函数参数提升为monad。 State只是一个值的占位符,取决于当前状态,这就是构造函数的参数取决于状态的原因。因此,给定State“值”,我们首先应用状态\s -> let (a, newState) = h s来获取相应的值和新状态。现在我们将该值传递给函数(注意类型匹配)并获得一个新状态,即从状态到值的新函数。最后,我们在newState评估该状态,以将状态线程化到计算的下一部分。