目前我在使用monad transformers时遇到了一些困难。我正在定义一些不同的非确定性关系,这些关系利用了转换器。不幸的是,我很难理解如何清晰地从一个有效的模型转换到另一个有效的模型。
假设这些关系是"foo“和"bar”。假设"foo“与As相关,Bs与Cs相关;假设"bar”与Bs相关,Cs与Ds相关。我们将根据"foo“来定义"bar”。更有趣的是,这些关系的计算将以不同的方式失败。(由于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函数了?
发布于 2013-05-15 07:52:59
我认为convert是一个很好的答案,并且使用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)无论如何,总体上看一下用于处理transformer堆栈(部分)上的自然转换的Control.Monad.Morph。我想说,这符合将一个函数“恰到好处”提升到堆栈中的定义。
https://stackoverflow.com/questions/16552316
复制相似问题