使用状态 monad 更新和返回多个值
Updating and returning multiple values using the state monad
我正在为我正在编写的合同 DSL 开发合同评估器。合约 DSL 基于 Simon Peyton Jones financial combinators 论文。我对 Haskell 和 monad 比较陌生,我在使用 state monad 时遇到了问题。正如您在下面看到的,递归调用 evalAll2 直到到达 End。我在调用 evalC 时更新记录 ContractSt 中的三个变量,people 是合同各方的数量(由唯一 int 标识),balance 是合同中的金额,owner 是拥有的人合同。合同的评估方式如下:
bettingContract :: Contract
bettingContract
= until (Date (2018,12,13))
(cashIn 20 1
(cashIn 20 2
(time (Date (2018,12,15))
(pay 1 2 40 End)
End)
End)
End)
End
c1 = evalAll(bettingContract)
我想让代码做的是输出正在评估的合同,"output" 这是一条基于特定操作生成的消息,并在合同被执行后显示 ContractSt 的值评估。主要问题是我得到:
<interactive>:12:1: error:
* No instance for (Show
(Control.Monad.Trans.State.Lazy.StateT
ContractSt
Data.Functor.Identity.Identity
()))
arising from a use of `print'
当我尝试评估合同时。我知道我必须使用 evalState 来 return 最终结果(这是我想要的),并且我不是要为 () 创建一个实例,而是 returning最终状态以及合同,输出是我遇到最大困难的地方。
data ContractSt = ContractSt {
people :: [Person], balance :: Money, owner :: Person } deriving (Show)
evalAll :: Contract -> (Contract, OP, State ContractSt ()
evalAll c = evalAll2 c [] initialState
evalAll2 :: Contract -> OP -> State ContractSt () -> (Contract, OP, State
ContractSt ()
evalAll2 c o s
| c == End = (c, o, s)
| otherwise = evalAll2 nc (o ++ no) ns
where
(nc, no,ns) = evalC c
我没有读懂你的动机,但如果你的问题是如何转换三元组的最后一个元素,你可以轻松地为此编写一个函数:
mapLast3 f (a,b,x) = (a, b, f x)
这是一个完整的示例,其中 3 元组 (Int, Double, State String ())
通过 运行 具有硬编码初始状态的计算转换为 (Int, Double, String)
:
import Control.Monad.State
mapLast3 :: (x -> c) -> (a,b,x) -> (a,b,c)
mapLast3 f (a,b,x) = (a, b, f x)
myExecutor :: State String () -> String
myExecutor f = execState f "Hello"
execLast3 :: (a, b, State String ()) -> (a, b, String)
execLast3 = mapLast3 myExecutor
myStateFunction :: State String ()
myStateFunction = modify (++ " World")
myOriginalTuple :: (Int, Double, State String ())
myOriginalTuple = (42, 3.14, myStateFunction)
myFinalTuple :: (Int, Double, String)
myFinalTuple = execLast3 myOriginalTuple
main = print myFinalTuple
它将打印(42,3.14,"Hello World")
我正在为我正在编写的合同 DSL 开发合同评估器。合约 DSL 基于 Simon Peyton Jones financial combinators 论文。我对 Haskell 和 monad 比较陌生,我在使用 state monad 时遇到了问题。正如您在下面看到的,递归调用 evalAll2 直到到达 End。我在调用 evalC 时更新记录 ContractSt 中的三个变量,people 是合同各方的数量(由唯一 int 标识),balance 是合同中的金额,owner 是拥有的人合同。合同的评估方式如下:
bettingContract :: Contract
bettingContract
= until (Date (2018,12,13))
(cashIn 20 1
(cashIn 20 2
(time (Date (2018,12,15))
(pay 1 2 40 End)
End)
End)
End)
End
c1 = evalAll(bettingContract)
我想让代码做的是输出正在评估的合同,"output" 这是一条基于特定操作生成的消息,并在合同被执行后显示 ContractSt 的值评估。主要问题是我得到:
<interactive>:12:1: error:
* No instance for (Show
(Control.Monad.Trans.State.Lazy.StateT
ContractSt
Data.Functor.Identity.Identity
()))
arising from a use of `print'
当我尝试评估合同时。我知道我必须使用 evalState 来 return 最终结果(这是我想要的),并且我不是要为 () 创建一个实例,而是 returning最终状态以及合同,输出是我遇到最大困难的地方。
data ContractSt = ContractSt {
people :: [Person], balance :: Money, owner :: Person } deriving (Show)
evalAll :: Contract -> (Contract, OP, State ContractSt ()
evalAll c = evalAll2 c [] initialState
evalAll2 :: Contract -> OP -> State ContractSt () -> (Contract, OP, State
ContractSt ()
evalAll2 c o s
| c == End = (c, o, s)
| otherwise = evalAll2 nc (o ++ no) ns
where
(nc, no,ns) = evalC c
我没有读懂你的动机,但如果你的问题是如何转换三元组的最后一个元素,你可以轻松地为此编写一个函数:
mapLast3 f (a,b,x) = (a, b, f x)
这是一个完整的示例,其中 3 元组 (Int, Double, State String ())
通过 运行 具有硬编码初始状态的计算转换为 (Int, Double, String)
:
import Control.Monad.State
mapLast3 :: (x -> c) -> (a,b,x) -> (a,b,c)
mapLast3 f (a,b,x) = (a, b, f x)
myExecutor :: State String () -> String
myExecutor f = execState f "Hello"
execLast3 :: (a, b, State String ()) -> (a, b, String)
execLast3 = mapLast3 myExecutor
myStateFunction :: State String ()
myStateFunction = modify (++ " World")
myOriginalTuple :: (Int, Double, State String ())
myOriginalTuple = (42, 3.14, myStateFunction)
myFinalTuple :: (Int, Double, String)
myFinalTuple = execLast3 myOriginalTuple
main = print myFinalTuple
它将打印(42,3.14,"Hello World")