我如何使用状态 monad 编写这个简单的代码?
How can I write this simple code using the state monad?
我是 Haskell 的初学者,我遇到过我想使用状态 monad 的情况。 (或者至少,我认为这就是我想要使用的。)状态 monad 有上百万个教程,但它们似乎都假定我的主要目标是在深层概念层面上理解它,并且因此,他们就在他们说如何用它实际开发软件的部分之前停下来。所以我正在寻找一个简化的实际示例的帮助。
下面是我当前代码的一个非常简单的版本。如您所见,我正在通过我的函数处理状态,我的问题只是如何使用 do
符号重写代码,这样我就不必这样做了。
data Machine = Register Int
addToState :: Machine -> Int -> Machine
addToState (Register s) a = Register $ s+a
subtractFromState :: Machine -> Int -> Machine
subtractFromState (Register s) a = Register (s-a)
getValue :: Machine -> Int
getValue (Register s) = s
initialState = Register 0
runProgram = getValue (subtractFromState (addToState initialState 6) 4)
代码模拟了一个简单的抽象机,它有一个寄存器,以及添加到寄存器、减去寄存器和获取寄存器值的指令。最后的 "program" 将寄存器初始化为 0,加 6,减 4 和 returns 结果,当然是 2.
我理解 state monad 的目的(或者至少我认为是这样),我希望它能让我重写这个,这样我最终得到类似
的东西
runProgram :: ???????
runProgram = do
put 0
addToState 6
subtractFromState 4
value <- getValue
return value
然而,尽管我已经阅读了所有教程,但我仍然不太清楚如何将我的代码转换成这种形式。
当然,我的实际机器的状态要复杂得多,而且我还在传递它的输出(将传递到另一台机器)和各种其他东西,所以我很想简化它。知道如何为这个简化的例子做这件事会有很大的帮助。
更新: 在 Lee 的出色回答之后,我现在知道如何做到这一点,但是当我有多个交互机器时,我仍然坚持如何以相同的优雅形式编写代码.我已经在 a new question.
中询问过这个问题
首先,您需要将现有函数转换为 return State Machine a
值:
import Control.Monad.State.Lazy
data Machine = Register Int
addToState :: Int -> State Machine ()
addToState i = do
(Register x) <- get
put $ Register (x + i)
subtractFromState :: Int -> State Machine ()
subtractFromState i = do
(Register x) <- get
put $ Register (x - i)
getValue :: State Machine Int
getValue = do
(Register i) <- get
pure i
然后你可以将它们组合成一个有状态的计算:
program :: State Machine Int
program = do
addToState 6
subtractFromState 4
getValue
最后你需要可以 运行 这个计算 evalState
得到最终结果并丢弃状态:
runProgram :: Int
runProgram = evalState program (Register 0)
我是 Haskell 的初学者,我遇到过我想使用状态 monad 的情况。 (或者至少,我认为这就是我想要使用的。)状态 monad 有上百万个教程,但它们似乎都假定我的主要目标是在深层概念层面上理解它,并且因此,他们就在他们说如何用它实际开发软件的部分之前停下来。所以我正在寻找一个简化的实际示例的帮助。
下面是我当前代码的一个非常简单的版本。如您所见,我正在通过我的函数处理状态,我的问题只是如何使用 do
符号重写代码,这样我就不必这样做了。
data Machine = Register Int
addToState :: Machine -> Int -> Machine
addToState (Register s) a = Register $ s+a
subtractFromState :: Machine -> Int -> Machine
subtractFromState (Register s) a = Register (s-a)
getValue :: Machine -> Int
getValue (Register s) = s
initialState = Register 0
runProgram = getValue (subtractFromState (addToState initialState 6) 4)
代码模拟了一个简单的抽象机,它有一个寄存器,以及添加到寄存器、减去寄存器和获取寄存器值的指令。最后的 "program" 将寄存器初始化为 0,加 6,减 4 和 returns 结果,当然是 2.
我理解 state monad 的目的(或者至少我认为是这样),我希望它能让我重写这个,这样我最终得到类似
的东西runProgram :: ???????
runProgram = do
put 0
addToState 6
subtractFromState 4
value <- getValue
return value
然而,尽管我已经阅读了所有教程,但我仍然不太清楚如何将我的代码转换成这种形式。
当然,我的实际机器的状态要复杂得多,而且我还在传递它的输出(将传递到另一台机器)和各种其他东西,所以我很想简化它。知道如何为这个简化的例子做这件事会有很大的帮助。
更新: 在 Lee 的出色回答之后,我现在知道如何做到这一点,但是当我有多个交互机器时,我仍然坚持如何以相同的优雅形式编写代码.我已经在 a new question.
中询问过这个问题首先,您需要将现有函数转换为 return State Machine a
值:
import Control.Monad.State.Lazy
data Machine = Register Int
addToState :: Int -> State Machine ()
addToState i = do
(Register x) <- get
put $ Register (x + i)
subtractFromState :: Int -> State Machine ()
subtractFromState i = do
(Register x) <- get
put $ Register (x - i)
getValue :: State Machine Int
getValue = do
(Register i) <- get
pure i
然后你可以将它们组合成一个有状态的计算:
program :: State Machine Int
program = do
addToState 6
subtractFromState 4
getValue
最后你需要可以 运行 这个计算 evalState
得到最终结果并丢弃状态:
runProgram :: Int
runProgram = evalState program (Register 0)