haskell - 变形金刚下的转型

标签 haskell monads lifting

目前我在使用 monad 转换器时遇到了一些困难。我正在定义一些使用变压器的不同的非确定性关系。不幸的是,我无法理解如何将一种有效的模型清晰地转换为另一种。

假设这些关系是“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功能如上图?

最佳答案

我认为转换是一个很好的答案,并使用 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 用于处理(部分)变压器堆栈的自然变换。我会说这符合将函数“足够”提升到堆栈中的定义。

关于haskell - 变形金刚下的转型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16552316/

相关文章:

Haskell Map函数实现问题

haskell - 使用 Reader 扩展 ServerPartT Monad

haskell - 用于广义多参数函数提升的类型类技巧

Scala奇怪的隐式装箱转换错误

c# - 如何分解一系列成员访问表达式?

haskell - 有没有办法使用 Cabal 来使 Haskell 包保持最新?

postgresql - 如何在 Haskell 中推广 Opaleye 查询(使用 Vinyl)?

json - 记录数据验证

haskell - 帮助读者单子(monad)

unit-testing - 如何扁平化IO(IO())?