2014-09-10 90 views
6

美好的一天。我是Haskell的新手。關於聲明和實例化一些自定義類,有一點我不清楚。Haskell:具有類類型參數的類實例

  1. haskell中有一個標準類Integral。根據hackage,Integral聲明強制方法quot :: a -> a -> a。所以這意味着該類的每個實例都應該有這個方法的實現,對吧?

  2. 我們可以聲明一些功能,採用積分作爲參數,如:

proba :: (Integral a) => a -> a -> a 
proba x y = x `quot` y 

到目前爲止好

  • 現在讓我們宣佈我們的自己的類普羅巴:
  • class Proba a where 
        proba :: a -> a -> a 
    

    我可以實現像這樣的Int或Integer(或其他數據類型)實例:

    instance Proba Integer where 
        proba x y = x `quot` y 
    
    instance Proba Int where 
        proba x y = x `quot` y 
    

    但我不想。 我想爲每個Integral提供一個實例。但是當我嘗試這樣做,我得到一個錯誤:

    instance (Integral a) => Proba a where 
        proba x y = x `quot` y 
    
    Illegal instance declaration for `Proba a' 
        (All instance types must be of the form (T a1 ... an) 
        where a1 ... an are *distinct type variables*, 
        and each type variable appears at most once in the instance head. 
        Use FlexibleInstances if you want to disable this.) 
    In the instance declaration for `Proba a' 
    

    好吧,它似乎問我要不同類型變量而不是類。但爲什麼?!爲什麼僅僅在這裏有Integral這不夠?由於quot是每Integral申報的,所以這個實例應該對每個Integral都有效,不是嗎?

    也許有辦法達到同樣的效果嗎?

    +4

    '如果您想禁用此功能,請使用FlexibleInstances。「您是否試過這樣做? – 2014-09-10 15:03:22

    +0

    我肯定會這樣做,但我想知道爲什麼這些東西隱藏在某個自定義選項的後面,並且默認情況下不可用? – skapral 2014-09-10 15:06:41

    +2

    因爲原因;這在技術上是默認情況下語言如何工作的擴展。你可以閱讀所有[這裏](https://www.haskell.org/ghc/docs/7.4.2/html/users_guide/ghc-language-features.html)。 – 2014-09-10 15:08:05

    回答

    5

    由於錯誤信息表明,你可以使用FlexibleInstances(一種相當常見的安全擴展),允許這種行爲,但你還需要UndecidableInstances

    {-# LANGUAGE FlexibleInstances #-} 
    {-# LANGUAGE UndecidableInstances #-} 
    
    class Proba a where 
        proba :: a -> a -> a 
    
    instance Integral a => Proba a where 
        proba = quot 
    

    之所以這樣,是不是啓用默認情況下是因爲它特別是GHC擴展,它不是Haskell98規範的一部分。你會發現有很多語言擴展是非常有用和安全的,通常你只希望它們在特定的模塊中啓用。不要問「爲什麼不是這種默認」,還要問「我不希望這是默認的?」。


    實現這個沒有擴展名的另一種方式是類型類直接編碼爲數據類型:

    data Proba a = Proba 
        { proba :: a -> a -> a 
        } 
    
    integralProba :: Integral a => Proba a 
    integralProba = Proba quot 
    

    然後,你可以,如果你身邊把它作爲

    foldProba :: Proba a -> a -> [a] -> a 
    foldProba p = foldr (proba p) 
    

    然後有foldProba integralProba,那麼它會自動將該類型限制爲Integral a => a -> [a] -> a

    +0

    非常感謝你,現在已經很清楚了。關於「我什麼時候不希望這是默認的?」的問題。 - 我認爲這很常見:如果我們可以用功能來做這樣的魔術,那麼爲什麼我們不能在課堂上做同樣的事情呢?無論如何 - 這個解決方案對我有好處。 – skapral 2014-09-10 15:16:02

    +0

    @skapral雖然這些不是一回事。使用'Integral a => Proba a'實例,您還可以定義一個特定的'Proba Int'實例,其中'proba'具有與使用'Int'''''''實例不同的行爲。這就是'UndecidableInstances'的意義所在。 – bheklilr 2014-09-10 15:44:05

    +1

    @skapral查看我編輯的替代實現,可以避免使用語言擴展,只使用Haskell98代碼。然後你可以很容易地擁有一個'integralProba',一個'intProba',一個'integerProba'等等,根據需要它們都有不同的行爲,甚至有'intProba1'和'intProba2' 。 – bheklilr 2014-09-10 15:50:00