使用状态 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")