Haskell 具有不同错误类型的嵌套EITHER

Haskell 具有不同错误类型的嵌套EITHER,haskell,typeclass,either,Haskell,Typeclass,Either,我有一个包含不同错误类型的嵌套数据库,看起来像: Either e1 (Either e2 a) 我想要一个函数,它的功能如下: Either e1 (Either e2 a) -> Either e2 a 更一般地说,是否有与此模式匹配的typeclass?您的函数是不可能的! 你所要求的并没有实际意义。让我们看看函数的类型: f :: Either e1 (Either e2 a) -> Either e2 a f :: (e1 -> e2) -> Either

我有一个包含不同错误类型的嵌套数据库,看起来像:

Either e1 (Either e2 a)
我想要一个函数,它的功能如下:

Either e1 (Either e2 a) -> Either e2 a
更一般地说,是否有与此模式匹配的typeclass?

您的函数是不可能的!
你所要求的并没有实际意义。让我们看看函数的类型:

f :: Either e1 (Either e2 a) -> Either e2 a
f :: (e1 -> e2) -> Either e1 (Either e2 a) -> Either e2 a
f g = either (Left . g) id
f :: Either e1 (Either e2 a) -> Either (Either e1 e2) a
f = either (Left . Left) (either (Left . Right) Right)
假设这个函数是total(Haskell中的绝大多数函数都应该是total),我们需要为任何类型的
或e1(或e2a)
的输入生成一个类型为
或e2a
的值。为了实现这一点,让我们考虑输入的所有“形状”。

结果表明,
e1(e2a)
类型的值可以有三种可能的形状:

Left _
Right (Left _)
Right (Right _)
底部的两个形状很容易处理。事实上,我们可以将任何
值映射到自身:

f (Right x) = x
但是,这不处理外部<代码>左侧情况。我们可以从编写模式开始:

f (Left x) = ???
在上面的模式中,我们得到一个值,
x
,类型为
e1
。我们需要生成类型为
的值,要么是e2a
。这意味着我们基本上需要具有以下类型的函数:

g :: e1 -> Either e2 a
但是等等!这种类型显然不可能满足,因为我们需要一个
e2
a
,但我们只有一个
e1
。因此,我们无法实现这种情况(假设我们不无限循环或使用
error
undefined
)。我们被卡住了

解决方案1:提供更多信息 如果不知道您实际上在做什么,就很难为这个问题提供一个好的解决方案。我至少可以提供一些可能性,其中一个可能与您的用例相关

一个简单的解决方案是提供一种将
e1
值映射到
e2
的方法。这样,我们可以将所有错误标准化为
e2
。在
功能的帮助下,实现这一点非常容易:

f :: Either e1 (Either e2 a) -> Either e2 a
f :: (e1 -> e2) -> Either e1 (Either e2 a) -> Either e2 a
f g = either (Left . g) id
f :: Either e1 (Either e2 a) -> Either (Either e1 e2) a
f = either (Left . Left) (either (Left . Right) Right)
您也可以通过将映射函数应用到外部
的左侧,然后使用monadic
join
函数合并两个层来完成此操作:

import Data.Bifunctor

f :: (e1 -> e2) -> Either e1 (Either e2 a) -> Either e2 a
f g = join . first g
解决方案2:更改结果类型 我们可以处理这个问题的另一种方法是调整结果,对这两种可能性进行编码。我们可以生成一个类型为
或(e1 e2或)a
的值来保存任何一个可能的错误。使用
函数编写此代码也相当容易:

f :: Either e1 (Either e2 a) -> Either e2 a
f :: (e1 -> e2) -> Either e1 (Either e2 a) -> Either e2 a
f g = either (Left . g) id
f :: Either e1 (Either e2 a) -> Either (Either e1 e2) a
f = either (Left . Left) (either (Left . Right) Right)
但是,使用模式匹配而不是
编写可能更清晰:

