Joh*_*ler 27 haskell monad-transformers
我在mtl库中查找了一些东西时遇到了RWS Monad及其MonadTransformer.那里没有真正的文档,我想知道这是什么以及它在哪里使用.
我发现RWS是Reader,Writer,State的首字母缩略词,这就是这三个monad变换器的堆栈.我无法弄清楚为什么这比国家本身更好.
Dav*_*ani 30
最实际的原因是可测试性和更精确的类型签名.
haskell的关键优势在于你可以通过类型系统指定函数的功能.比较c#/ java类型:
public int CSharpFunction(int param) { ...
Run Code Online (Sandbox Code Playgroud)
有一个haskell之一:
someFunction :: Int -> Int
Run Code Online (Sandbox Code Playgroud)
haskell不仅告诉我们参数和返回类型所需的类型,还告诉我们函数可能会影响的类型.例如,它不能执行任何IO,也不能读取或更改任何全局状态,也不能访问任何静态配置数据.c#函数可能都不是,但我们无法分辨.
这对测试有很大帮助.我们知道,我们需要用haskell测试的唯一事情someFunction
是它是否获得了某些样本输入的预期输出.不需要任何可能的设置,并且该功能永远不会为相同的输入提供不同的结果.这使得纯函数的测试非常简单.
但是,通常功能不能是纯粹的.例如,它可能需要访问一些仅供阅读的全局信息.我们可以在函数中添加另一个参数:
readerFunc :: GlobalConfig -> Int -> Int
Run Code Online (Sandbox Code Playgroud)
但是使用monad通常更容易,因为它们更容易构成:
readerFunc2 :: Int -> Reader GlobalConfig Int
Run Code Online (Sandbox Code Playgroud)
测试这几乎就像测试纯函数一样简单.我们只需要测试输入Int值和GlobalConfig reader配置值的各种排列.
函数可能需要写出值(例如,用于记录).这也可以用monad完成:
writerFunc :: Int -> Writer String Int
Run Code Online (Sandbox Code Playgroud)
再次测试这几乎就像纯函数一样简单.我们只测试对于给定的Int
输入,Int
是否返回适当的输入,以及正确的最终作者String
.
另一个函数可能需要读取和更改状态:
stateFunc :: Int -> State GlobalState Int
Run Code Online (Sandbox Code Playgroud)
但这很难测试.我们不仅需要使用各种输入Ints和初始GlobalStates来检查输出,而且我们还需要测试最终的GlobalState是否是正确的值.
现在考虑一个函数:
ProgramState
值.我们可以这样做:
data ProgramData = ProgramData { pState :: ProgramState, pConfig :: ProgramConfig, pLogs :: String }
complexFunction :: Int -> State ProgramData Int
Run Code Online (Sandbox Code Playgroud)
但是,这种类型不是很准确.这意味着ProgramConfig可能会被更改,但它不会.它还暗示该函数可能使用pLogs值,而不是.此外,测试它更复杂,理论上,该函数可能会意外地更改程序配置,或在其计算中使用当前的pLogs值.
这可以大大改善:
betterFunction :: Int -> RWS ProgramConfig String ProgramState Int
Run Code Online (Sandbox Code Playgroud)
这种类型非常准确.我们知道ProgramConfig只能从中读取.我们知道这String
只会改变.ProgramState
正如预期的那样,唯一需要阅读和写作的部分.这是更容易测试,因为我们只需要测试ProgramConfig,ProgramState和不同的组合Int
输入,并检查输出Int
,String
并且ProgramState
输出.我们知道我们不能无意中更改程序配置,或在我们的计算中使用当前程序日志值.
haskell类型系统可以帮助我们,我们应该尽可能多地提供它,以便它可以在我们做之前发现错误.
(请注意,此答案中的每个haskell类型可能等同于顶部的c#类型,具体取决于c#函数的实际实现方式).