gpt4 book ai didi

Haskell:Monad 变压器和全局状态

转载 作者:行者123 更新时间:2023-12-02 11:06:09 26 4
gpt4 key购买 nike

我正在尝试学习 Haskell。我正在尝试编写一个包含“全局状态”的程序:Vars。我想在每次调用函数时更改状态的一个组成部分(例如 var1)。更改可以是组件上的简单函数(例如+4)。此外,它还会打印出更改的组件。这是我到目前为止所做的(但我被困住了)。编辑:运行代码后我想查看全局状态的最新版本。

import Control.Monad.State
import Control.Monad.IO.Class (liftIO)

data Vars = Vars {
var1 :: Int,
var2 :: Float
} deriving (Show)

sample :: StateT Vars IO a
sample = do
a <- change
liftIO $ print a
-- I want to call change again and apply more change to the state


change :: StateT Vars IO a
change = do
dd <- get
-- I don't know what to do next!

main = do
runStateT sample (Vars 20 3)
evalStateT sample (Vars 20 3)

最佳答案

让我们尝试从更简单的小部分开始逐步解决您的问题。这是编程中的重要技能,FP 会以很好的方式教你这项技能。此外,使用 State monad,尤其是 monad-transformers 中的几种效果,可以帮助您推理效果并更好地理解事物。

  1. 您想要更新不可变数据类型内的var1。这只能通过创建新对象来完成。那么让我们编写这样的函数:

    plusFour :: Vars -> Vars
    plusFour (Vars v1 v2) = Vars (v1 + 4) v2

    Haskell 中存在一些方法可以将这个函数编写得更短,尽管不太容易理解,但我们现在不关心这些事情。

  2. 现在您想在 State monad 中使用此函数来更新不可变状态并通过此模拟可变性。仅通过查看其类型签名就可以了解此函数的哪些信息:change::StateT Vars IO a?我们可以说这个函数有几个作用:它可以访问 Vars 状态并且可以执行任意 IO 操作。此函数还返回 a 类型的值。嗯,最后这个有点奇怪。什么是a?这个函数应该返回什么?在命令式编程中,该函数的类型为 voidUnit。它只是事情,并不会返回所有内容。仅更新上下文。所以它的结果类型应该是()。它可以是不同的。例如,我们可能希望在更改后返回新的 Vars。但这在编程中通常是不好的方法。这使得这个函数变得更加复杂。

  3. 在我们了解函数应该具有什么类型(尝试始终从定义类型开始)之后,我们就可以实现它。我们想改变我们的状态。有些函数与我们上下文的有状态部分一起运行。基本上,您对这个感兴趣:

    modify :: Monad m => (s -> s) -> StateT s m ()

    modify 函数采用更新状态的函数。运行此函数后,您可以观察到状态根据传递的函数进行修改。现在change可以这样写:

    change :: StateT Vars IO ()
    change = modify plusFour

    您可以实现modify(因此仅使用putget函数来实现change,这是一个很好的练习初学者)。

  4. 现在让我们从其他函数调用 change 函数。在这种情况下,调用意味着什么?这意味着您执行单子(monad)操作change。这个操作改变了你的上下文,你不关心它的结果,因为它是()。但是,如果您在 change 之后运行 get 函数(它将整个状态绑定(bind)到变量),您可以观察到新的变化。如果您只想打印更改的组件,例如 var1,您可以使用 gets 函数。再说一遍,sample 应该具有什么类型?它应该返回什么?如果在调用方,您只对结果状态感兴趣,那么,它应该是这样的 () :

    sample :: StateT Vars IO ()
    sample = do
    change
    v1 <- gets var1
    liftIO $ print v1
    change
    v1' <- gets var1
    liftIO $ print v1' -- this should be v1 + 4

这应该会让您对正在发生的事情有一些了解。 Monad 转换器需要一些时间来习惯它们,尽管它是一个强大的工具(不完美但非常有用)。

作为旁注,我想补充一点,使用常见的 Haskell 设计模式可以更好地编写这些函数。但您现在不需要关心这些,只需尝试了解这里发生了什么即可。

关于Haskell:Monad 变压器和全局状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45163246/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com