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中似乎强烈建议不要这样做.
通常的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变换器来说有点复杂,但它的工作方式与这个更简单的版本相同.