Haskell 学习笔记二:数据类型

Haskell 拥有非常神奇且优秀的数据类型系统。它非常先进,一些理念被其他的语言所借鉴,比如 Java 的 <T> (generics)。

举个例子:

data Bool = True | False

这里就定义了 Bool 类型。Bool 可以是 True 或者 False他们都是 Bool

对 OOP 熟悉的同学肯定对 polymorphism 的概念了然于胸。Haskell 的类型系统会让你对 polymorphism 的理解更上层楼,它简直就是艺术品 ,写代码像写诗

再举个例子,

data Coordinate = MkCoordinate Integer Integer

这里我们定义了 Coordinate 类型,数据是两个 Integer。MkCoordinate 被称为 constructor。新建一个实例的时候需要用到 constructor,后面跟具体的数据。比如新建一个 (0, 0) 的坐标(Coordinate)的话只需要这么做:

MkCoordinate 0 0

非常优雅,不是吗?

Generics

Haskell 一大令人称赞的设计是它的 generics。

⬆️这样的事情在21世纪发生简直是不可容忍的!

回到我们刚刚的例子,我们的 Coordinate 类型只能接受两个 Integer,如果我们想让他接受其他的类型,比如 Int[1]Float 甚至虚数[2]呢?

data Coordinate2 a = MkCoordinate2 a a

这里我们用到了 generics,我的理解是这是一个 Coordinate2 of a 的类型,而 a 可以是任何类型。MkCoordinate2 现在可以跟任何类型的两个参数,比如 MkCoordiante2 1 1 或者 MkCoordinate2 0.1 1.5

如果更进一步,我们想要它能接受两个不同类型的参数呢?

data Coordinate3 a b = MkCoordinate3 a b

很简单对吧。

再进一步,有时候可能这个坐标根本就不存在,我们想加上一个 Null 状态来表达这种情况,可以吗?

结合最上面写过的,可以这么做:

data Coordinate4 a b = MkCoordinate4 a b | Null

简直像写诗,对吧。有坐标的时候用 MkCoordinate4,没有的时候用 Null。他们都是 Coordinate4 类型。Polymorphism!

同样,一个函数要接受 generics 也很简单。比如我们自己定义一个 toList 函数,它接受一个参数,返回一个 list,里面包含着我们的输入:

toList :: a -> [a]
toList i = [i]

运行起来是这样的:

*Main> toList 1
[1]
*Main> toList "hello"
["hello"]

注意它可以接受任何类型的输入,简直太优雅了对不对。

下一集:Haskell 学习笔记三:Functors


  1. Int 和 Integer 的区别在于,Int 是有限位数的,更像 C 的整数。Integer 可以无限大,更像数学上的整数。 ↩︎

  2. 看到这里的时候你应该已经能很轻松地写出自己的虚数类型了 ↩︎