f :: Either e1 (Either e2 a) -> Either (Either e1 e2) a
f (Left x) = Left (Left x)
f (Right (Left x)) = Left (Right x)
f (Right (Right x)) = Right x
你的功能是不可能的!
你所要求的并没有实际意义。让我们看看函数的类型:

f :: Either e1 (Either e2 a) -> Either e2 a
f :: (e1 -> e2) -> Either e1 (Either e2 a) -> Either e2 a
f g = either (Left . g) id
f :: Either e1 (Either e2 a) -> Either (Either e1 e2) a
f = either (Left . Left) (either (Left . Right) Right)
假设这个函数是total(Haskell中的绝大多数函数都应该是total),我们需要为任何类型的
或e1(或e2a)
的输入生成一个类型为
或e2a
的值。为了实现这一点,让我们考虑输入的所有“形状”。

结果表明,
e1(e2a)
类型的值可以有三种可能的形状:

Left _
Right (Left _)
Right (Right _)
底部的两个形状很容易处理。事实上,我们可以将任何
值映射到自身:

f (Right x) = x
但是,这不处理外部<代码>左侧情况。我们可以从编写模式开始:

f (Left x) = ???
在上面的模式中,我们得到一个值,
x
,类型为
e1
。我们需要生成类型为
的值,要么是e2a
。这意味着我们基本上需要具有以下类型的函数:

g :: e1 -> Either e2 a
但是等等!这种类型显然不可能满足,因为我们需要一个
e2
a
,但我们只有一个
e1
。因此,我们无法实现这种情况(假设我们不无限循环或使用
error
undefined
)。我们被卡住了

解决方案1:提供更多信息 如果不知道您实际上在做什么,就很难为这个问题提供一个好的解决方案。我至少可以提供一些可能性,其中一个可能与您的用例相关

一个简单的解决方案是提供一种将
e1
值映射到
e2
的方法。这样,我们可以将所有错误标准化为
e2
。在
功能的帮助下,实现这一点非常容易:

f :: Either e1 (Either e2 a) -> Either e2 a
f :: (e1 -> e2) -> Either e1 (Either e2 a) -> Either e2 a
f g = either (Left . g) id
f :: Either e1 (Either e2 a) -> Either (Either e1 e2) a
f = either (Left . Left) (either (Left . Right) Right)
您也可以通过将映射函数应用到外部
的左侧,然后使用monadic
join
函数合并两个层来完成此操作:

import Data.Bifunctor

f :: (e1 -> e2) -> Either e1 (Either e2 a) -> Either e2 a
f g = join . first g
解决方案2:更改结果类型 我们可以处理这个问题的另一种方法是调整结果,对这两种可能性进行编码。我们可以生成一个类型为
或(e1 e2或)a
的值来保存任何一个可能的错误。使用
函数编写此代码也相当容易:

f :: Either e1 (Either e2 a) -> Either e2 a
f :: (e1 -> e2) -> Either e1 (Either e2 a) -> Either e2 a
f g = either (Left . g) id
f :: Either e1 (Either e2 a) -> Either (Either e1 e2) a
f = either (Left . Left) (either (Left . Right) Right)
但是,使用模式匹配而不是
编写可能更清晰:

f :: Either e1 (Either e2 a) -> Either (Either e1 e2) a
f (Left x) = Left (Left x)
f (Right (Left x)) = Left (Right x)
f (Right (Right x)) = Right x

除非函数是局部的或者你提供了一些额外的信息,否则你所要求的是没有意义的。如果外部的值是左<代码>怎么办?然后,您没有类型为
e2
a
的值,只有类型为
e1
,因此您不能构造
或e2 a
。我理解您希望对错误进行分组(例如在左侧),以便生成的类型为
或(或e1 e2)a
。但是现在你不知何故想要忽略某种类型的错误。所以你想要一个函数,它接受一个错误并使错误不发生?你所要求的是没有意义的,除非这个函数是部分的或者你提供了一些额外的信息。如果外部的值是左<代码>怎么办?那么您没有类型为
e2
a
的值,只有类型为
e1
,因此