7

假设我们想要使用ReaderT [(a,b)]monad Maybe,然后我们想要在列表中进行查找。

现在,一个简单且不常见的方法是:

第一种可能性

find a = ReaderT (lookup a)

但是,这似乎确实断言了有关 ReaderT 转换器如何工作的一些不平凡的事情。查看 Control.Monad.Reader 的源代码,很明显这很好用。但我还没有阅读任何支持这一点的文档。但是我们也可以这样写 find:

第二种可能性

find a = do  y <- ask 
             lift (lookup a y)

类似的想法也适用于包装MaybeT,StateT和. 通常我会写类似于第一个示例的内容,但大多数情况下,如何编写类似于第二个示例真的很明显,您甚至可能会说它更具可读性。所以我的问题是:像第一个例子这样的代码应该被认为是坏的吗?StateReader

4

3 回答 3

9

我认为第一种方式最大的问题是:

如果 mtl 作者(或您使用的任何转换器库)决定停止为 ReaderT 导出数据构造函数,那么它将停止工作。在从 mtl 1 到 mtl 2 的版本中,State monad 发生了这种情况,这很烦人。然而,ask它是 Reader 官方 api 的一部分,您应该计划保留它。

另一方面,我不会认为第一种方式是错误的。

于 2010-11-30T18:54:38.167 回答
3

至少有一个速度差异。

我写了一个程序,它使用随机生成作为状态,运行时必须生成大约 5000000 个随机值。现在考虑这两个掷骰子的函数:

random16  = State $ randomR (1,6) -- Using the internal representation
random16' = do
            s <- get
            (r,s') <- randomR (1,6) s
            put s'
            return r

对于第一个,程序运行大约 6 秒,而第二个要慢得多,大约需要 8 秒。我可以想象,它对于读者来说是相似的,所以当运行时很重要时,可以使用这个而不是更清晰的。我为此使用了严格的版本。

于 2010-12-01T06:02:45.750 回答
3

当前版本的mtl库(基于转换器库)在使用简单monadreader :: (r -> a) -> Reader r a时正是为此目的导出函数。Reader所以我们看到库的设计确实考虑到了这种用法。由于没有为 提供这样的功能ReaderT,我们可以肯定地说,官方支持的方法ReaderT是直接使用构造函数。

readerT :: Monad m => (r -> a) -> ReaderT r m a如果您说应该将类似物添加到库中,我会同意您的看法。这对于一致性和允许有朝一日在不破坏任何人的代码的情况下更改内部表示的可能性都有好处。

但就目前而言,你的“第一种可能性”是要走的路。

于 2010-12-05T10:26:25.777 回答