2012-09-13 85 views
3

我試圖實現康托爾配對功能,爲 通用對類型類的實例,像這樣:添加類約束類型類實例

module Pair (Pair, CantorPair) where 

-- Pair interface 
class Pair p where 
    pi :: a -> a -> p a 
    k :: p a -> a 
    l :: p a -> a 

-- Wrapper for typing 
newtype CantorPair a = P { unP :: a } 

-- Assume two functions with signatures: 
cantorPair :: Integral a => a -> a -> CantorPair a 
cantorUnpair :: Integral a => CantorPair a -> (a, a) 

-- I need to somehow add an Integral a constraint to this instance, 
-- but I can't work out how to do it. 
instance Pair CantorPair where 
    pi = cantorPair 
    k = fst . cantorUnpair 
    l = snd . cantorUnpair 

我怎麼能適當積分約束添加到實例? 我有一種模糊的感覺,我可能需要修改Pair接口本身,但不知道如何去解決這個問題。

回答

1

您是否希望所有對始終包含整數元素?在這種情況下,你可以在限制增加的方法的簽名:

class Pair p where 
    pi :: Integral i => i -> i -> p i 
    k :: Integral i => p i -> i 
    l :: Integral i => p i -> i 

這會讓你對類一般較少,但將確保您的CantorPair類型可能是它的一部分。

如果你想保持你的Pair類有點普遍,你可以使用多參數類型的類。 (這將需要兩個擴展:MultiParamTypeClassesFlexibleInstances

class Pair p a where 
    pi :: a -> a -> p a 
    k :: p a -> a 
    l :: p a -> a 

instance Integral i => Pair CantorPair i where 
    pi = cantorPair 
    k = fst . cantorUnpair 
    l = snd . cantorUnpair 

我不知道這是必然從設計的角度來說是最好的選擇,但它是瞭解multiparamter類型類是如何工作的一個好辦法。 (當然,這很簡單。)

+0

嗯,在這個階段,我想我會一直使用積分,但是我認爲將這個約束添加到Pair是沒有意義的。 例如,另一實例可以IdentityPair一個可以添加諸如 數據= I AA 實例對IdentityPair其中 PI XY = I XY K(1×_)= X 升(I _ Y)= Y – guhou

+0

啊,謝謝!我認爲我需要MultiParamTypeClasses,但無法讓我的小提琴工作。一定是因爲我沒有使用FlexibleInstances。 – guhou

1

此解決方案使用type families,因此您需要-XTypeFamilies。我們把類型約束的類型本身,而不是類型構造:

class Pair p where 
    type First p :: * 
    type Second p :: * 
    pi :: First p -> Second p -> p 
    k :: p -> First p 
    l :: p -> Second p 

,然後我們創建這樣的實例:

instance Integral a => Pair (CantorPair a) where 
    type First (CantorPair a) = a 
    type Second (CantorPair a) = a 
    pi = cantorPair 
    k = fst . cantorUnpair 
    l = snd . cantorUnpair 

instance Pair (a, b) where 
    type First (a, b) = a 
    type Second (a, b) = b 
    pi = (,) 
    k = fst 
    l = snd 
2

如果你有機會到類定義,您可以添加pi,kl方法的約束。然而,這有點令人不滿意:沒有什麼說Integral會成爲所有實例的正確約束,畢竟你不想因爲沒有足夠的遠見而拒絕某些實例。所以,下面是一個泛化:我們將允許約束在每個實例中變化。

{-# LANGUAGE ConstraintKinds, TypeFamilies #-} 
import GHC.Exts 

newtype CantorPair a = P { unP :: a } 
cantorPair :: Integral a => a -> a -> CantorPair a 
cantorUnpair :: Integral a => CantorPair a -> (a, a) 
cantorPair = undefined 
cantorUnpair = undefined 

class Pair p where 
    type Ctxt p a :: Constraint 
    pi :: Ctxt p a => a -> a -> p a 
    k :: Ctxt p a => p a -> a 
    l :: Ctxt p a => p a -> a 

instance Pair CantorPair where 
    type Ctxt CantorPair a = Integral a 
    pi = cantorPair 
    k = fst . cantorUnpair 
    l = snd . cantorUnpair 

-- just for fun 
data DataPair a = DataPair a a 

instance Pair DataPair where 
    type Ctxt DataPair a =() 
    pi = DataPair 
    k (DataPair a _) = a 
    l (DataPair _ a) = a 

-- this one made GHC panic! neat, I get to file a bug 
data Unit a = Unit 

instance Pair Unit where 
    type Ctxt Unit a = a ~() 
    pi _ _ = Unit 
    k _ =() 
    l _ =()