【问题标题】:Adding class constraints to typeclass instance向类型类实例添加类约束
【发布时间】:2012-09-06 00:55:38
【问题描述】:

我正在尝试实现康托尔配对函数,作为一个实例 泛型 Pair 类型类,如下所示:

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 接口本身, 但不知道该怎么做。

【问题讨论】:

    标签: haskell typeclass


    【解决方案1】:

    如果您有权访问类定义,则可以将Integral 约束添加到pikl 方法。不过,这有点不令人满意:没有什么可以说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 _ = ()
    

    【讨论】:

      【解决方案2】:

      您是否希望所有对始终包含整数元素?在这种情况下,您可以将约束添加到方法的签名中:

      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
      

      我不知道从设计的角度来看这是否一定是最佳选择,但这是了解多参数类型类如何工作的好方法。 (诚​​然,这相当简单。)

      【讨论】:

      • 嗯,在这个阶段我想我会一直使用 Integrals,但我认为将这个约束添加到 Pair 是没有意义的。例如,可以添加另一个实例,例如 data IdentityPair a = I a a instance Pair IdentityPair where pi x y = I x y k (I x _) = x l (I _ y) = y
      • 啊,谢谢!我以为我需要 MultiParamTypeClasses,但无法让我的摆弄工作。一定是因为我没有使用 FlexibleInstances。
      【解决方案3】:

      此解决方案使用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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-10-02
        • 2018-06-15
        • 1970-01-01
        • 1970-01-01
        • 2015-09-06
        • 2019-03-21
        • 1970-01-01
        • 2018-10-04
        相关资源
        最近更新 更多