目前我对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
函数吗?
答案 0 :(得分:3)
我认为转换是一个很好的答案,使用Control.Monad.Morph
和Control.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
来处理变压器堆栈(部分)的自然变换。我认为这符合将“正常”功能提升到堆栈中的定义。