我有一个功能:
test :: String -> State String String
test x =
get >>= \test ->
let test' = x ++ test in
put test' >>
get >>= \test2 -> put (test2 ++ x) >>
return "test"
我几乎可以理解整个函数中发生的事情,并且我开始了解monad。我不明白的是,当我这样做时:
runState (test "testy") "testtest"
'test'中的'get'函数以某种方式获得初始状态“testtest”。有人可以打破这个并向我解释一下吗?
我感谢任何回复!
答案 0 :(得分:18)
我原本打算将此作为评论发布,但决定进一步阐述。
严格地说,get
没有“接受”一个论点。我想很多正在发生的事情都被你没看到的东西掩盖了 - 状态monad的实例定义。
get
实际上是MonadState类的一种方法。 State monad是MonadState的一个实例,提供get
的以下定义:
get = State $ \s -> (s,s)
换句话说,get
只返回一个非常基本的状态monad(记住monad可以被认为是计算的“包装器”),其中任何输入s
进入计算将返回一对s
作为结果。
接下来我们要看的是>>=
,State定义了这个:
m >>= k = State $ \s -> let
(a, s') = runState m s
in runState (k a) s'
因此,>>=
将产生一个新的计算,在它获得初始状态之前不会计算(当它们处于“包装”形式时,所有状态计算都是如此)。这个新计算的结果是通过将>>=
右侧的任何内容应用于运行左侧计算的结果来实现的。 (这是一个相当混乱的句子,可能需要额外的阅读或两个。)
我发现“desugar”对所有正在发生的事情非常有用。这样做需要更多的打字,但应该非常清楚地回答你的问题(get
来自哪里)。请注意,以下内容应视为psuedocode ...
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
提供了s
。如果在上面的伪代码中用“testtest”代替get
,你会发现首先发生的是我们运行("testtest", "testtest")
并将“testtest”作为“初始状态”。这会产生get
等等。
这就是{{1}}获得初始状态“testtest”的地方。希望这有帮助!
答案 1 :(得分: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
采用这样的函数,初始输入,并且(很可能)只将一个函数应用于另一个函数来检索(结果,状态)对。
你的test
函数是State
类型函数的一个重要组成部分,每个函数都接受一个状态输入并产生一个(结果,状态)输出,以一种有意义的方式相互插入你的计划。所有runState
所做的就是为他们提供一个州的起点。
在此上下文中,get
只是一个函数,它将状态作为输入,并返回(结果,状态)输出,使得结果是输入状态,状态不变(输出状态)是输入状态)。换句话说,get s = (s, s)
答案 2 :(得分:1)
经过Graham Hutton Programming in Haskell的第8章(“功能解析器”)几次,直到我正确地理解它,然后去教程All About Monads,为我做了这个点击。
monad的问题在于它们对于我们这些来自通常编程背景的人发现的几件事非常有用。需要一些时间才能意识到控制流和处理状态不仅足够相似,而且可以通过相同的机制来处理,但是当你退回到足够远的时候,同样的事情。
当我在考虑C中的控制结构(for
和while
等)时出现顿悟,我意识到到目前为止最常见的控制结构只是将一个语句放在另一个之前一。在我意识到它甚至是一个控制结构之前花了一年时间研究Haskell。