类型类、重载和实例声明

4

如果有这个:

data Rectangle = Rectangle Height Width
data Circle = Circle Radius

class Shape a where
    area :: a -> Float
    perimeter :: a -> Float

instance Shape Rectangle where
    area (Rectangle h w) = h * w
    perimeter (Rectangle h w) = 2*h+w*2

instance Shape Circle where
    area (Circle r) = pi * r**2
    perimeter (Circle r) = 2*pi*r

volumenPrism base height = (area base) * height

surfacePrism shape h = (area shape) * 2 + perimeter shape * h

为什么我不能写这个呢?a是一种类型,为什么这不起作用呢?
instance (Shape a) => Eq a where
      x==y = area x == area y

很明显地,要做到这样:
instance Eq Circle where
     x==y = area x == area y

首先对于圆形,然后对于矩形进行操作是可行的,但似乎不是正确的方法。

在这一切中,我没有理解哪一部分。

谢谢

1个回答

7

根本问题在于类型类实例解析机制不会回溯。因此,如果您编写了instance Shape a => Eq a,那么每当编译器想要找到一个Eq实例时,编译器将尝试使用此实例,对于大多数类型而言,这不起作用,因为它们不是Shape的实例。

如果您仍然真的想这样做,可以添加:

{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}

在你的源文件顶部添加代码。

你也可以通过将OverlappingInstances添加到语言声明集中来解决上述问题之一,但是你仍然会有一个全局的Eq实例,在程序的其他地方会引起严重的混乱。

最好只枚举你真正需要的实例,即使看起来很丑。你可以使用一个辅助函数将样板代码最小化,例如:

x `areaEq` y = area x == area y

然后

instance Eq Circle where
    (==) = areaEq

etc.


1
它不是不会回溯——而是它所表示的含义并非你所想象的那样。instance Shape a => Eq a 的意思是“所有类型都使用这个定义作为 Eq 实例。使用此实例会使得使用的类型添加一个 Shape 约束条件。” - Carl
1
顺便说一下,鉴于开放世界的假设,这是唯一可能的含义。无论你多么小心,始终可以稍后添加一个实例,使事情变得模棱两可。 - Carl
1
在操作上,解析不会回溯。从语义上讲,实例是按您描述的方式定义的。真的不确定哪个是因果关系 :-) 开放世界假设已经被 IncoherentInstances 等弯曲得相当厉害了,所以我不认为这一切都完全自然地遵循那个假设。 - GS - Apologise to Monica

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接