haskell StateT over Reader和ReaderT over State之间是否存在显著差异?

xxhby3vn  于 2023-02-09  发布在  其他
关注(0)|答案(1)|浏览(138)

当我设计编程模型时,我总是左右为难,哪种方法更好:

type MyMonad1 = StateT MyState (Reader Env)
type MyMonad2 = ReaderT Env (State MyState)

使用一个单子栈和使用另一个单子栈之间有什么好处和权衡?这有关系吗?性能呢?

yshpjwxd

yshpjwxd1#

一般情况下,单子变换器的不同排序会导致不同的行为,但正如评论中所指出的,对于“state”和“reader”这两种排序,我们有以下同构直到newtype:

StateT MyState (Reader Env) a  ~  MyState -> Env -> (a, MyState)
ReaderT Env (State MyState) a  ~  Env -> MyState -> (a, MyState)

因此,唯一的区别是参数顺序不同,这两个单子在其他方面是等价的。
至于性能,如果不对实际代码进行基准测试,就很难确定。但是,作为一个数据点,如果考虑下面的一元操作:

foo :: StateT Double (Reader Int) Int
foo = do
  n <- ask
  modify (* fromIntegral n)
  gets floor

那么当使用-O2编译GHC 8.6.4时,新的类型--显然--被优化掉了,这会生成完全相同的Core,如果你把签名改为:

foo :: ReaderT Int (State Double) Int

除了foo的两个参数被翻转了,所以,至少在这个简单的例子中,没有任何性能上的差异。
从风格上来说,你可能会遇到这样的情况:一种排序方式比另一种排序方式产生的代码看起来更漂亮,但通常在它们之间没有太多可供选择的。特别是,像上面这样的基本一元动作在两种排序方式下看起来完全一样。
没有什么好的理由,我倾向于选择#2,主要是因为Env -> MyState -> (a, MyState)在我看来更自然。

相关问题