下面给出的代码编译,好的。
data Car p q r = Car {company :: p
, model :: q
, year ::r
} deriving (Show)
tellCar :: (Show a) => Car String String a -> String
哪些基本原则/惯例/逻辑可以提醒我,我只需要在'tellCar'中显示'显示',而不是任何其他选项?我在哪里可以找到资源来学习这些原则/惯例/逻辑?
如果我错误地在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.
答案 0 :(得分:6)
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
)
答案 1 :(得分:5)
主要思想:在=>之前列出的每个约束在类型签名中,约束在=>右侧的一个或多个类型变量在类型签名中。
原则:类型签名中的约束在某处总是有一个类型变量。
编写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 = ...
自动生成的代码仅在Show
有p,q,r
个实例时才有效。你的专长
tellCar :: (Show a) => Car String String a -> String
提及Car String String a
。在Show
的{{1}}上使用Car String String a
会为tellCar
选择自动生成的实例,并需要Show (Car p q r)
和Show String
。然后,编译器会从隐式导入的Show a
模块中看到Show String
的实例,只留下悬空Prelude
约束。 Show a
上的此约束会感染a
的类型。
了解Haskell类型系统的资源是books on Haskell之一。
编辑:Haskell 98报告的精确部分涵盖了section 4.1.3。更多背景信息位于"A History of Haskell"。
答案 2 :(得分:4)
您只需要对多态类型变量进行约束,因为该函数不知道该类型是什么,并且您为编译器提供了使其知道它将起作用的最小信息。
Show Car =>
(或Show String
)并不意味着任何约束。编译器已经知道Car p q r
有一个Show实例。如果Car p q r
没有 有一个Show实例,那么编译器也会知道它!因此,如果允许这种约束,它将是多余的或矛盾的。
Show z
的错误可以帮助您。您正在约束未在类型签名中使用的变量,因此几乎肯定是一个错误。