5

我有一个模块,其中通过调用初始化函数来创建和初始化全局环境(定义某些约束,例如邻居 IP 地址等)。许多后续函数在调用时应使用这些约束。

虽然原则上我了解 reader monad 的作用,但我不太确定如何将其应用于我的问题,尤其是。

  • 如何使用它来初始化由用户定义并作为数据/参数传递给初始化函数的环境。我的意思是,阅读器 monad 必须从某个地方获取构成全局不可变环境的实际值。我希望这些值是从初始化函数调用中读取的,例如myinitial :: arg1 -> arg1 -> IOStringwhere 随后arg1成为arg2全局不可变数据,后续函数可以通过 reader monad(?)

  • 我如何将这些环境值用作函数参数,例如我的环境recvFrom s arg1arg1的全局不可变数据在哪里。或者if arg2 > arg1 then ... else ...

我当然可以制作配置文件,但我觉得配置文件会带来很大的灵活性。

[编辑]我理解询问,但是如果函数签名已正确定义,是否应该有额外的“类似pointfree”的方式以便可以省略全局/环境不可变?我将如何需要重构我的 if-then-else 来应用this

4

2 回答 2

5

您的大多数问题都可以通过检查askrunReader函数的类型和文档来回答。

首先,ask

ask :: Reader m r => m r

这将返回包装在 monad 中的底层只读数据。很酷,所以当您想将它与其他功能一起使用时,这就是您将如何进入状态,在上面的示例中:

do x <- ask
   recvFrom s x

(当然取决于 的类型recvFrom

接下来是runReader,这就是你如何给它你正在谈论的初始数据。它基本上只是Reader使用它给出的数据运行计算:

runReader :: Reader r a -> r -> a

这意味着:使用类型的只读数据r(第二个参数)运行计算(第一个参数)。它最终将返回第一个参数的结果类型a. 在您的情况下,这可能如下所示:

result = runReader computationUsingArg1Arg2 (arg1, arg2)

然后在里面computationUsingArg1Arg2你可以阅读arg1arg2通过ask

于 2012-06-13T00:29:50.450 回答
4

这是一个可以解决问题的示例。首先你需要导入 Reader 模块:

import Control.Monad.Reader

现在让我们定义一些数据结构(我们将使用它来保存姓名和年龄)

data Config = Config { name :: String, age :: Int }

现在定义一个在 Reader monad 中工作的函数(它的类型是Reader Config (String, Int),但我们不需要指定它 - 它可以被推断出来)。这个函数所做的只是询问环境(类型Config),然后提取字段并对其进行处理。

example = do
    c <- ask
    return ("Hello " ++ name c, 2 * age c)

现在我们把它们放在一个程序中。do 块之后的前四行允许用户输入他们的姓名和年龄。然后我们Config使用用户的输入构建一个结构(我们必须使用read将变量_agea 转换为 aString以便Int我们可以将其提供给Config构造函数)并example使用该runReader函数在此环境中执行。最后,我们使用这个计算的结果来生成一些输出。

main = do
    putStrLn "Enter your name:"
    _name <- getLine
    putStrLn "Enter your age:"
    _age <- getLine
    let config = Config _name (read _age)
    let result = runReader example config
    putStrLn $ fst result
    putStrLn $ "Twice your age is: " ++ show (snd result)
于 2012-06-13T08:17:20.610 回答