[英]Global variables & the reader monad
I have a module where a global environment (defining certain constraints such as neighbor IP addresses, etc.) is created and initialized by calling an initializing function. 我有一个模块 ,通过调用初始化函数创建并初始化全局环境(定义某些约束,如邻居IP地址等)。 A number of subsequent functions should use these constraints when they are called.
许多后续函数在调用时应使用这些约束。
Whilst in principle I understand what the reader monad does I am not quite sure how I can apply this to my problem, esp. 虽然原则上我理解读者monad的作用,但我不太确定如何将其应用于我的问题,尤其是。
How it can be used to initialize an environment that is defined by the user and passed as data/arguments to the initializing function. 它如何用于初始化由用户定义并作为数据/参数传递给初始化函数的环境。 I mean, the reader monad has to get the actual values that make up the global immutable environment from somewhere.
我的意思是,读者monad必须从某个地方获得构成全局不可变环境的实际值。 I would like that the values are read from an initializing function call like
myinitial :: arg1 -> arg1 -> IOString
where subsequently arg1
and arg2
become global immutable data accessible to subsequent functions via the reader monad(?) 我希望从初始化函数调用中读取值,如
myinitial :: arg1 -> arg1 -> IOString
,其中arg1
和arg2
成为后续函数可通过reader monad(?)访问的全局不可变数据
How I can use these environment values as function arguments eg recvFrom s arg1
where arg1
is global immutable data from my environment. 我如何将这些环境值用作函数参数,例如
recvFrom s arg1
其中arg1
是来自我的环境的全局不可变数据。 Or if arg2 > arg1 then ... else ...
或者
if arg2 > arg1 then ... else ...
I could make a configuration file of course, but I feel that a configuration file will take away to much flexibility. 我当然可以制作一个配置文件,但我觉得配置文件会带来很大的灵活性。
[Edit] I understand about ask, but shouldn't there be additional "pointfree-like" ways so that the global/environment immutable can be omitted if the function signature has been defined right? [编辑]我理解问题,但是不应该有额外的“无点类似”方式,以便如果函数签名被定义正确,可以省略全局/环境不可变吗? How would I ie need to refactor my if-then-else to apply this .
我怎么会需要重构我的if-then-else来应用它 。
Most of your questions can be answered by inspecting the types and documentation for the ask and runReader functions. 您可以通过检查ask和runReader函数的类型和文档来回答大多数问题。
First, the ask
: 首先,
ask
:
ask :: Reader m r => m r
This returns the underlying read-only data wrapped in the monad. 这将返回包含在monad中的基础只读数据。 Cool, so that's how you will get to the state when you want to use it with other functions, in your example above:
很酷,所以当你想将它与其他函数一起使用时,你将如何进入状态,在上面的例子中:
do x <- ask
recvFrom s x
(depending on the type of recvFrom
, of course) (当然,取决于
recvFrom
的类型)
Next is the runReader
, this is how you give it that initial data you were talking about. 接下来是
runReader
,这就是你给出它所谈论的初始数据的方式。 It basically just runs the Reader
computation using the data it's given: 它基本上只使用给定的数据运行
Reader
计算:
runReader :: Reader r a -> r -> a
which means: run the computation (the first argument) with the read-only data of type r
(the second argument). 这意味着:使用类型为
r
的只读数据(第二个参数)运行计算(第一个参数)。 It finally will return the result type of the 1st argument, a
. 它最终将返回第一个参数的结果类型,
a
。 In your case, this may look like: 在您的情况下,这可能看起来像:
result = runReader computationUsingArg1Arg2 (arg1, arg2)
Then inside computationUsingArg1Arg2
you can read arg1
and arg2
via ask
. 然后里面
computationUsingArg1Arg2
你可以阅读arg1
和arg2
通过ask
。
Here's an example that may clear things up. 这是一个可以解决问题的例子。 First you need to import the Reader module:
首先,您需要导入Reader模块:
import Control.Monad.Reader
Now let's define some data structure (that we're going to use to hold a name and an age) 现在让我们定义一些数据结构(我们将用它来保存名称和年龄)
data Config = Config { name :: String, age :: Int }
Now define a function that works in the Reader monad (it's type is Reader Config (String, Int)
but we don't need to specify that - it can be inferred). 现在定义一个在Reader monad中工作的函数(它的类型是
Reader Config (String, Int)
但是我们不需要指定它 - 它可以被推断)。 All this function does is ask for the environment (of type Config
) and then extracts the fields and does something with them. 所有这个函数都要求环境(类型为
Config
),然后提取字段并对它们执行某些操作。
example = do
c <- ask
return ("Hello " ++ name c, 2 * age c)
Now we put it all together into a program. 现在我们将它们整合到一个程序中。 The first four lines after the do block allow the user to enter their name and age.
do块之后的前四行允许用户输入他们的姓名和年龄。 Then we build a
Config
structure using the user's inputs (we have to use read
to convert the variable _age
, which is a String
, into an Int
so that we can feed it to the Config
constructor) and execute example
with this environment, using the runReader
function. 然后我们使用用户的输入构建一个
Config
结构(我们必须使用read
将变量_age
,这是一个String
转换为Int
以便我们可以将它提供给Config
构造函数)并使用此环境执行example
,使用runReader
函数。 Finally, we use the result of this computation to generate some output. 最后,我们使用此计算的结果生成一些输出。
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)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.