编写Monad Transformer,它真的需要这么多硬编码实例

时间:2016-02-20 18:45:39

标签: haskell monads monad-transformers

我是monad变压器用户,第一次使用monad变压器编写器....我觉得我做了一些不必要的事情。

我们正在开发一个具有多个数据库表的项目,并且将该组硬编码到不同的monad堆栈中变得难以处理,因此我们决定将其分解为不同的可插入monad变换器,允许我们在函数类型级别进行选择,像这样

doSomething::(HasUserTable m, HasProductTable m)=>Int->m String

(HasXTable是类,XTableT是具体的monad变换器)。这些单独的monad变换器可以完全模块化的方式插入或移除,并存储数据库句柄,需要ResourceT等....

我的第一次尝试是将ReaderT包裹起来,它将用于保存数据库句柄。很明显,这不会起作用,因为ReaderT(和StateT等)不能在不使用硬编码“升力”链的情况下堆叠,从而打破了堆栈元素的可插拔模块性。

唯一的解决方案似乎是编写完全独立的ReaderT monad副本,每个副本允许访问较低级别的其他副本。这是有效的,但解决方案充满了样板代码,就像这样

class HasUserTable m where
    getUser::String->m User

newtype UserTableT m r = UserTableT{runUserTableT::String->m r}

--Standard monad instance stuff, biolerplate copy of ReaderT
instance Functor m=>Functor (UserTableT m) where....
instance Applicative m=>Applicative (UserTableT m) where....
instance Monad m=>Monad (UserTableT m) where....
instance Monad m=>HasUserTable (UserTableT m) where....

--Gotta hardcode passthrough rules to every other monad transformer
--in the world, mostly using "lift"....
instance MonadTrans BlockCacheT where....
instance (HasUserTable m, Monad m)=>HasUserTable (StateT a m)....
instance (HasUserTable m, Monad m)=>HasUserTable (ResourceT m)....
.... etc for all other monad transformers

--Similarly, need to hardcode passthrough rules for all other monads
--through the newly created one
instance MonadResource m=>MonadResource (UserTableT m) where....
instance MonadState a m=>MonadState a (UserTableT m) where....
instance (MonadBaseControl IO m) => MonadBaseControl IO (UserTableT m)....
.... etc for all other monad transformers

更糟糕的是,我们需要为我们添加的每个新monad变换器添加更多直通规则(即 - 我们添加的每个新表都需要通过所有其他表monad变换器,因此我们需要n ^ 2个实例声明!)

有更简洁的方法吗?

1 个答案:

答案 0 :(得分:8)

是的,这是monad变换器的问题之一:当你添加一个新的变换器时,你必须编写越来越多的样板实例。它每次都是 n 个实例,总共 O(n ^ 2)个实例。例如,您可以观察此缩放问题in the mtl source code。 Monad变压器不易扩展。

现在,我们每天使用的monad中有很大一部分可以表示为mtl提供的变换器的某种组合,这意味着其他人已经完成了编写所有这些无聊的工作实例。但是那些变形金刚当然不会涵盖每个 monad,而且每当你需要自己编写时,你都会被咬伤。

这就是为什么正在努力设计新的方法来实现打字。 Haskell的一个很好的例子是Kiselyov等人的extensible-effects library,它基于免费monad 采用代数方法来实现打字效果。这个库的设计在两篇文章中进行了描述:An Alternative to Monad Transformers花了一些时间来描述mtl方法的问题,More Extensible Effects描述了库的更新和优化实现。< / p>

如果您想了解安全性和可扩展效果输入的效果,请参阅Edwin Brady的effects library了解Idris语言。有很多资源可以解释effectstutorial,原始的Programming and Reasoning with Algebraic Effects文章,Resource-dependent Algebraic Effects描述effects的一些新功能。可能还有一些我在这个列表中忘记的资源。