Haskell:类型与类型类 读书笔记核心内容
一、 核心概念:静态类型与类型推断
静态类型系统 (Static Type System):
- Haskell 是静态类型的。这意味着每个表达式的类型在编译时就已经确定了。
- 这样做的好处是极大地提高了代码的安全性。例如,你不能将一个布尔值和数字相除,这样的错误在编译阶段就会被发现,而不是在程序运行时崩溃。
类型推断 (Type Inference):
- 与 Java 或 Pascal 不同,Haskell 具有强大的类型推断能力。
- 你不必为每个函数或表达式显式地写出类型。Haskell 编译器通常能自动推断出它们的类型。
- 尽管如此,为顶层函数(toplevel functions)编写明确的类型签名(Type Declaration)被认为是最佳实践。
二、 类型 (Types)
什么是类型?
- 类型是给表达式贴上的“标签”,用于说明该表达式属于哪个“类别”。例如:
True是Bool类型。"hello"是[Char]类型(即String)。'a'是Char类型。(True, 'a')是(Bool, Char)类型。
- 类型是给表达式贴上的“标签”,用于说明该表达式属于哪个“类别”。例如:
在 GHCI 中检查类型:
- 使用
:t命令可以查看任何表达式的类型。 - 例如:
:t 'a'会返回'a' :: Char。 ::读作“具有类型”。
- 使用
常见的内置类型:
Int:有界的整数(通常为 32 或 64 位)。它更高效。Integer:无界的整数。可以用来表示任意大的数字,但效率低于Int。Float:单精度浮点数。Double:双精度浮点数。Bool:布尔值,只有True和False两个值。Char:单个字符。String:字符串,它实际上是[Char](字符列表)的类型别名。
三、 函数的类型
- 类型签名 (Type Signatures):
- 函数也有类型。我们使用
::来声明它。 - 格式:
functionName :: argumentType1 -> argumentType2 -> returnType - 例如:
addThree :: Int -> Int -> Int -> Int - 这表示
addThree函数接收三个Int类型的参数,并返回一个Int类型的值。最后一个类型总是返回类型。
- 函数也有类型。我们使用
四、 类型变量 (Type Variables) 与多态
- 多态函数 (Polymorphic Functions):
- 当一个函数的类型签名中包含小写字母开头的名称(如
a,b,t)时,这些就是“类型变量”。 - 类型变量意味着“可以是任何类型”。
- 例如,
head函数的类型是head :: [a] -> a。 - 这表示
head接受一个任何类型a的元素组成的列表([a]),并返回一个该类型a的元素。它不关心列表里具体是Int还是String。 - 这类似于其他语言中的“泛型”。
- 当一个函数的类型签名中包含小写字母开头的名称(如
五、 类型类 (Typeclasses)
什么是类型类?
- 类型类不是面向对象语言中的“类”。它更像是定义行为的接口(Interface)。
- 如果一个类型是某个类型类的“实例”(instance),那么它必须实现了该类型类所描述的行为(即特定的函数)。
类约束 (Class Constraints):
- 在类型签名中,使用
=>来表示类型约束。 - 例如:
(==) :: (Eq a) => a -> a -> Bool - 这读作:
==函数接受两个类型为a的值,并返回一个Bool。前提条件是:类型a必须是Eq类型类的实例。
- 在类型签名中,使用
常见的类型类:
Eq:用于支持相等性比较的类型。它提供了==和/=(不等于) 函数。Ord:用于支持排序的类型。它提供了<,>,<=,>=等函数。一个类型必须首先是Eq的实例,才能成为Ord的实例。Show:用于可以转换成字符串的类型。它提供了show函数(例如show 3返回"3")。Read:Show的反向操作。用于将字符串转换回特定类型。它提供了read函数。- 注意:使用
read时,Haskell 常常无法推断你想要的目标类型,因此需要显式类型注解,例如:read "5" :: Int。
- 注意:使用
Enum:用于可以枚举的类型(即有顺序的)。这使得它们可以用于列表的范围表达式,如['a'..'e']或[1..5]。Bounded:用于有明确上界和下界的类型。它提供了minBound和maxBound。Num:用于“数字”的类型类。像20这样的字面量本身是多态的,其类型是(Num t) => t。这意味着20既可以是Int,也可以是Float或Double,Haskell 会根据上下文来推断。Integral:Num的子类,只包括整数(Int,Integer)。Floating:Num的子类,只包括浮点数(Float,Double)。
fromIntegral函数:- 这是一个非常有用的函数,其类型为
fromIntegral :: (Num b, Integral a) => a -> b。 - 它用于将一个整数类型(如
Int)转换成一个更通用的数字类型(如Float或Double),以便在计算中混合使用它们。
- 这是一个非常有用的函数,其类型为