有了前面一些基础介绍,现在我们开始介绍 Haskell 中关于类型的各种知识。

由于 Haskell 支持类型推导 (Type Inference),允许我们不对类型进行显示声明,但对于初学者,有时我们可能依旧需要类型检查来帮助我们理解和学习。在 GHCi 中,可以使用 :t 表达式 命令来检查类型:

Haskell> :t 'a'
'a' :: Char
Haskell> :t True
True :: Bool
Haskell> :t (1, 'a')
(1, 'a') :: Num t => (t, Char)
Haskell> :t (False, 'a')
(False, 'a') :: (Bool, Char)
Haskell> :t 1 == 2
1 == 2 :: Bool
Haskell>

你可以将 :: 读作 “是一个”、”类型为”。

对于明确的类型,其首字母均为大写,例如刚才的示例中 'a'Char 类型。

类似的,函数也有类型,这一点在前面函数定义一文已经有过说明。定义函数时进行类型声明是一个好的习惯。

常见类型

Int

Int 类型表示整数,它是有界的 (bounded),也就是说这种类型的值在一个最小值与最大值范围之间,我们使用 GHC 时,这个最大值与最小值的界定根据不同的计算机会有所不同,一般的,如果你是用 64 位计算机,则 Int 值在 -263 与 263 - 1 之间。

Integer

Int 类似,也表示整数,但它的无界的,也就意味着这种类型的值可以极其大或及其小,远远超出 Int 表示的范围。相对的,效率要低于 Int

Float

Float 表示单精度浮点数。

Double

Double 表示双精度浮点数。两种浮点数类型的区别在于精度不同,也就是说,小数点后可用的数字位数长度不同。但相应的也会占据更多内存控件。

Bool

Bool 表示布尔 (Boolean) 值,这种类型只有 TrueFalse 两种值。

Char 与 String

Char 是字符 (Character) 类型,表示一个 Unicode 字符。例如 'a'。由字符组成的列表叫做字符串,即 String,它是 [Char] 的别名,二者完全等价,但 String 更易于书写和阅读。

元组

元组也是类型,但通常它是一个统称,其实际类型取决于元素类型和数量。理论上存在无数种类型的元组,但实际中元组中元素数量最大为 62,最小为 0,即空元组 (()),空援组也是一种类型。

类型变量

对于函数,有时需要处理多种不同类型。这不但让函数变得更加合理,增强了通用性,避免为每一种类型都定义类似函数的尴尬。举个例子,我们前面用过的 head 函数可以接受一个列表数据,不论列表元素是何种类型都返回数据。那么,我们来看看它的类型:

Haskell> :t head
head :: [a] -> a
Haskell>

刚才我们说过,类型的首字母是大写,所以此处的 a 绝不是类型。事实上,它是一个类型变量 (Type Variable),表示 a 可以是任何类型。这样,在保证类型安全的前提下,轻松的实现了一个通用函数。

类型变量的命名允许使用多个字符,例如 somename,但在习惯上,通常使用单个字符,例如 axn

使用类型变量的函数,称为多态函数 (Polymorphic Function)。

需要注意的是,当我们使用多个类型变量时,并不表示它们的类型一定不同,例如:

Haskell> :t fst
fst :: (a, b) -> a
Haskell>

这个例子中,ab 可以是同类型,可以是不同类型,但不论怎样,它们始终是 fst 函数不同的参数。