使用带>>的put在州monad

时间:2017-02-27 03:08:15

标签: haskell state-monad

使用Haskell进行功能性思考(第248页):

  

您可以将State s a类型视为

type State s a = s -> (a,s) 
     

...

put :: s -> State s ()
get :: State s s
state :: (s -> (a,s)) -> State s a
     

... state可以使用putget来定义:

state f = do {s <- get; let (a,s') = f s;
              put s'; return a}

我认为可以这样重写:

get >>= \s ->
    let (a,s') = fs in
        put s' >> return a

那么put s'的目的是什么,如果>>抛弃其返回值?

3 个答案:

答案 0 :(得分:3)

>>不会丢弃第一个参数中的所有内容。状态monad的定义(忽略newtype

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

因此>>使用了第一个参数的状态,但忽略了'返回值'(结果的x组件)。这是状态monad的重点 - 跟踪状态的变化,而程序员不必明确地将它们考虑在内。

显然,OP没有正确解释这个问题,所以这里是你如何得出上面的定义。所有monad中>>的定义是

a >> b = a >>= \ _ -> b

状态monad的>>=定义(忽略newtype s)是

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

现在,将>>=的定义替换为上面>>的定义并简化,我们得到:

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'

答案 1 :(得分:3)

  

那么put s'的目的是什么,如果>>抛弃其返回值?

(>>)会丢弃返回值,但我们不会使用put作为返回值。 put的类型为:

put :: s -> State s ()

返回值为put(),而()大部分只是一个无趣的占位符。 put所做的有意义的部分 - 替换状态 - 没有反映在返回值中。类似的情况是putStrLn :: String -> IO ()的类型。

答案 2 :(得分:3)

fib :: Int -> Int fib 1 = 0 fib 2 = 1 fib n = fib (n-1) + fib (n-2) 是一个使用monad抽象来封装效果的示例。在这种情况下,操作的效果很重要,但它可能没有有意义的返回值,这是完全正常的。

我将举例说明。考虑每个人最喜欢的递归算法,Fibonacci序列:

fib

我们都知道这是计算这些数字的一种非常低效的方法,但 效率如何呢?如果我们使用较小的语言,我们可能会试图破解一个可变变量并在每次调用State时递增它。我们可以使用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) 以纯函数方式在Haskell中执行类似的操作。

让我们定义一个新版本的fib:

> 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)

现在我们可以同时计算第n个数字并计算调用次数:

modify (+1)

好的,这很好但是它如何回答这个问题?上述实现中要注意的要点是>>。这具有向计数器添加1的效果,但是它本身没有任何有用的结果。我们使用{{1}}对下一个操作进行排序,它有一个有用的结果,即计算。