我发现monad变换器的一个问题是需要lift
操作进入正确的monad。这里有一个lift
并且没有坏,但有时会出现这样的函数:
fun = do
lift a
lift b
c
lift d
lift e
f
我希望能够编写这个函数:
fun = monadInvert $ do
a
b
lift c
d
e
lift f
这会使lift
的数量减半并使代码更清晰。
问题是:对于monad是monadInvert
可能的?应该如何创建这个功能?
加分点:为monad m
定义MonadIO
的实例。
这个问题的标题提到了排列:实际上,我们如何处理monad变换器堆栈的任意排列?
答案 0 :(得分:16)
嗯,首先,你实际上并不需要这么多提升。对于monad变换器,以下标识包含:
lift c >>= lift . f = lift (c >>= f)
lift c1 >> lift c2 = lift (c1 >> c2)
写这个并不罕见:
x <- lift $ do
{- ... -}
接下来是:当您使用 mtl 或 monadLib 等库(即基于类型类的库而不是变换器)时,您实际上可以直接访问大多数底层monad:
c :: StateT MyState (ReaderT MyConfig SomeOtherMonad) Result
c = do
x <- ask
y <- get
{- ... -}
最后,如果你真的需要大量提升尽管有这两点,你应该考虑编写自定义monad甚至使用完全不同的抽象。我发现自己使用自动机箭头进行有状态计算而不是状态monad。
答案 1 :(得分:10)
您可能对Tom Schrijvers和Bruno Oliveira的Monads, Zippers and Views, Virtualizing the Monad Stack感兴趣。
这并没有解决你关于减少升降机的观点,但这对你的“monad排列”来说是一个有趣的方法。问题
这是摘要:
这项工作旨在使monadic组件更具可重用性和可靠性 通过使用两种新技术来虚拟化monad来改变 堆栈:monad拉链和monad视图。 monad拉链是monad 通过忽略特定来创建虚拟monad堆栈的转换器 混凝土堆栈中的图层。 Monad视图提供了一个通用框架 对于monad堆栈虚拟化:他们采用monad zipper一步 进一步将其与各种其他虚拟化集成。 例如,特定视图允许限制访问monad 堆栈。此外,组件可以使用monad视图 提供类似于引用的调用机制来访问特定层 monad堆栈。有了这两种机制的组件要求 就monad堆栈形状而言,不再需要字面意思 反映在混凝土monad堆栈中,使这些组件更多 可重复使用且对变化具有鲁棒性。
答案 2 :(得分:1)
我很确定你所描述的内容对于IO来说是不可能的,IO总是最内在的monad:
来自MartinGrabmüller:Monad变形金刚一步一步,可在 http://www.grabmueller.de/martin/www/pub/
在本文档中,我们在eval6中调用liftIO来执行I / O操作。 在这种情况下,为什么我们需要解除?因为没有IO类 我们可以将类型实例化为。因此,对于I / O操作,我们 必须调用lift来向内发送命令
通常,对于比IO更少限制的monad,(例如Error和State)顺序对于语义仍然很重要,因此您不能仅仅为了使语法更方便而更改堆栈的顺序。
对于像Reader这样的一些monad(它们在堆栈中通勤),你的想法似乎并非显而易见。我不知道如何写它。我想这是一个类型类CentralMonad
ReaderT
是一个实例,有一些实现......