2017-07-31 80 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