[英]Use of put with >> in the state monad
From Thinking Functionally with Haskell (pg 248): 从与Haskell的功能思考 (第248页):
You can think of type
State sa
as being 您可以将State sa
视为存在类型type State sa = s -> (a,s)
... ...
put :: s -> State s () get :: State ss state :: (s -> (a,s)) -> State sa
...
state
can be defined usingput
andget
: ...state
可以使用put
和get
来定义:state f = do {s <- get; let (a,s') = fs; put s'; return a}
Which I believe can be rewritten this way: 我认为可以这样重写:
get >>= \s ->
let (a,s') = fs in
put s' >> return a
So what is the purpose of put s'
, if >>
throws away its return value? 那么put s'
的目的是什么,如果>>
抛弃它的返回值?
>>
doesn't throw away everything from the first argument. >>
不会丢掉第一个论点中的所有内容。 The definition for the state monad (ignoring the newtype
) 状态monad的定义(忽略newtype
)
a >> b = \s -> let (x, s') = a s in b s'
So the state form the first argument is used by >>
, but the 'return value' (the x
component of the result) is ignored. 因此,第一个参数的状态由>>
,但是'返回值'(结果的x
分量)被忽略。 That's the point of the state monad --- to track changes to the state without the programmer having to explicitly take them into account. 这是状态monad的重点---跟踪状态的变化,而程序员不必明确地将它们考虑在内。
Apparently, this isn't explained properly by what the OP has been reading, so here's how you can derive the definition above. 显然,OP已经阅读的内容没有对此进行适当的解释,因此,您可以在此处推导出上述定义。 The definition of >>
across all monads is >>
所有monad的定义是
a >> b = a >>= \ _ -> b
The definition of >>=
for the state monad (ignoring newtype
s) is >>=
for state monad(忽略newtype
s)的定义是
a >>= f = \ s -> let (x, s') = a s in f x s'
Now, substituting the definition of >>=
into the definition of >>
above and simplifying, we get: 现在,将>>=
的定义替换为上面的>>
的定义并简化,我们得到:
a >> b = let f = \ _ -> b in \ s -> let (x, s') = a s in f x s'
= \ s -> let (x, s') = a s in (\ _ -> b) x s'
= \ s -> let (x, s') = a s in b s'
So what is the purpose of
put s'
, if>>
throws away its return value? 那么put s'
的目的是什么,如果>>
抛弃它的返回值?
(>>)
does throw away the return value, but we don't use put
for the return value. (>>)
会丢弃返回值,但我们不使用put
作为返回值。 The type of put
is: put
的类型是:
put :: s -> State s ()
The return value is of put
is a ()
, and ()
is for the most part just an uninteresting placeholder. put
的返回值是a ()
,而()
在很大程度上只是一个无趣的占位符。 The meaningful part of what put
does -- replacing the state -- is not reflected in the return value. 什么样的有意义的部分put
更换状态- -确实没有反映在返回值。 A similar case is the type of putStrLn :: String -> IO ()
. 类似的情况是putStrLn :: String -> IO ()
。
State
is an example where the monad abstraction is used to encapsulate an effect . State
是一个使用monad抽象来封装效果的示例。 In such cases, it's perfectly normal to have operations in which the effect of the operation is important, but it might not have a meaningful return value. 在这种情况下,操作的效果很重要,但它可能没有有意义的返回值,这是完全正常的。
I'll illustrate with an example. 我将以一个例子来说明。 Consider everyone's favorite recursive algorithm, the Fibonacci sequence: 考虑每个人最喜欢的递归算法,Fibonacci序列:
fib :: Int -> Int
fib 1 = 0
fib 2 = 1
fib n = fib (n-1) + fib (n-2)
We all know this is a pretty inefficient way to calculate these numbers, but how inefficient is it? 我们都知道这是计算这些数字的一种效率很低的方法,但效率如何呢? If we were using a lesser language, we would probably be tempted to hack in a mutable variable and increment it every time fib
is called. 如果我们使用较小的语言,我们可能会想要破解一个可变变量并在每次调用fib
递增它。 We can do something similar in Haskell in a purely functional way using State
. 我们可以使用State
以纯函数方式在Haskell中执行类似的操作。
Let's define a new version of fib: 让我们定义一个新版本的fib:
fib' :: Int -> State Int Int
fib' 1 = modify (+1) >> return 0
fib' 2 = modify (+1) >> return 1
fib' n = modify (+1) >> (+) <$> fib' (n-1) <*> fib' (n-2)
Now we can simultaneously compute the nth number and also count how many calls were made: 现在我们可以同时计算第n个数字,并计算调用的次数:
> runState (fib' 7) 0
(8,25)
> runState (fib' 10) 0
(34,109)
> runState (fib' 30) 0 -- this takes about 5 seconds on my machine
(514229,1664079)
Ok, that's great but how does it answer the question? 好的,那很好但是它如何回答这个问题呢? The point to note in the above implementation is modify (+1)
. 上述实现中要注意的是modify (+1)
。 This has the effect of adding 1 to the counter, but doesn't have any useful result on its own. 这具有向计数器添加1的效果,但是它本身没有任何有用的结果。 We use >>
to sequence it with the next operation, which does have a useful result, namely the computation. 我们使用>>
对下一个操作进行排序,它有一个有用的结果,即计算。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.