我有一个模块,通过调用初始化函数创建并初始化全局环境(定义某些约束,如邻居IP地址等)。许多后续函数在调用它们时应该使用这些约束。
虽然原则上我理解读者monad的作用,但我不太确定如何将其应用于我的问题,尤其是。
如何使用它来初始化由用户定义并作为数据/参数传递给初始化函数的环境。我的意思是,读者monad必须从某个地方获得构成全局不可变环境的实际值。我希望从myinitial :: arg1 -> arg1 -> IOString
之类的初始化函数调用中读取值,随后arg1
和arg2
成为后续函数可通过reader monad(?)
如何将这些环境值用作函数参数,例如recvFrom s arg1
其中arg1
是来自我的环境的全局不可变数据。或if arg2 > arg1 then ... else ...
我当然可以制作配置文件,但我觉得配置文件会带来很大的灵活性。
[编辑]我理解ask,但是不应该有额外的“无点类似”方式,这样如果功能签名被定义正确,可以省略全局/环境不可变吗?我如何需要重构我的if-then-else以应用this。
答案 0 :(得分:5)
通过检查ask和runReader函数的类型和文档,可以回答大部分问题。
首先,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
内,您可以通过arg1
阅读arg2
和ask
。
答案 1 :(得分: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
将变量_age
转换为String
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)