下面给出的代码编译好了。
data Car p q r = Car {company :: p
, model :: q
, year ::r
} deriving (Show)
tellCar :: (Show a) => Car String String a -> String
哪些基本原则/惯例/逻辑可以提醒我只需要在"tellCar"中选择"Show a",而不是任何其他选项?我在哪里可以找到学习这些原则/惯例/逻辑的资源
如果我在tellCar中错误地拍摄了"Show Car",则在编译时会收到以下错误消息:
*Main> :load "/home/optimight/baby.hs"
[1 of 1] Compiling Main ( /home/optimight/baby.hs, interpreted )
/home/optimight/baby.hs:96:18:
Expecting three more arguments to `Car'
In the type signature for `tellCar':
tellCar :: Show Car => Car String String a -> String
Failed, modules loaded: none.
如果我在tellCar中错误地拍摄了"Show z",则在编译时会收到以下错误消息:
*Main> :load "/home/optimight/baby.hs"
[1 of 1] Compiling Main ( /home/optimight/baby.hs, interpreted )
/home/optimight/baby.hs:96:1:
Ambiguous constraint `Show z'
At least one of the forall'd type variables mentioned by the constraint
must be reachable from the type after the '=>'
In the type signature for `tellCar':
tellCar :: Show z => Car String String a -> String
Failed, modules loaded: none.
如果我在tellCar中错误地使用了"Show String",则在编译时会收到以下错误消息:
Prelude> :load "/home/optimight/baby.hs"
[1 of 1] Compiling Main ( /home/optimight/baby.hs, interpreted )
/home/optimight/baby.hs:96:1:
Non type-variable argument in the constraint: Show String
(Use -XFlexibleContexts to permit this)
In the type signature for `tellCar':
tellCar :: Show String => Car String String a -> String
Failed, modules loaded: none.
tellCar :: (Show a) => Car String String a -> String
请记住Show a
在这里的意思是:
在
=>
之后的类型规范中,a
应该有一个Show
的实例。
这意味着Car
数据构造函数的第三个参数必须是具有Show
实例的某种类型。
指定(Show Car) => …
或(Show String) => …
是没有意义的;这些具体类型可能有也可能没有Show
的实例(在这种情况下,它们都有),但它并没有阐明实际类型Car String String a -> String
。
指定(Show z) => …
表示:
在
=>
之后的类型规范中,z
应该有一个Show
的实例。
注意,下面的类型是Car String String a -> String
,我们可以得出结论,a)z
从未被提及,所以我们可以忽略(Show z) => …
,并且b(a
是多态的,没有约束!(也就是说,你不能依赖它有任何特定的类型类实例,包括Show
)
主要思想:类型签名中在=>之前列出的每个约束都是为了将一个或多个类型变量约束到类型签名中=>的右侧。
原则:类型签名中的约束总是在某个地方有一个类型变量。
编写Show String =>
或Show Car =>
没有帮助,错误消息告诉这是因为String
没有类型变量(总是以小写字母开头)。这是因为instance Show String
在tellCar
的作用域中可见或不可见,并且您永远不需要将完全具体的实例列为类型中的约束。
原则:您列出的约束必须提到类型签名中=>右侧的至少一个类型变量。对于LANGUAGE扩展,约束可能会提到零个或多个仅存在于类型签名中=>左侧的额外类型变量。
写入tellCar :: Show z => Car String String a -> String
违反了这一点,因为a
是=>的RHS上唯一的类型变量,而Show z
没有提到a
。此Show z
不约束类型变量a
。
更具体地说,对于您的案例,您编写了deriving (Show)
,它自动生成了一个实例:
instance (Show p, Show q, Show r) => Show (Car p q r) where
showsPrec = ...
只有当p,q,r
有Show
实例时,自动生成的代码才有效。您对的专业化
tellCar :: (Show a) => Car String String a -> String
提到CCD_ 34。在tellCar
中的Car String String a
上使用Show
为Show (Car p q r)
选择自动生成的实例,并创建对Show String
和Show a
的需求。编译器然后从隐式导入的Prelude
模块中看到一个Show String
的实例,只留下悬挂的Show a
约束。对a
的这种约束影响了tellCar
的类型。
了解Haskell类型系统的资源是关于Haskell的书籍之一。
编辑:Haskell 98报告中涉及这一点的确切部分似乎是第4.1.3节。更多背景信息请参阅"哈斯克尔的历史"。
您只需要对多态类型变量进行约束,因为函数不知道类型是什么,并且您为编译器提供了使其知道它将工作的最少信息。
Show Car =>
(或Show String
)作为约束并不意味着什么。编译器已经知道Car p q r
有一个Show实例。如果Car p q r
没有的Show实例,那么编译器也会知道!因此,如果允许这种限制,它要么是多余的,要么是矛盾的。
Show z
的错误会对您有所帮助。您正在约束一个没有在类型签名中使用的变量,所以几乎可以肯定这是一个错误。