5

当我设计我的编程模型时,我总是陷入困境,哪种方法更好:

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

使用一个monad而不是另一个monad有什么好处和权衡?这有关系吗?性能怎么样?

4

1 回答 1

6

在一般情况下,monad 转换器的不同排序会导致不同的行为,但正如评论中所指出的,对于“状态”和“读者”这两个排序,我们有以下同构到新类型:

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

然后当使用 GHC 8.6.4 编译时-O2,新类型 - 显然 - 优化了,如果您将签名更改为,这将生成完全相同的核心:

foo :: ReaderT Int (State Double) Int

除了要foo翻转的两个参数。因此,至少在这个简单的示例中,根本没有性能差异。

从风格上讲,您可能会遇到一种排序导致代码比另一种更好看的情况,但通常它们之间不会有太多选择。特别是,像上面这样的基本一元动作在任何一种排序下看起来都完全相同。

没有充分的理由,我倾向于支持#2,主要是因为Env -> MyState -> (a, MyState)看起来对我来说更自然。

于 2019-06-03T05:29:31.357 回答