2012-09-13 58 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 _ =()