2011-01-11 121 views
11

我非常理解3/4语言的其余部分,但每次我在我的代码中以有意义的方式使用类时,我都会受到永久的根深蒂固。我似乎无法找出与类混合的类型变量

为什么这不是非常简单的代码工作?

data Room n = Room n n deriving Show 

class HasArea a where 
    width :: (Num n) => a -> n 

instance (Num n) => HasArea (Room n) where 
    width (Room w h) = w 

所以,房间宽度由整数表示也许花车,我不想在这一点上,以限制它。无论是类和实例限制n型,以订购数量,但它仍然不喜欢它,我得到这个错误:

Couldn't match expected type `n1' against inferred type `n' 
    `n1' is a rigid type variable bound by 
     the type signature for `width' at Dungeon.hs:11:16 
    `n' is a rigid type variable bound by 
     the instance declaration at Dungeon.hs:13:14 
In the expression: w 
In the definition of `width': width (Room w h) = w 
In the instance declaration for `HasArea (Room n)' 

所以它告诉我的类型不匹配,但是它没有告诉我它认为他们是什么类型,这将是非常有用的。作为一个方面说明,有没有简单的方法来调试这样的错误?我知道做这件事的唯一方法是随机更换东西,直到它工作。

回答

18

你得到的错误告诉你它认为该类型应该是什么;不幸的是,这两种类型都是用类型变量表示的,这使得它更难以看清。第一行表示你给出了n这个表达式,但是它想给它类型n1。为了弄清楚这是什么意思,看接下来的几行:

`n1' is a rigid type variable bound by 
    the type signature for `width' at Dungeon.hs:11:16 

这是说n1是一种变量,其值是已知的,因此不能改变(是“刚性”)。由于它受width类型签名的约束,因此您知道它受限于行width :: (Num n) => a -> n。在范围内还有另一个n,所以此n被重命名为n1width :: (Num n1) => a -> n1)。接下来,我们

`n' is a rigid type variable bound by 
    the instance declaration at Dungeon.hs:13:14 

这是告诉你,哈斯克尔发现从线instance (Num n) => HasArea (Room n) where类型n。正在报告的问题是n,这是为width (Room w h) = w计算的GHC类型,与n1不同,它是预期的类型。

你遇到这个问题的原因是你的width的定义比预期的多态性少。 width的类型签名是(HasArea a, Num n1) => a -> n1,这意味着对于HasArea的一个实例,您可以用任何类型的数字来表示它的宽度。但是,在您的实例定义中,行width (Room w h) = w意味着width的类型为Num n => Room n -> n。请注意,这不是足够多态的:Room nHasArea的一个实例,这将需要width的类型为(Num n, Num n1) => Room n -> n1。这是无法统一具体的n与一般n1这是导致您的类型错误。

有几种方法可以解决这个问题。一种方法(也许是最好的方法),你可以在sepp2k's answer中看到的是使HasArea取类型变量* -> *;这意味着,而不是a本身是一种类型,例如a Inta n是类型。 Maybe[]是类型为* -> *的示例。 (普通类型如IntMaybe Double有种类*。)这可能是最好的选择。

如果你有一些类型的一种*其中有一个区域(例如data Space = Space (Maybe Character),其中width总是1),但是,这是行不通的。另一种方法(其需要一些扩展Haskell98/Haskell2010)是使HasArea多参数类型的类:

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-} 

data Room n = Room n n deriving Show 

class Num n => HasArea a n where 
    width :: a -> n 

instance Num n => HasArea (Room n) n where 
    width (Room w h) = w 

现在,传递宽度的类型作为参数的类型类本身,所以width有类型(HasArea a n, Num n) => a -> n。但是,这可能会有一个缺点,那就是你可以声明instance HasArea Foo Intinstance HasArea Foo Double,这可能有问题。如果是,那么为了解决这个问题,你可以使用函数依赖或类型族。函数依赖允许你指定给定的类型,其他类型是唯一确定的,就像你有一个普通的函数一样。使用这些给人的代码

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FunctionalDependencies #-} 

data Room n = Room n n deriving Show 

class Num n => HasArea a n | a -> n where 
    width :: a -> n 

instance Num n => HasArea (Room n) n where 
    width (Room w h) = w 

的位告诉GHC,如果它可以推断a,那么它也可以推断n,因为只有一个n为每a。这可以防止上面讨论的那种情况。

类型的家庭都更不同:

{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts, TypeFamilies #-} 

data Room n = Room n n deriving Show 

class Num (Area a) => HasArea a where 
    type Area a :: * 
    width :: a -> Area a 

instance Num n => HasArea (Room n) where 
    type Area (Room n) = n 
    width (Room w h) = w 

这是说,除了具有width功能,HasArea类也有一个Area(或类型的功能,如果你要考虑一下那样)。对于每个HasArea a,指定Area a类型(其中,由于超类约束,必须是Num的实例),然后使用该类型作为您的数字类型。

至于如何调试这样的错误?老实说,我最好的建议是“练习,练习,练习”。随着时间的推移,你会得到更多使用,以搞清楚(一)中的错误在说什么,和(b)什么可能出了问题。随机更换东西是做这种学习的一种方式。建议我可以给最大的一块,不过,是要注意Couldn't match expected type `Foo' against inferred type `Bar'线。这些告诉你的编译器计算什么(Bar)和预期(Foo)的类型,如果你能准确地找出那些是哪种类型,可以帮助你找出错误所在。

+0

哇,你真把那走出公园。我在Google上搜索这些确切的主题,因为我立即发现缺乏将它用于多种类型的功能。不仅如此,我错过了迄今为止最简单的多参数类型类。谢谢。 – 2011-01-11 02:33:07

9
class HasArea a where 
    width :: (Num n) => a -> n 

类型(Num n) => a -> n意味着对于任何类型n这是Num一个实例,width必须能够返回该类型的值。因此,对于任何价值Tv,其中THasArea实例下面的代码必须是有效的:

let x :: Integer = width v 
    y :: Double = width v 
in 
    whatever 

然而,这是不是你Room实例的情况。例如对于Room Integery :: Dobule = width v无效。

为了使您的工作,例如,你可以做这样的事情:

data Room n = Room n n deriving Show 

class HasArea a where 
    width :: (Num n) => a n -> n 

instance HasArea Room where 
    width (Room w h) = w 

在这里我们不说Room IntegerRoom Float等都是HasArea实例,而是RoomHasArea一个实例。和width类型是这样的,给定a n类型的值时,它产生n类型的值,所以如果你把一个Room Integer,你回来了Integer。这种方式适合。

+0

我明白了,谢谢你。这就说得通了。 – 2011-01-11 00:59:25