具有不同错误类型的嵌套 Eithers
Nested Eithers with different error types
我有一个带有不同错误类型的嵌套 either,看起来像:
Either e1 (Either e2 a)
我想要一个函数,它可以做类似的事情:
Either e1 (Either e2 a) -> Either e2 a
更一般地说,是否有匹配此模式的类型类?
你的功能是不可能的!
你的要求就目前而言没有任何意义。让我们看看你的函数类型:
f :: Either e1 (Either e2 a) -> Either e2 a
假设这个函数是总函数(因为 Haskell 中的绝大多数函数确实应该是),我们需要为 any[ 生成一个 Either e2 a
类型的值 输入类型 Either e1 (Either e2 a)
。为了尝试实现这一点,让我们考虑输入可以进入的所有“形状”。
事实证明,Either e1 (Either e2 a)
类型的值可以有三种可能的形状:
Left _
Right (Left _)
Right (Right _)
下面两个形状好办。事实上,我们可以将任何 Right
值映射到自身:
f (Right x) = x
但是,这不处理外部 Left
的情况。我们可以从编写模式开始:
f (Left x) = ???
在上面的模式中,我们得到了一个值,x
,类型为 e1
。我们需要生成一个 Either e2 a
类型的值。这意味着我们本质上需要一个具有以下类型的函数:
g :: e1 -> Either e2 a
但是等等!这种类型显然无法满足,因为我们需要 e2
或 a
,但我们只有 e1
。因此,我们 不能 实现这种情况(假设我们不无限循环或使用 error
或 undefined
)。我们卡住了。
解决方案 1:提供更多信息
如果不知道您实际要做什么,就很难为这个问题提供好的解决方案。我至少可以提供几种可能性,也许其中一种与您的用例相关。
一个简单的解决方案是提供一种将 e1
值映射到 e2
的方法。这样,我们就可以将所有错误归一化为 e2
。在 either
函数的帮助下实现这一点非常容易:
f :: (e1 -> e2) -> Either e1 (Either e2 a) -> Either e2 a
f g = either (Left . g) id
您也可以通过将映射函数应用到外部 Either
的左侧,然后使用 monadic join
函数合并两层来完成此操作:
import Data.Bifunctor
f :: (e1 -> e2) -> Either e1 (Either e2 a) -> Either e2 a
f g = join . first g
解决方案 2:更改结果类型
我们处理这个问题的另一种方法是调整结果以对两种可能性进行编码。我们可以生成一个 Either (Either e1 e2) a
类型的值来保存任何可能的错误。这也很容易用 either
函数编写:
f :: Either e1 (Either e2 a) -> Either (Either e1 e2) a
f = either (Left . Left) (either (Left . Right) Right)
但是,用模式匹配而不是 either
写的可能更清楚:
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
我有一个带有不同错误类型的嵌套 either,看起来像:
Either e1 (Either e2 a)
我想要一个函数,它可以做类似的事情:
Either e1 (Either e2 a) -> Either e2 a
更一般地说,是否有匹配此模式的类型类?
你的功能是不可能的!
你的要求就目前而言没有任何意义。让我们看看你的函数类型:
f :: Either e1 (Either e2 a) -> Either e2 a
假设这个函数是总函数(因为 Haskell 中的绝大多数函数确实应该是),我们需要为 any[ 生成一个 Either e2 a
类型的值 输入类型 Either e1 (Either e2 a)
。为了尝试实现这一点,让我们考虑输入可以进入的所有“形状”。
事实证明,Either e1 (Either e2 a)
类型的值可以有三种可能的形状:
Left _
Right (Left _)
Right (Right _)
下面两个形状好办。事实上,我们可以将任何 Right
值映射到自身:
f (Right x) = x
但是,这不处理外部 Left
的情况。我们可以从编写模式开始:
f (Left x) = ???
在上面的模式中,我们得到了一个值,x
,类型为 e1
。我们需要生成一个 Either e2 a
类型的值。这意味着我们本质上需要一个具有以下类型的函数:
g :: e1 -> Either e2 a
但是等等!这种类型显然无法满足,因为我们需要 e2
或 a
,但我们只有 e1
。因此,我们 不能 实现这种情况(假设我们不无限循环或使用 error
或 undefined
)。我们卡住了。
解决方案 1:提供更多信息
如果不知道您实际要做什么,就很难为这个问题提供好的解决方案。我至少可以提供几种可能性,也许其中一种与您的用例相关。
一个简单的解决方案是提供一种将 e1
值映射到 e2
的方法。这样,我们就可以将所有错误归一化为 e2
。在 either
函数的帮助下实现这一点非常容易:
f :: (e1 -> e2) -> Either e1 (Either e2 a) -> Either e2 a
f g = either (Left . g) id
您也可以通过将映射函数应用到外部 Either
的左侧,然后使用 monadic join
函数合并两层来完成此操作:
import Data.Bifunctor
f :: (e1 -> e2) -> Either e1 (Either e2 a) -> Either e2 a
f g = join . first g
解决方案 2:更改结果类型
我们处理这个问题的另一种方法是调整结果以对两种可能性进行编码。我们可以生成一个 Either (Either e1 e2) a
类型的值来保存任何可能的错误。这也很容易用 either
函数编写:
f :: Either e1 (Either e2 a) -> Either (Either e1 e2) a
f = either (Left . Left) (either (Left . Right) Right)
但是,用模式匹配而不是 either
写的可能更清楚:
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