什么是RWS Monad?什么时候使用 [英] What is the RWS Monad and when is it used

查看:128
本文介绍了什么是RWS Monad?什么时候使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在查找mtl库中的某些内容时碰到了RWS Monad及其MonadTransformer。这里没有真正的文档,我想知道这是什么以及它的用途。



我发现RWS是Reader的首字母缩写,作家,国家,这是三个单体变压器的堆叠。我无法弄清楚为什么这样比状态本身更好。

解决方案

最可行的原因是可测试性和以获得更精确的类型签名。



haskell的关键优势在于,您可以通过类型系统指定函数的功能。比较c#/ java类型:

  public int CSharpFunction(int param){... 



with a haskell one:

  someFunction :: Int  - > int 

haskell不仅告诉我们参数和返回类型所需的类型,还告诉我们该功能可能会影响。例如,它不能执行任何IO,也不能读取或更改任何全局状态,也不能访问任何静态配置数据。这两者对于c#函数都不是这样,但我们不能说明。



这对测试非常有帮助。我们知道,我们需要用haskell someFunction 来测试的唯一事情是,它是否获得了某些样本输入的预期输出。没有任何可能的设置要求,并且该功能永远不会为相同的输入给出不同的结果。这使得使用纯函数的测试变得非常简单。

然而,通常一个函数不能是纯粹的。例如,它可能需要访问一些全球信息以供阅读。我们可以在函数中添加另一个参数:

  readerFunc :: GlobalConfig  - > Int  - > Int 

但是使用monad通常更容易,因为它们更容易编写:

  readerFunc2 :: Int  - > Reader GlobalConfig Int 

测试这几乎与测试纯函数一样简单。我们只需要测试输入Int值和GlobalConfig读取器配置值的各种排列。

函数可能需要写出值(例如,用于日志记录)。这也可以用monad来完成:

  writerFunc :: Int  - > Writer String Int 

再次测试它与纯函数一样简单。我们只测试给定的 Int 输入是否返回适当的 Int ,以及正确的最终作者 String



另一个函数可能需要读取和更改状态:

  stateFunc :: Int  - > State GlobalState Int 

虽然这很难测试。我们不仅需要使用各种输入Ints和初始GlobalState来检查输出,还需要测试最终的GlobalState是否是正确的值。




现在考虑一个函数:


  • 需要从ProgramConfig数据类型读取
  • 将值写入字符串以进行日志记录

  • 使用并修改 ProgramState 值。



我们可以这样做:

  data ProgramData = ProgramData {pState :: ProgramState,pConfig :: ProgramConfig,pLogs :: String} 
complexFunction :: Int - > State ProgramData Int

但是,这种类型不是很准确。这意味着ProgramConfig可能会改变,但它不会。这也意味着该函数可能会使用pLogs值,而不会。此外,测试它更复杂,理论上,该功能可能会意外地更改程序配置,或者在其计算中使用当前的pLogs值。



这可以大大改善在这之后:

  betterFunction :: Int  - > RWS ProgramConfig字符串ProgramState Int 

这种类型非常准确。我们知道ProgramConfig只能读取。我们知道字符串只会被更改。按照预期,唯一需要读取和写入的部分是 ProgramState 。这很容易测试,因为我们只需要测试ProgramConfig,ProgramState和 Int 的不同组合来输入,并检查输出 Int 字符串 ProgramState 输出。我们知道我们不能意外地改变程序配置,或者在我们的计算中使用当前的程序日志值。




haskell类型系统在帮助我们,我们应该尽可能多地提供信息,这样我们才能在发现错误之前发现错误。



(请注意,此答案中的每个haskell类型可能相当于顶部的c#类型,具体取决于c#函数的实际实现方式)。


I ran across the RWS Monad and its MonadTransformer while looking up something in the mtl library. There is no real documentation there, and I was wondering what this is and where it gets used.

I've gotten as far as finding that RWS is an acronym fro Reader, Writer, State and that is is a stack of those three monad transformers. What I cannot figure out is why this is better then State by itself.

解决方案

The most practical reason for this is for testability and for more precise type signatures.

The key strength of haskell is how well you can specify what a function does through the type system. Compare the c#/java type:

public int CSharpFunction(int param) { ...

with a haskell one:

someFunction :: Int -> Int

The haskell one not only tells us the types needed for parameters and the return type, but also what the function may affect. For example, it can't do any IO, nor can it read or change any global state, or access any static configuration data. Neither may be true for the c# function, but we cannot tell.

This is a great help with testing. We know that the only things we need to test with the haskell someFunction is if it gets the expected outputs for some sample inputs. There isn't any possible setup required, and the function will never give a different result for the same input. This makes testing pretty simple with pure functions.


However, often a function cannot be pure. For example, it may need to access some global information just for reading. We could just add another parameter to the function:

readerFunc :: GlobalConfig -> Int -> Int

But it is often easier to use a monad, since they are easier to compose:

readerFunc2 :: Int -> Reader GlobalConfig Int

Testing this is almost as easy as testing a pure function. We just need to test various permutations of the input Int value, and the GlobalConfig reader configuration value.

A function may need to write out values (eg for logging). This can also be done with a monad:

writerFunc :: Int -> Writer String Int

Again testing this is almost as easy as for a pure function. We just test if for a given Int input, the appropriate Int is returned, as well as the right final writer String.

Another function may need to read and change state:

stateFunc :: Int -> State GlobalState Int

This is harder to test though. Not only do we have to check the output using various input Ints and initial GlobalStates, but we also need to test if the final GlobalState is the correct value.


Now consider a function which:

  • Needs to read from a ProgramConfig data type
  • Write values to a string for logging
  • Use and modify a ProgramState value.

We could do something like this:

data ProgramData = ProgramData { pState :: ProgramState, pConfig :: ProgramConfig, pLogs :: String }
complexFunction :: Int -> State ProgramData Int

However, that type isn't very accurate. It implies that the ProgramConfig may be changed, which it won't. It also implies that the function may use the pLogs value, which it won't. Also, testing it is more complex, as theoretically, the function could accidently change the program config, or use the current pLogs value in its computations.

This can be greatly improved upon with this:

betterFunction :: Int -> RWS ProgramConfig String ProgramState Int

This type is very accurate. We know that the ProgramConfig is only ever read from. We know the String is only ever changed. The only part that requires both reading and writing is the ProgramState, as expected. This is easier to test, as we only need to test different combinations of ProgramConfig, ProgramState and Int for input, and check the output Int, String and ProgramState for output. We know we cannot accidently change the program config, or use the current program log value in our computations.


The haskell type system is there to help us, we should give it as much information as we can so it can catch errors before we do.

(Note that every haskell type in this answer could be the equivalent to the c# type at the top, depending on how the c# function is actually implemented).

这篇关于什么是RWS Monad?什么时候使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