说我有类似的东西
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点相等,我希望Circle类型相等。如何将类型类的实例与(==)和(/ =)运算符进行比较。我知道我可以做这样的事情,但是可以超载操作员吗?
equal :: Circle a => Circle b => a -> b -> Bool
equal a b = (x a == x b && y a == y b)
我希望能够与
进行比较 (Location 5.0 5.0) == (Blob 5.0 5.0 ... )
应该给我真实的
答案 0 :(得分:7)
Zeroth ,一些标准导入:
import Data.Function (on)
import Control.Arrow ((&&&))
<小时/> 首先,这不是一个好主意。只有当
a==b
和a
(对于与用户相关的所有目的)可互换时,b
才应该是真的 - 对于两个只是碰巧共享相同中心点的圆圈来说显然不是这种情况!
第二次,首先让Circle
成为类型类别可能不是一个好主意。只有当你想要抽象不能仅用参数直接表达的东西时,类型类才有意义。但是,如果你只是想将不同的“有效载荷”附加到空间中的点,那么更明智的方法可能是定义像
data Located a = Located {x,y :: ℝ, payload :: a}
如果在某种情况下,您实际上希望允许不同的Circle
实例共存并在运行时具有可比性,那么类型类完全是错误的选择。这将是一个OO类。 Haskell没有任何内置的概念,但你可以使用
data Blob = Blob
{ x,y :: ℝ
, radius :: ℝ
, rating :: Maybe Int }
并没有其他类型。
https://lukepalmer.wordpress.com/2010/01/24/haskell-antipattern-existential-typeclass/
第三次,您要求的实例可以理论上说,定义为
instance (Circle a) => Eq a where
(==) = (==)`on`(x &&& y)
但这将是一个非常可怕的想法。这将是一个包罗万象的实例:无论何时比较任何,编译器都会检查“它的格式是a
吗?”(字面意思任何是那个形式)“哦,很好,然后说实例告诉我如何比较这个。”只有稍后才会看到Circle
要求。
正确解决方案是根本不定义任何此类Eq
实例。你的类型已经单独拥有Eq
个实例,这通常应该是正确的使用 - 根本不需要通过Circle
类来表达它,只需给出需要做的任何函数来比较约束(Circle a, Eq a) => ...
。
当然,这些实例然后不只是比较位置而是整个数据,正如我所说,这是一件好事。如果你真的想只比较结构的一部分,那么,明确说明!不要使用==
本身,而是提取相关部分并进行比较。对此有用的帮助可能是
location :: Circle a => a -> Location
location c = Location (x c) (y c)
...然后,对于任何Circle
类型,您只需编写(==)`on`location
而不是(==)
,即可忽略除该位置之外的任何其他信息。或者直接写出(==)`on`(x &&& y)
,这可以很容易地调整到其他情况。
答案 1 :(得分:2)
共享一个共同中心的两个圆圈不一定相等,但它们是同心;那就是你应该写一个要检查的函数。
concentric :: (Circle a, Circle b) => a -> b -> Bool
concentric c1 c2 = x c1 == x c2 && y c1 == y c2