2017-07-31 59 views
0

我有一个嵌套的或者与不同的错误类型,看起来像:嵌套Eithers不同的错误类型

Either e1 (Either e2 a) 

而且我想,做类似的功能:

Either e1 (Either e2 a) -> Either e2 a 

更多一般来说,有没有一种类型符合这种模式?

+5

什么你问没有意义除非该功能是部分功能,或者您提供了一些额外的信息。如果外面的价值是“左”呢?那么你没有'e2' *或*'a'类型的值,只有'e1'类型,所以你不能构造'E2a'。 –

+4

我明白你想分组错误(例如在左边),以便得到的类型是'Either(e1 e2)a'。但是现在你想要省略某种类型的错误。 –

+1

所以你想要一个函数,它会带来一个错误并且不会发生错误? – immibis

回答

5

你的功能是不可能的!

你所要求的并不是真的有意义。让我们来看看你的函数的类型:

f :: Either e1 (Either e2 a) -> Either e2 a 

假设这个函数是总(因为绝大多数的Haskell函数真的应该是),我们需要生产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。因此,我们不能执行(假设我们不是无限循环或使用errorundefined)。我们被卡住了。

解决方案1:提供更多信息

不知道你实际上试图做,很难提供一个很好的解决这个问题。我至少可以提供一些可能性,也许其中一个可能与您的用例有关。

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

f :: (e1 -> e2) -> Either e1 (Either e2 a) -> Either e2 a 
f g = either (Left . g) id 

您还可以通过应用映射函数到外Either的左侧,然后使用一元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 
+0

谢谢,你的文章写得非常好,也帮助我在自己的脑海中回答和澄清这个问题。 为了澄清,e1和e2与错误具有相似的结构,所以我会考虑将e1错误映射到e2错误,所以解决方案1对我的情况非常适用。 – plint