变形金刚下的转型

时间:2013-05-14 20:31:11

标签: haskell monads lifting

目前我对monad变形金刚有点困难。我正在定义一些利用变压器的不同的非确定性关系。不幸的是,我无法理解如何从一个有效的模型转换到另一个有效的模型。

假设这些关系是“foo”和“bar”。假设“foo”将As和Bs与Cs联系起来;假设“bar”将Bs和Cs与Ds联系起来。我们将用“foo”来定义“bar”。为了使事情变得更有趣,这些关系的计算将以不同的方式失败。 (由于条形关系取决于foo关系,因此其失败情况是超集。)因此,我给出以下类型定义:

data FooFailure = FooFailure String
data BarFailure = BarSpecificFailure | BarFooFailure FooFailure
type FooM = ListT (EitherT FooFailure (Reader Context))
type BarM = ListT (EitherT BarFailure (Reader Context))

我希望能用以下函数签名编写关系:

foo :: A -> B -> FooM C
bar :: B -> C -> BarM D

我的问题是,在编写“bar”的定义时,我需要能够从“foo”关系中接收错误并在“bar”空间中正确地表示它们。所以我可以使用

形式的函数
convert :: (e -> e') -> ListT (EitherT e (Reader Context) a
                     -> ListT (EitherT e' (Reader Context) a

我甚至可以通过运行ListT,在EitherT上映射,然后重新组装ListT来编写那个小野兽(因为它发生m [a]可以转换为ListT m a)。但这似乎......凌乱。

有一个很好的理由我不能只运行一个变压器,在它下面做一些东西,并且通常“把它放回去”;我跑的变压器可能有效果,我不能神奇地撤消它们。但是有一些方法我可以将一个函数提升到一个变换器堆栈中,为我做一些工作,所以我不必编写上面显示的convert函数吗?

1 个答案:

答案 0 :(得分:3)

我认为转换是一个很好的答案,使用Control.Monad.MorphControl.Monad.Trans.Either它(几乎)写起来非常简单:

convert :: (Monad m, Functor m, MFunctor t)
           => (e -> e')
           -> t (EitherT e m) b -> t (EitherT e' m) b
convert f = hoist (bimapEitherT f id)

轻微的问题是ListT不是MFunctor的实例。我认为这是作者抵制ListT,因为它doesn't follow the monad transformer laws因为它很容易编写类型检查实例

instance MFunctor ListT where hoist nat (ListT mas) = ListT (nat mas)

无论如何,通常看一下Control.Monad.Morph来处理变压器堆栈(部分)的自然变换。我认为这符合将“正常”功能提升到堆栈中的定义。