12

我有一个功能:

test :: String -> State String String
test x = 
    get >>= \test ->
    let test' = x ++ test in
    put test' >>
    get >>= \test2 -> put (test2 ++ x) >>
    return "test"

我几乎可以理解整个函数中发生的事情,并且我开始掌握 monads 的窍门。我不明白的是,当我运行这个时:

runState (test "testy") "testtest"

'test' 中的 'get' 函数以某种方式获取初始状态“testtest”。有人可以分解并向我解释吗?

我很感激任何回应!

4

3 回答 3

18

我原本打算将此作为评论发布,但决定再解释一下。

严格来说,get不“接受”一个论点。我认为很多正在发生的事情都被你没有看到的东西所掩盖——State monad 的实例定义。

get实际上是 MonadState 类的一个方法。State monad 是 MonadState 的一个实例,提供以下定义get

get = State $ \s -> (s,s)

换句话说,get只返回一个非常基本的 State monad(请记住,monad 可以被认为是计算的“包装器”),其中任何输入s到计算中都会返回一对s作为结果。

接下来我们需要看的是>>=,State 是这样定义的:

m >>= k  = State $ \s -> let
    (a, s') = runState m s
    in runState (k a) s'

因此,>>=将产生一个新的计算,直到它获得初始状态才会计算(当它们处于“包装”形式时,所有状态计算都是如此)。这种新计算的结果是通过将右侧的任何内容应用于>>=左侧计算的运行结果来实现的。(这是一个非常令人困惑的句子,可能需要额外阅读一两次。)

我发现对正在发生的一切“脱糖”非常有用。这样做需要更多的输入,但应该使您的问题(从哪里get得到)的答案非常清楚。请注意,以下应视为伪代码...

test x =
    State $ \s -> let
        (a,s') = runState (State (\s -> (s,s))) s  --substituting above defn. of 'get'
        in runState (rightSide a) s'
        where 
          rightSide test = 
            let test' = x ++ test in
            State $ \s2 -> let
            (a2, s2') = runState (State $ \_ -> ((), test')) s2  -- defn. of 'put'
            in runState (rightSide2 a2) s2'
          rightSide2 _ =
            -- etc...

这应该清楚地表明我们函数的最终结果是一个新的状态计算,它将需要一个初始值 ( s) 来完成其余的事情。你提供s"testtest"与你的runState电话。如果您在上面的伪代码中替换“testtest” s,您会看到发生的第一件事是我们get以“testtest”作为“初始状态”运行。这会产生("testtest", "testtest")等等。

这就是get你的初始状态“testtest”的地方。希望这可以帮助!

于 2009-06-24T10:40:33.910 回答
5

它可能会帮助您更深入地了解State类型构造函数的真正含义,以及 runState 如何使用它。在 GHCi 中:

Prelude Control.Monad.State> :i State
newtype State s a = State {runState :: s -> (a, s)}
Prelude Control.Monad.State> :t runState
runState :: State s a -> s -> (a, s)

State接受两个参数:状态的类型和返回的类型。它被实现为一个获取初始状态并产生返回值和新状态的函数。

runState接受这样一个函数,即初始输入,并且(很可能)只是将一个应用于另一个以检索 (result,state) 对。

您的test函数是State-type 函数的一大组合,每个函数都接受状态输入并产生(结果,状态)输出,以对您的程序有意义的方式相互插入。所做的只是为他们runState提供一个状态起点。

在这种情况下,get它只是一个将状态作为输入的函数,并返回一个 (result,state) 输出,使得结果为输入状态,并且状态不变(输出状态为输入状态)。换句话说,get s = (s, s)

于 2009-06-24T08:05:34.550 回答
1

多次阅读Graham Hutton 的Haskell 编程的第 8 章(“函数解析器”),直到我正确理解它,然后阅读教程All About Monads,为我点击了此按钮。

monad 的问题在于,它们对于我们这些来自通常编程背景的人发现非常不同的一些事情非常有用。需要一些时间来理解控制流和处理状态不仅足够相似以至于它们可以由相同的机制处理,而且当你退一步足够远时,它们也是一样的。

An epiphany came when I was considering control structures in C (for and while, etc.), and I realized that by far the most common control structure was simply putting one statement before the other one. It took a year of studying Haskell before I realized that that even was a control structure.

于 2009-06-24T18:52:03.603 回答