Haskell类型类

Haskell的类型类

类型类(typeclass)是定义行为的接口。类似于其他语言中的interface,比如C++的抽象接口类。如果一个类型是某类型类的实例(instance),那么他必须实现该类型定义的行为。千万不要将Haskell的类型类与面向对象语言中的类Class搞混淆

假设定义类一个类型类(接口)名字叫做吃饭,然后又定义了鸟、人、鱼作为吃饭类型类的实例,那么鸟、人、鱼类就需要实现怎么吃饭这个行为。如果用C++的语言表达,可能是需要创建一个类,在里面声明了一个抽象函数(virtual void eat() = 0), 其他的类继承了这个类,那么必须要实现它。
不过个人觉得,这个思想的方法,Rust中的trait似乎更贴切一点。

使用:t (==) 查看 == 在Haskell中的函数原型

1
2
3
4
5
ghci> :t (==)
(==) :: Eq a => a -> a -> Bool

ghci> :t (+)
(+) :: Num a => a -> a -> a

这里出现了一个新的符号 => (实际在上篇文章中使用到了),它的左侧叫做类型约束,这里的 == 和 + 分别约束了使用 Eq和Num两个类。

Eq 类型类

用于判断相等性的类型,Eq类定义了等式(==)和不等式(/=)。由Prelude导出的所有基本数据类型都是Eq的实例,并且Eq可以为其成分也是Eq实例的任何数据类型派生。 Haskell官方没有定义Eq一定要实现什么。 然而,==通常期望实现等价关系,其中两个比较相等的值通过“公共”函数无法区分,“公共”函数是不允许查看实现细节的函数。 例如,对于表示非规范化自然数模100的类型,“公共”函数不会在1和201之间产生差异。

最小完整定义

1
(==) | (/=)

Ord 类型类

Ord 类用于可比较大小的类型。 Ord的实例可以为任何组成类型为Ord的用户定义数据类型派生。数据声明中构造函数的声明顺序决定派生Ord实例的顺序。Ordering数据类型允许一次比较来确定两个对象的精确排序。
和Eq类似,取两个参数,返回一个Bool类型的值,用来表示是否满足对应的比较关系

最小完整定义

1
compare | (<=)

Show 类型类

将值转换为可读字符串。

Show的派生实例具有以下属性,它们与Read的派生实例兼容:

  • show的结果是一个语法正确的Haskell表达式,只包含常量,只要在声明类型时有效地声明了固定性。它只包含在数据类型、括号和空格中定义的构造函数名称。当使用带标签的构造函数字段时,还会使用大括号、逗号、字段名和等号。
  • 如果构造函数被定义为中缀操作符,那么showsPrec将生成构造函数的中缀应用。
  • 如果x中的顶级构造函数的优先级小于d(忽略结合性),则表示将被括在括号中。因此,如果d为0,则结果永远不会被括号包围;如果d是11,则它总是用圆括号括起来,除非它是原子表达式。
  • 如果构造函数是使用记录语法定义的,那么show将生成记录语法表单,其中字段的顺序与原始声明的顺序相同。

除函数以外的所有类型都是SHow的实例,可以使用show函数将值转换为字符串

1
2
3
4
5
6
7
8
ghci> read 3
"3"

ghci> read 5.334
"5.334"

ghci> read True
"True"

最小完整定义

1
showsPrec | show

Read 类型类

解析字符串,生成值。

Read的派生实例做出以下假设,这些假设派生了Show obey的实例:

  • 如果构造函数被定义为中缀操作符,那么派生的Read实例将只解析构造函数的中缀应用程序(而不是前缀形式)。
  • 结合性不用于减少括号的出现,尽管可以使用优先级。
  • 如果构造函数是使用记录语法定义的,则派生的Read将只解析记录语法形式,而且,字段必须按照与原始声明相同的顺序给出。
  • 派生的Read实例允许在输入字符串的令牌之间使用任意的Haskell空白。还允许使用额外的括号。

Read可以看做Show相反的类型类,之前提到的所有类都是Read类型,read函数可以去一个字符串作为参数并转为Read的某个实例的类型

1
2
3
4
5
ghci > read "True" || False
True

ghci > read "8.2" + 3.8
12.0

如果尝试使用read "4"来得到生成一个数字4的效果,那么这是不可能的,会得到一个大大的报错。因为GHCI并不能准确知道到底要个什么玩意来满足屏幕面前的你
所以如果在没有一个表达式的情况下,或者说最好给read转换增加一个显示的类型注解

1
2
3
4
5
ghci> "5" :: Int
5

ghci> "5" :: Float
5.0

最小完整定义

1
readsPrec | readPrec

Enum 类型类

Enum的实例对象都是可连续的(枚举),每一个值都有相应的后继和前驱,分别可以通过succpred得到。

1
2
3
4
5
ghci> ['a'..'e']
"abcde"

ghci> succ 'B'
'C'

Bounded 类型类

类型存在一个上限和下限,分别可以通过maxBound和minBound两个函数获得

1
2
3
4
5
ghci> minBound ::Int
-9223372036854775808

ghci> maxBound ::Char
'\1114111'

Num 类型类

表示一个数值的类型类,它的实例都具有数的特征。
只有具有Show和Eq的实例类型,才可以成为Num类型类的实例

Floating 类型类

包含Float和Double两种浮点类型,用于存储浮点数。
使用Floating类型类的实例类型作为参数或者返回类型的函数,一般需要用到浮点数进行某种计算

Integral 类型类

另一种表示数值的类型类。Num类型类包含了实数和整数相关的所有类型,Integral只包含了整数。

详细的对于类型类的介绍可以参考以下官方文档链接:
https://hackage.haskell.org/package/ghc-prim-0.7.0/docs/GHC-Classes.html
https://hackage.haskell.org/package/base-4.15.1.0/docs/GHC-Show.html#t:Show
https://hackage.haskell.org/package/base-4.15.1.0/docs/Text-Read.html#v:Read


Haskell类型类
http://cvrain.cloudvl.cn/2023/11/14/Haskell/haskell-typeclas/
作者
ClaudeRainer
发布于
2023年11月14日
许可协议