2010-11-30 51 views
7

假设我们想要在Maybe monad上使用ReaderT [(a,b)],然后我们要在列表中进行查找。是否应该将函数包装到单体变压器中被认为是不好的做法?

现在一个简单的,并没有太罕见的方式是这样的:

第一种可能性

find a = ReaderT (lookup a) 

但是它似乎像这样断言关于如何ReaderT变压器厂的一些不平凡的事。看看Control.Monad.Reader的源代码很明显,这工作得很好。但是我没有阅读任何支持这一点的文档。然而,我们也可以这样写找到这样的:

第二种可能性

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

类似的想法保持包装MaybeTStateTStateReader。通常我会写第一个例子,但大多数情况下很明显,如何像第二个例子那样编写它,甚至可以说它更具可读性。所以我的问题是:应该像第一个例子的代码被认为是坏的?

+2

你也可以写`find a = lift。 lookup a =“ask”,与第二个选项一样清晰(恕我直言),但更短。 – 2010-11-30 19:35:14

+0

或使用fmap:find a = fmap(lookup a)ask = lookup a <$> ask – urso 2010-12-01 02:01:32

回答

3

MTL库的当前版本 - 这是基于变压器库 - 使用简单Reader单子时导出功能reader :: (r -> a) -> Reader r a出于这样的目的。所以我们看到图书馆的设计确实考虑了这种用法。由于ReaderT没有提供这种功能,所以我们可以放心地说,ReaderT正式支持的方式是直接使用构造函数。

如果你说类似的readerT :: Monad m => (r -> a) -> ReaderT r m a应该被添加到库中,我会同意你的意见。这对于一致性以及允许在不破坏任何人的代码的情况下改变内部表示的可能性来说都是好的。

但是现在,你的“第一种可能性”是要走的路。

9

我想用第一种方式的最大问题是:

如果MTL作者(或任何变压器库使用),决定停止出口为ReaderT数据构造那么它就会停止工作。这种情况发生在mtl 1到mtl 2版本的状态monad中,这很烦人。而ask是Reader的官方API的一部分,你应该计划它坚持下去。

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

3

至少有一个速度差。

我写了一个program,它使用随机gen作为状态,并且在运行时必须产生大约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秒。我可以想象,它对读者来说是相似的,所以当运行时很重要时,可以使用这个而不是更清晰。我为此使用了严格的版本。

相关问题