2015-05-19 82 views
1

假設我需要不同的輸出,具體取決於函數的多態參數的類型。我的初始嘗試失敗一些神祕的錯誤消息:多態參數類型的模式匹配 - 替代

choice :: a -> Int 
choice (_ :: Int) = 0 
choice (_ :: String) = 1 
choice _ = 2 

然而,我們可以通過在不同的數據構造包裹所需類型固定,很容易並使用這些中的模式匹配:

data Choice a = IntChoice Int | StringChoice String | OtherChoice a 

choice :: Choice a -> Int 
choice (IntChoice _) = 0 
choice (StringChoice _) = 1 
choice (OtherChoice _) = 2 

問題:你知道一個方法來繞過這個嗎? Haskell2010,GHC或允許我使用第一個變體(或類似的東西)的任何擴展中有沒有功能?

+1

'Data.Typeable'允許的查詢類型在運行時,但它不應該輕易使用。 – chi

回答

4

問題:你知道一種方法來規避這種情況嗎? Haskell2010,GHC或允許我使用第一個變體(或類似的東西)的任何擴展中有沒有功能?

沒有,要麼是在2010年或任何GHC擴展,讓你寫類型

choice :: a -> Int 

,其返回值取決於它的參數類型的函數提供哈斯克爾沒有特色。你可以指望將來也不存在這樣的特徵。

即使有瑕疵在運行時檢查GHC的內部數據表示,不可能從類型爲Int的新類型的值中區分出Int類型的值:這些類型具有相同的運行時表示。

由函數返回的Int是一個存在於運行時的值,因此需要由運行時存在的另一個值來確定。這可能是要麼

  1. 一個普通的價值,就像你data Choice a = IntChoice Int | StringChoice String | OtherChoice a; choice :: Choice a -> Int例如,或者

  2. 一種類字典,使用一個自定義類,如大衛年輕人的答案,或者內置Typeable類:

    choice :: Typeable a => a -> Int 
    choice a 
        | typeOf a == typeOf (undefined :: Int) = 0 
        | typeOf a == typeOf (undefined :: String) = 1 
        | otherwise        = 2 
    
+0

感謝您澄清替代方案。 _「不可能將int類型的值與類型爲Int的新類型的值區分開來:這些類型具有相同的運行時表示」_ - 不是TypeSynonymInstances的用途嗎? (在[大衛的回答](http://stackoverflow.com/a/30333292/3041008)中提到)。你會如此善良,並使用Typeable類型提供'choice'函數的定義嗎? – mucaho

+3

@mucaho'TypeSynonymInstances'專門用於類型實例。特別是,它可以讓你爲一個類型同義詞做一個實例。類型同義詞是類型系統對待另一種類型的類型(在本例中,type = String = [Char]')。即使有了這個擴展和'OverlappingInstances',你也不能創建'String'和'[Char]'的實例,因爲它們是同義詞。另一方面,'newtype's被類型系統視爲不同類型,但它們具有完全相同的運行時表示。 –

5

這混淆了兩種不同的多態性。你想要的是 ad-hoc多態性,這是通過類型類來完成的。 a -> Int類型函數的多態性類型爲參數多態性。對於參數多態,choice的一個函數定義必須適用於任意可能的類型a。在這種情況下,這意味着它實際上不能使用類型爲a的值,因爲它不知道任何關於它的值,因此choice必須是一個常量函數,例如choice _ = 3。這實際上給你非常強大的保證功能可以做什麼,只是看它的類型(這個屬性被稱爲參數)。

與類型類,你可以實現你的例子如:

class ChoiceClass a where 
    choice :: a -> Int 

instance ChoiceClass Int where 
    choice _ = 0 

instance ChoiceClass String where 
    choice _ = 1 

instance ChoiceClass a where 
    choice _ = 2 

現在,我要指出的是,這種類型的類的做法往往是錯誤的尤其是當別人誰是剛開始想用它。你絕對是不想這樣做,以避免簡單的類型,如Choice類型在你的問題。它可以增加很多複雜性,實例解析起初可能會讓人困惑。請注意,爲了使類型解決方案能夠正常工作,需要打開兩個擴展:FlexibleInstancesTypeSynonymInstances,因爲String[Char]的同義詞。 OverlappingInstances也是必需的,因爲類型類在「開放世界」假設下工作(這意味着任何人都可以隨後出現併爲新類型添加實例,並且必須考慮這一點)。這不是必然一件壞事,但在這裏它是由於使用類型解決方案而非簡單的數據類型解決方案導致的爬行復雜性的一個標誌。特別是可以使事情變得更難以思考和使用。

+1

你還需要'OverlappingInstances'來實際使用這個類 - 我覺得這個擴展特別棘手,很難完全掌握。例如。 http://stackoverflow.com/questions/29504107/which-dictionary-does-ghc-choose-when-more-than-one-is-in-scope – chi

+0

@chi感謝您帶來了!有一段時間,我認爲這是沒有必要的,因爲'選擇'''沒有它(即使我原本以爲會這樣)。但是,如果沒有它,不可能使用其他兩個實例。我發現這個擴展也很難理解,我絕對認爲它增加了額外的複雜性,如果可能的話應該避免。我更新了我的答案,提到它。 –

+0

感謝您提供寶貴的信息。所以如果可能的話,你建議堅持使用數據構造函數,對吧? – mucaho