你得到的错误告诉你它认为该类型应该是什么;不幸的是,这两种类型都是用类型变量表示的,这使得它更难以看清。第一行表示你给出了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
被重命名为n1
(width :: (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 n
是HasArea
的一个实例,这将需要width
的类型为(Num n, Num n1) => Room n -> n1
。这是无法统一具体的n
与一般n1
这是导致您的类型错误。
有几种方法可以解决这个问题。一种方法(也许是最好的方法),你可以在sepp2k's answer中看到的是使HasArea
取类型变量* -> *
;这意味着,而不是a
本身是一种类型,例如a Int
或a n
是类型。 Maybe
和[]
是类型为* -> *
的示例。 (普通类型如Int
或Maybe 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 Int
和instance 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
)的类型,如果你能准确地找出那些是哪种类型,可以帮助你找出错误所在。
哇,你真把那走出公园。我在Google上搜索这些确切的主题,因为我立即发现缺乏将它用于多种类型的功能。不仅如此,我错过了迄今为止最简单的多参数类型类。谢谢。 – 2011-01-11 02:33:07