提取嵌套的 ExceptION monad 转换器的左值

Extract left values of nested ExceptT monad transfomers

是否可以创建具有以下签名的函数?

ExceptT A (ExceptT B m) () -> ExceptT B m A

我的想法是这样的,因为 x 是上述函数的参数(而且我很讨厌 monad,所以它很可能是错误的)。

runExceptT x :: ExceptT B m (Either A ())

但是后来我坚持得到像 Either A () -> A 这样的东西,它本质上必须考虑 Right,因此要么失败,要么 return A 的某个常数(我没有)。

但是,我知道 x 的原始值恰好是左值之一,否则定义将无限循环。

简答:那不可能。

长答案:ExceptT e m a 包含一个 m (Either e a),因此 ExceptT A (ExceptT B m) () 包含一个 ExceptT B m (Either A ()),后者又包含一个 m (Either B (Either A ()))。问题是:我们怎样才能从那里得到一个 m (Either B A)

让我们忘记 m,因为它并不重要。我们想要一个函数:

awesomeFunction :: Either B (Either A ()) -> Either B A

类型 Either B (Either A ()) 的值可以是 Left bRight (Left a)Right (Right ()),这意味着您可以有一个 A、一个 B 或者什么都没有。

Either B A 类型的值可以是 Left bRight a。让我们尝试实现 awesomeFunction.

awesomeFunction (Left b) = Left b
awesomeFunction (Right (Left a)) = Right a
awesomeFunction (Right (Right ()) = ???

在我们什么都没有的情况下(一个Right (Right ()),我们不能凭空创建一个A,所以不可能创建这个函数。

OP 在评论中澄清:

I have some value [x] that is of type ExceptT B m (). That value may be either left or right. However, then I do forever x and then it becomes left at some point (m is StateT) or it never terminates.

问题是您过早地将结果类型固定为 ()。当你 运行 一个动作 forever 时,你会得到任何你想要的结果类型。特别是,您可以将左侧和右侧固定为同一类型,突然一切正常。也就是说,你可以这样写

either return return =<< runExceptT (forever x)

你最终会得到一个 returns 异常发生的动作。