2014-01-15 108 views
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 

第一的圆,然后矩形works..but似乎不正确的做法。

这是什么我没有得到所有这一切?

Ty

回答

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 

+1

它不是,它不会回溯 - 那就是它并不意味着你认为它。 '实例Shape a => Eq a'意味着“所有类型都是Eq的实例,使用这个实例会导致为所使用的类型添加一个”形状“约束。 – Carl

+1

顺便说一句,这是它唯一可以表达的意思,因为这个开放世界的假设。无论你多么小心,总会在后面添加一个实例,使事情变得模糊。 – Carl

+1

在操作上,分辨率不会发生变化。从语义上讲,实例按照您的描述进行定义。不确定哪个是因果关系:-)开放世界的假设已经被例如'IncoherentInstances',所以我不完全遵循这一点。 –