使用在读取后不会更改的参数

bea*_*rdc 2 monads haskell purely-functional pure-function

我正在学习Haskell并编写一个解决玩具问题的程序.在从文件读取参数之后,程序使用在运行时不会更改的参数k.我对使用纯函数非常陌生,我想尽可能多地编写纯函数.

我有一个数据类型Node,用于比较节点,获取节点的后代等等.目前,所有这些函数都将参数k作为参数,例如

compare k node1 node2 = ...
desc k node = ...
Run Code Online (Sandbox Code Playgroud)

每当我必须递归调用函数中的任何一个时,我必须重复k参数.这似乎是多余的,因为k对于这些函数永远不会有不同的值,因为它使类型签名的可读性降低,并且如果可能的话我想重构它.

是否有任何策略可以使用纯函数执行此操作,还是仅仅是我必须处理的限制?

我想到了什么

之前我在顶级硬编码k,它似乎工作(我能够在函数中使用k而不需要它作为显式参数).但是,一旦我需要从文件中读取输入,这显然是不可行的.

另一种可能的策略是在函数中定义所有这些main函数,但在Haskell中似乎强烈建议不要这样做.

Joh*_*n L 5

通常的Haskell方法是使用Readermonad.一种思考方式Reader是它提供对环境的访问的计算.它可以定义为

newtype Reader r a = Reader { runReader :: r -> a }
Run Code Online (Sandbox Code Playgroud)

所以你的函数会有类型

compare :: Node -> Node -> Reader k Ordering -- or Bool, or whatever the return value is

desc :: Node -> Reader k String -- again, guessing at the output type.
Run Code Online (Sandbox Code Playgroud)

Reader计算中,使用该函数ask :: Reader r r来访问参数.

在顶层,您可以使用运行Reader计算runReader theComputation env

这通常比明确传递参数更好.首先,任何不需要环境的函数都可以作为普通函数编写,而不必将其作为参数.如果它调用另一个确实使用环境的函数,monad会自动提供它,而不需要你做额外的工作.

您甚至可以定义类型同义词,

type MyEnv = Reader Env
Run Code Online (Sandbox Code Playgroud)

并将其用于您的函数类型签名.然后,如果您需要更改环境,则只需更改一种类型,而不是更改所有类型的签名.

标准库的定义对于处理monad变换器来说有点复杂,但它的工作方式与这个更简单的版本相同.