在Haskell中,如何重写类型类的(==)和(/=)运算符?

在Haskell中,如何重写类型类的(==)和(/=)运算符?,haskell,operator-overloading,Haskell,Operator Overloading,假设我有这样的东西 class Circle c where x :: c -> Float y :: c -> Float radius :: c -> Float data Location = Location { locationX :: Float , locationY :: Float } deriving (Show, Eq) data

假设我有这样的东西

class Circle c where
    x :: c -> Float
    y :: c -> Float
    radius :: c -> Float

data Location = Location { locationX :: Float
                         , locationY :: Float
                         } deriving (Show, Eq)

data Blob = Location { blobX :: Float
                     , blobY :: Float
                     , blobRadius :: Float,
                     , blobRating :: Int
                     } deriving (Show, Eq)

instance Circle Location where
    x = locationX
    y = locationY
    radius = pure 0

instance Circle Blob where
    x = blobX
    y = blobY
    radius = blobRadius
例如,如果圆的x点和y点相等,我希望圆类型相等。如何将type类的实例与==和/=运算符进行比较。我知道我可以做这样的事情,但有可能让操作员超负荷工作吗

equal :: Circle a => Circle b => a -> b -> Bool
equal a b = (x a == x b && y a == y b)
我想能够与你相比

位置5.0 5.0==Blob 5.0 5.0。。。应该给我真实的第0个,一些标准导入:

import Data.Function (on)
import Control.Arrow ((&&&))
首先,这不是一个好主意。a==b只有在a和b在所有方面都与用户可互换的情况下才应为真-这显然不是两个恰好共享同一中心点的圆的情况

第二,首先将Circle作为typeclass可能不是一个好主意。typeclass只有在您想对不能直接用参数表示的内容进行抽象时才有意义。但如果您只是想将不同的“有效载荷”附加到空间中的点上,则更明智的方法可能是定义如下内容

data Located a = Located {x,y :: ℝ, payload :: a}
如果您真的想让Circle的不同实例共存并在运行时具有可比性,那么typeclass是完全错误的选择。那将是一个OO类。Haskell没有任何内置的概念,但您可以使用

data Blob = Blob
   { x,y :: ℝ
   , radius :: ℝ
   , rating :: Maybe Int }
没有其他类型

第三,从理论上讲,你所要求的例子可以定义为

instance (Circle a) => Eq a where
  (==) = (==)`on`(x &&& y)
但这将是一个真正可怕的想法。这将是一个包罗万象的实例:每当你比较任何东西时,编译器都会检查“它是形式a的吗?”字面上任何东西都是这种形式的“哦,太好了,然后说实例告诉我如何比较它。”直到后来它才会查看循环要求

正确的解决方案是根本不定义任何此类Eq实例。您的类型已经有了单独的Eq实例,这通常应该是正确的使用方法–根本不需要通过Circle类来表达它,只需给出任何需要进行此类比较的函数约束Circle a,Eq a=>

当然,这些实例不仅会比较位置,还会比较整个数据,正如我所说的,这是一件好事。如果您实际上只想比较结构的一部分,那么,请明确说明!使用not==本身,但提取相关部分并进行比较。这方面的一个有用助手可能是

location :: Circle a => a -> Location
location c = Location (x c) (y c)

…然后,对于任何圆类型,只需将==`on`location而不是===,忽略除位置之外的任何其他信息。或者直接在`x&&&y上写出==`on,这可以很容易地调整到其他情况。

共用一个中心的两个圆不一定相等,但它们是同心的;这就是你应该写一个函数来检查的

concentric :: (Circle a, Circle b) => a -> b -> Bool
concentric c1 c2 = x c1 == x c2 && y c1 == y c2

你说的超载到底是什么意思?==应该如何处理其他东西?==:Eq a=>a->a->Bool;您无法比较两种不同类型的值,即使这两种类型实现相同的类型类。显式定义radius=const 0可能是个好主意。半径不是任意的应用程序;这是一个函数。