2016-03-02 106 views
3

我很好奇通過「FlexibleInstances」可以在Haskell的類型類中完成什麼樣的「重載」。可以通過FlexibleInstances「重載」返回不同類型,還是匹配類型類?

作爲一個簡單的測試,這裏是AdjusterType數據類型的一個例子。它定義了一個adjust操作,將根據是否包含一個整數或雙不同的量添加到它的價值:

{-# LANGUAGE FlexibleInstances #-} 

class Adjustable a where 
    adjust :: a -> Double 

data AdjusterType a = Adjuster a 
    deriving Show 

instance Adjustable (AdjusterType Integer) where 
    adjust (Adjuster a) = fromIntegral (a + 20) 

instance Adjustable (AdjusterType Double) where 
    adjust (Adjuster a) = a + 0.04 

說得多按預期工作:

Prelude> adjust (Adjuster (1000 :: Integer)) 
1020.0 

Prelude> adjust (Adjuster (3 :: Double)) 
3.04 

是否有可能使Integer版本的adjust返回一個Integer,而Double版本返回一個Double?

歸納的adjust簽名和所述整數的情況下去除所述fromIntegral不起作用:

class Adjustable a where 
    adjust :: Num n => a -> n 

instance Adjustable (AdjusterType Integer) where 
    adjust (Adjuster a) = a + 20 

這產生一個錯誤說,「n」是一個剛性的類型的變量不匹配整數:

Couldn't match expected type ‘n’ with actual type ‘Integer’ 
    ‘n’ is a rigid type variable bound by 
     the type signature for adjust :: Num n => AdjusterType Integer -> n 
Relevant bindings include 
    adjust :: AdjusterType Integer -> n 
In the first argument of ‘(+)’, namely ‘a’ 
In the expression: a + 20 

是期待它什麼類型這裏整數不匹配......或將沒有類型的實際工作,它只是一個奇怪的錯誤消息?(n爲小寫,所以想必知道這是不是一個數據類型)的實例規格

類型約束也不會出現參加匹配分辨率:

instance Integral i => Adjustable (AdjusterType i) where 
    adjust (Adjuster a) = fromIntegral (a + 20) 

instance RealFloat r => Adjustable (AdjusterType r) where 
    adjust (Adjuster a) = a + 0.04 

因此,這些行爲像重複,就好像它們都是Adjustable (AdjusterType x))。約束僅適用於解決方案完成後。

有沒有什麼辦法可以像上面那樣向類型類提供重載行爲,或者它是否必須總是針對特定的實例?

+0

你想使用該[類型系列(https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/type-families.html)。 – Bakuriu

回答

3

是否有可能使Integer版本的調整返回一個Integer,而Double版本會返回Double?

可以使Adjustable型類接受兩個類型的參數,而不是一個,所以它會知道什麼是AdjusterType內:

{-# LANGUAGE MultiParamTypeClasses #-} 

class Adjustable f a where 
    adjust :: f a -> a 

那麼情況應該是:

instance Adjustable AdjusterType Int where 
    adjust (Adjuster a) = a + 20 

instance Adjustable AdjusterType Double where 
    adjust (Adjuster a) = a + 0.04 

還有一些結果來自ghci:

> :set +t 

> adjust (Adjuster (100 :: Int)) 
< 120 
< it :: Int 
> adjust (Adjuster (100 :: Double)) 
< 100.04 
< it :: Double 

它在這裏期待什麼類型的整數不匹配...或者沒有類型實際工作,它只是一個奇怪的錯誤信息?

adjust返回類型是forall n . Num n => n類型,多態型與單一約束Num的,所以你的函數返回的具體類型將不會打字檢查。用fromIntegral包裝你的功能將會解決fromIntegral :: (Integral a, Num b) => a -> b以來的問題。

有沒有什麼辦法可以像上面那樣爲類型類提供重載行爲,或者它是否一直是某個特定的實例?

如果您希望函數針對每種不同的類型行爲不同,那麼您必須爲每個類型添加一個實例。您可能會雖然添加了一些默認行爲,通過限制類的類型參數:

{-# LANGUAGE DeriveFunctor   #-} 
{-# LANGUAGE MultiParamTypeClasses #-} 

class Extract f where 
    extract :: f a -> a 

class (Extract f, Functor f, Num a) => Adjustable f a where 
    adjust :: f a -> a 
    adjust = extract . fmap (+20) 

data AdjusterType a = Adjuster a 
    deriving (Functor) 

instance Extract AdjusterType where 
    extract (Adjuster a) = a 

instance Adjustable AdjusterType Int where 
-- don't have to write any code here 
+0

感謝您的徹底迴應!關於「類型家庭」如何影響這方面的內容,關於Bakuriu的答案是什麼? – HostileFork

+0

@HostileFork類型系列是至關重要的,甚至在您希望返回類型「adjust」遵循一些規則而不是簡單地匹配輸入類型時,甚至是必需的。 'adjust :: AdjusterType Int - > Double'和'adjust :: AdjusterType Double - > Int',在這種情況下,type family通常是唯一的解決方案或最好的類型推斷,代碼樣式等等。對於您當前的用例,它是一個可能的(也許更可擴展的)解決方案等等。 – zakyggaps

2

使用類型家庭提供的解決方案,特別是相關的數據類型,如下:

{-# LANGUAGE TypeFamilies, FlexibleInstances #-} 

class Adjustable a where 
    type Elem a :: * 
    adjust :: a -> Elem a 


data AdjusterType a = Adjuster a 
    deriving (Show) 


instance Adjustable (AdjusterType Integer) where 
    type Elem (AdjusterType Integer) = Integer 

    adjust (Adjuster a) = a + 20 

instance Adjustable (AdjusterType Double) where 
    type Elem (AdjusterType Double) = Double 

    adjust (Adjuster a) = a + 0.04 

main = do 
    let x = Adjuster 1 :: AdjusterType Integer 
     y = Adjuster 1 :: AdjusterType Double 
    print $ adjust x 
    print $ adjust y 

它編譯和輸出:

21 
1.04 
+0

有趣,謝謝你的例子。好的,問的問題...這似乎能夠解決*「是否有可能使整數版本的調整返回一個整數,並且雙重版本返回一個雙重?」*,這將完成,沒有參數化可調整與另一種類型。這是需要的,還是有一些像'adjust(Adjuster a)= a +(20 :: Integer)'這可以做同樣的技巧?這不起作用(抱怨Double,即使它是整數情況)......這是爲了解決這個問題的目的嗎? – HostileFork