如何根据上下文制作 class 的不同实例?

How to make the different instances of class, depending on context?

我有一些简单的 class 和一些用于各种数据类型和上下文的实例。这是一个部分工作的案例:

{-# LANGUAGE FlexibleInstances, UndecidableInstances, IncoherentInstances  #-}

class Neighbors a where
     (~~) :: a -> a -> Bool

instance Neighbors Int where
  x ~~ y = (abs (x-y) == 1)

instance (RealFloat a) => Neighbors a where
  x ~~ y = (abs(x-y) < 1.0)

缺点是我必须对 Int:

使用类型限制
*Main> (1::Int) ~~ (2::Int)
True

但是,如果我只想使用上下文,我的第二个脚本根本不起作用:

{-# LANGUAGE FlexibleInstances, UndecidableInstances, IncoherentInstances  #-}

class Neighbors a where
     (~~) :: a -> a -> Bool

instance (Integral a) => Neighbors a where
  x ~~ y = (abs(x-y) == 1)

instance (RealFloat a) => Neighbors a where
  x ~~ y = (abs(x-y) < 1.0)

错误是:

Neighborhood.hs:7:10: error:
    Duplicate instance declarations:
      instance [incoherent] Integral a => Neighbors a
        -- Defined at Neighborhood.hs:7:10
      instance [incoherent] RealFloat a => Neighbors a
        -- Defined at Neighborhood.hs:10:10
Failed, modules loaded: none.

但我想为这些不同的上下文定义不同的实例:Integral 和 RealFloat。如何在一个文件中完成?

您可以像这样在一个文件中完成:

instance Neighbors Int     where x ~~ y = abs (x-y) == 1
instance Neighbors Double  where x ~~ y = abs (x-y) < 1
instance Neighbors Float   where x ~~ y = abs (x-y) < 1
instance Neighbors CDouble where x ~~ y = abs (x-y) < 1
instance Neighbors CFloat  where x ~~ y = abs (x-y) < 1
instance RealFloat a => Neighbors (Identity a) where x ~~ y = abs (x-y) < 1
instance RealFloat a => Neighbors (Const a b)  where x ~~ y = abs (x-y) < 1

我知道您不喜欢为每个类型构造函数编写一个实例;但这是对所问问题的正确答案,因为类型构造函数是 Haskell 类型类进行分派的方式。

您可以通过定义

来减少一些重复
neighborsDef :: (Num a, Ord a) => a -> a -> Bool
neighborsDef x y = abs (x-y) < 1

并在实例中给出 (~~) = neighborsDef

事实上,我怀疑你根本不想要一个新的类型类。也许以下两个定义之一适合您:

(~~) :: (Num a, Ord a) => a -> a -> Bool
x ~~ y = abs (x-y) <= 1 -- if the Int definition was slightly off, OR
x ~~ y = x /= y && abs (x-y) <= 1 -- if the RealFloat definition was slightly off