Haskell 对“上的州单子代码”的混淆;向你学习哈斯凯尔语;

Haskell 对“上的州单子代码”的混淆;向你学习哈斯凯尔语;,haskell,monads,state-monad,Haskell,Monads,State Monad,我正试图通过这本在线书了解哈斯克尔 据我所知,到目前为止,我一直能够理解单子,直到我读到介绍单子的那一章 然而,所呈现并声称是State类型的Monad实现的代码(我一直无法在Hoogle中找到它)似乎太多了,我无法处理 首先,我不理解它背后的逻辑,即为什么它应该工作,以及作者如何考虑这项技术。(可能可以建议相关文章或白皮书?) 在第4行,建议函数f采用1个参数。 然而,在下面的几行中,我们看到了pop,它不需要任何参数 为了扩展第1点,作者试图用一个函数来表示状态来完成什么 非常感谢您对理

我正试图通过这本在线书了解哈斯克尔

据我所知,到目前为止,我一直能够理解单子,直到我读到介绍单子的那一章

然而,所呈现并声称是State类型的Monad实现的代码(我一直无法在Hoogle中找到它)似乎太多了,我无法处理

  • 首先,我不理解它背后的逻辑,即为什么它应该工作,以及作者如何考虑这项技术。(可能可以建议相关文章或白皮书?)

  • 在第4行,建议函数f采用1个参数。
    然而,在下面的几行中,我们看到了pop,它不需要任何参数

  • 为了扩展第1点,作者试图用一个函数来表示状态来完成什么

非常感谢您对理解正在发生的事情的任何帮助

编辑 与之相关的人

下面的答案完全涵盖了我的问题。
不过,我想补充一点:

在阅读了下面的一篇文章后,我找到了上述第二点的答案: 一直以来,我都认为pop函数的用法如下:

stuff>>=pop
,因为在bind类型中,第二个参数是函数,而正确的用法是this
pop>=stuff
,我在再次阅读后意识到,符号是如何转换为纯bind lambda的。

状态monad本质上是

type State s a = s -> (a,s)
从一种状态(
s
)到一对期望结果(
a
)和一种新状态的函数。该实现使状态线程化成为隐式的,并为您处理状态传递和更新,因此不存在意外地将错误状态传递给下一个函数的风险

因此,采用
k>0
参数的函数(其中一个参数是状态并返回一对某物和一个新状态)在
状态s
monad中成为采用
k-1
参数并返回一元动作的函数(这里基本上是采用一个参数即状态的函数)

在非状态设置中,
pop
接受一个参数stack,即状态。因此,在一元设置中,
pop
成为一个不带显式参数的
状态堆栈Int
操作


使用
State
monad而不是显式的状态传递可以使代码更清晰,出错的机会更少,这就是
State
monad所实现的。如果没有它,一切都可以完成,只会更加麻烦和容易出错。

简短回答:

  • State
    旨在利用monad的特性,用局部变量模拟命令式系统状态。基本思想是在monad中隐藏接受当前状态并返回新状态的活动,以及每个步骤的中间结果(这里我们有
    s->(a,s)
  • 不要将任意函数与包装在
    状态
    中的函数相混淆。前者可能具有您想要的任何类型(前提是如果您想在状态monad中使用它们,它们最终会产生一些
    状态a
    ),后者包含
    s->(a,s)类型的函数
    :这是由monad管理的状态传递层
  • 正如我所说,
    State
    中包装的函数实际上是通过
    (>>=)
    return
    生成的,因为它们是为
    Monad(State s)
    实例定义的。它的作用是通过调用代码传递状态
  • 第3点也是state参数从state monad中实际使用的函数中消失的原因

    长答案:

    State Monad已经在不同的论文中研究过,并且也存在于Haskell框架中(我现在不记得有什么好的参考文献,我会尽快添加它们)

    这是下面的想法:考虑一个类型<代码>数据MyStase=…<代码>,其值保存系统的当前状态。< /P> 如果您想通过一组函数传递它,您应该以这样的方式编写每个函数:它至少将当前状态作为参数,并返回一对结果(取决于状态和其他输入参数)和新的(可能已修改)状态。这正是状态monad的类型告诉您的:

    s->(a,s)
    。在我们的示例中,
    s
    MyState
    ,用于传递系统的状态

    包装在
    状态
    中的函数不接受当前状态以外的参数,而当前状态是生成新状态和中间结果所必需的。示例中看到的参数更多的函数不是问题,因为当您在monad中的
    do
    -符号中使用它们时'将它们应用于所有需要的“额外”参数,这意味着它们中的每一个都将产生一个部分应用的函数,其唯一的剩余参数是state;
    state
    的monad实例将完成其余部分

    如果您查看可能在monad中使用的函数类型(实际上,在monad中它们通常被称为actions),您将看到它们的结果类型在monad中是装箱的:这一点告诉您,一旦您给它们所有参数,它们实际上不会返回结果,但是(在本例中)一个函数
    s->(a,s)
    ,它将符合单子的合成法则

    计算将通过将系统的第一个/初始状态传递给整个块/组合来执行

    最后,不带参数的函数将具有类似于
    statea
    的类型,其中
    a
    是它们的返回类型:如果您查看
    f :: a -> b
    
    f :: a -> s -> (b, s)
    
    f :: a -> ( s -> (b, s) )
    
    runState pop :: Stack -> (Int, Stack) runState (push 3) :: Stack -> ((), Stack) (>>=) :: (Monad m) => m a -> (a -> m b) -> m b (>>=) :: State Stack a -> (a -> State Stack b) -> State Stack b stackManip :: State Stack Int stackManip = do push 3 pop pop stackManip :: State Stack Int stackManip = push 3 >>= \_ -> pop >>= \_ -> pop (\_ -> pop) :: a -> State Stack Int (\_ -> push 3) :: a -> State Stack () 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 (State s) :: State Stack s :: Stack (State h) :: State Stack a h :: Stack -> (a, Stack) newtype State s a = State { runState :: s -> (a,s) } runState pop :: Stack -> (Int, Stack) runState (push 3) :: Stack -> ((), Stack) runState stackManip :: Stack -> (Int, Stack)