【问题标题】:Updating and returning multiple values using the state monad使用 state monad 更新和返回多个值
【发布时间】:2019-01-04 18:33:12
【问题描述】:

我正在为我正在编写的合同 DSL 开发合同评估器。合同 DSL 基于 Simon Peyton Jones 金融组合文件。我对 Haskell 和 monads 比较陌生,而且我遇到了 state monad 的问题。正如您在下面看到的,递归调用 evalAll2 直到到达 End。我在记录 ContractSt 中有三个变量,它们在调用 evalC 时更新,people 是合同的参与方数量(由唯一的 int 标识),balance 是合同中的金额,所有者是拥有的人合同。合同的评估如下:

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)

我希望代码做的是输出正在评估的合同,“输出”是基于特定操作生成的消息,并在合同评估后显示 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 来返回最终结果(这是我想要的),并且我不是试图为 show for () 创建一个实例,而是与合同一起返回最终状态,并且输出是我遇到最大困难的地方。

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 

【问题讨论】:

  • 这是问题“如何从State Foo () 获得最终的Foo?”的长格式版本吗?或者是如何在给定函数f :: x -&gt; c的情况下将元组(a,b,x)变成元组(a,b,c)
  • 是的更多是后者,我想在一个元组中返回 Contractst 的最终结果,其中包含当前正在评估的合约和输出消息。

标签: haskell state monads


【解决方案1】:

我没有阅读您的动机,但如果您的问题是如何转换 3 元组的最后一个元素,您可以轻松地为此编写一个函数:

mapLast3 f (a,b,x) = (a, b, f x)

这是一个完整的示例,其中一个三元组 (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")

【讨论】:

  • 应该给三元组一个 Functor 实例 stackoverflow.com/questions/36460833/… 和一个 Foldable 一个,而我们正在使用它。
  • 感谢您的帮助,只是一个简单的问题。当我使用 execState 时,状态按预期返回,但是当我尝试 evalState 时,它​​返回 ()。为什么是这样?它不应该返回最终状态吗?
  • evalState 返回最后一个结果。 execState 返回最后一个状态。 runState 返回两者的元组。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-09
  • 2011-05-07
  • 1970-01-01
  • 1970-01-01
  • 2022-11-22
  • 1970-01-01
相关资源
最近更新 更多