2012-07-17 142 views
4

爲什麼不類型檢查?爲什麼沒有這個函數簽名類型檢查?

data MyEither a b = MyLeft a | MyRight b 
        deriving (Read, Show) 

extractEither :: MyEither a b -> c 
extractEither (MyLeft p) = p 

編譯器顯示:

Couldn't match type `a' with `c' 
    `a' is a rigid type variable bound by 
     the type signature for extractEither :: MyEither a b -> c 
     at /Users/tongmuchenxuan/playground/test.hs:5:1 
    `c' is a rigid type variable bound by 
     the type signature for extractEither :: MyEither a b -> c 
     at /Users/tongmuchenxuan/playground/test.hs:5:1 
In the expression: p 
In an equation for `extractEither': extractEither (MyLeft p) = p 

是不是'C」一般足以捕捉任何類型的?

回答

12

c任何類型的主叫方可能希望函數返回,即一個函數f :: a -> c,將總是需要可以寫f x + 1 :: Int以及putStr $ f x以及main = f x。你的功能當然不允許。

你想要的是返回一個動態類型。 Haskell故意不允許像其他語言那樣容易,因爲當返回的類型出乎意料時,它顯然會導致運行時錯誤。有很多種方法可以做到,但是哪一種取決於你實際需要什麼。你能給一些背景嗎?這很可能是你的問題的最佳解決方案而不是使用動態類型,但更多的Haskell慣用。

也許你想要的是簡單

extractEither :: MyEither a a -> a 
extractEither (MyLeft p) = p 
extractEither (MyRight p) = p 

這就要求在兩個「面」的類型是相同的。

+0

謝謝。你們兩個的答案都很清楚,解決了我的難題! – 2012-07-17 08:17:45

+0

@ M.Tong:不要忘記將其中的一個標記爲接受的答案。 – 2012-07-17 08:28:34

+0

其實這個問題並不是以動態類型爲前提的,而只是子類型和upcasts,可以靜態檢查哪個AFAIK。雖然Haskell沒有這些。 – 2012-07-17 17:10:37

11

基本上,你的類型的簽名說,你的函數可以返回給定MyEither a b,再次任何類型c的值,爲每一位ab。然而,正如編譯器指出的那樣,這裏不可能產生任何這樣的c,因爲你實際上返回了一個類型爲a(即p :: a)的值。

此外,您的定義仍不管理您的MyEither a b不是Left a的情況。它應該怎麼做,例如,當你致電extractEither (MyRight 1)時?如果你試圖運行這樣的函數(在修改了類型簽名之後),你可能會產生所謂的非窮舉模式異常,這意味着extractEither沒有處理一些可能的輸入模式的主體定義。

如果您正在嘗試編寫一個函數,用於提取MyLeftMyRight值,恐怕您應該改變主意。爲了提取任何東西,左邊或右邊,你應該在兩邊都有相同的類型(即MyEither a a),這是沒有用的。

您應該考慮具有這些功能來提取值。在這裏,我回到Maybe,從而避免管理到error調用的負擔比如當你嘗試從正確的價值提取左:

extractMyLeft :: MyEither a b -> Maybe a 
extractMyRight :: MyEither a b -> Maybe b 

編輯:也看到dblhelix的回答對另一種可能的重構建議這可能對幾乎有用獲取您正在查找的類型簽名。

8

除了裏卡多的回答是:如果你想有一個功能是能夠從MyEither值提取一些類型c的值,不管它是否被MyLeftMyRight建,你可以,而不是要求的值已鍵入MyEither c c「指示」,關於如何從任一側獲得c -typed值的函數:

extractEither :: (a -> c) -> (b -> c) -> MyEither a b -> c 
extractEither f g (MyLeft x) = f x 
extractEither f g (MyRight y) = g y 

即,extractEither現在需要兩個額外的參數和fg:其用於採取所提取的功能值轉換爲所需t的值YPE。

這是使用總和類型時非常常見和有用的習慣用法。

+0

我要在我的答案中添加一行以引用您的答案。 +1 :) – 2012-07-17 12:07:17

+0

謝謝。但在這種情況下,我必須指定f和g,對嗎?有沒有一種方法可以只指定其中的一個,並說出「我不在乎別人是什麼」之類的東西 - 在模式匹配中類似'_'。 – 2012-07-17 13:48:50

+0

是的,你可以:'提取未定義的id(MyRight 3.14)',但是最好確定你沒有提供用'MyLeft'構建的值。 ;) – 2012-07-17 14:16:48

1
extractEither :: MyEither a b -> c 
extractEither (MyLeft p) = p 

是不是'C」一般足以捕捉任何類型的?

您帶給表格的直覺在面向對象的語言或其他帶有子類型的語言中工作,但不適用於Haskell或其他參數化類型系統(例如,Java泛型)。在Java中採取這種方法簽名:

public Object blah(Foo whatever); 

這個方法返回Object,這是在層次最高的類型。這意味着這個方法的實現可以選擇返回任何想要的類型,並且它會被簡單地上傳到Object

Haskell類型不能像那樣工作。你認爲你的extractFilter只能返回p,因爲你隱式假設,作者extractEither,得到挑選哪種類型用於c,就像blah的作者得到選擇的運行時類型結果。但在Haskell中,情況恰恰相反:你的函數的調用者可以選擇用於a,bc的類型,並且他們可以選擇不兼容的ac。如果我選擇任何ac,則沒有終止的代碼片段可以將我的任意選擇a轉換爲我的任意選擇c