Haskell用户定义的类型(User-Defined Types)
类型同义词
您已经有一个用户定义类型的示例:type Cipher = String
如果需要,可以嵌套类似这种情况的类型同义词:
type Pos = (Int, Int)
type Trans = Pos -> Pos
(Pos 表示 2D 点,Trans 是采用一个 2D 点并返回另一个点的函数的类型)
虽然您可以引用其他类型的同义词,并将它们嵌套在中,但您不能自行引用它们
定义新类型
如果要定义一个全新的类型(而不是现有类型的同义词),则需要使用数据机制
例如,Bool 在标准 Prelude 中定义如下:
数据布尔 = 假 | 真的
“|”读作“或”,替代项称为构造函数。
构造函数必须以大写字母开头(就像类型一样)
你不能在不同的类型定义中使用同名的构造函数
构造函数名称没有实际意义,它们只是标签(但请选择合理的标签,就像您选择合理的函数和参数名称一样)
使用数据声明
如果你以这种方式定义新类型,你可以像 Haskell 中的常规类型一样使用它们,例如:
data Move = North | East | South | West
move :: Move -> Pos -> Pos
move North (x, y) = (x, y+1)
move East (x, y) = (x+1, y)
move South (x, y) = (x, y-1)
move West (x, y) = (x-1, y)
“Move”类型可以采用四个值之一;函数“move”(注意小写“m”)与 Move 类型完全不同)
构造函数可以有参数!
Move 和 Bool 都使用不带参数的构造函数,但它们可以更复杂,例如
data Shape = Circle Float | Rect Float Float
在这里,我们定义了一个 Shape 类型,它可以是圆形或矩形。
如果是圆形,则使用单个参数构造;如果是矩形,两个参数。
area :: Shape -> Float
area (Circle r) = pi * r^2
area (Rect l w) = l * w
Shape 的构造函数实际上是构造函数。 您甚至可以检查它们的类型,例如
参数化数据声明
数据声明也可以参数化。
你们中的一些人已经使用了 Maybe 类型,它被定义为:
data Maybe a = Nothing | Just a
这表示“Maybe a”类型的值可以是 Nothing 或 Just x 形式,其中 x 是 a 类型的值
有些人发现它是因为他们发现了 elemIndex 函数,它可以找到一个元素在列表中的第一个位置。 它需要一个 Maybe 类型,因为列表中可能没有该项目的实例。
新式Newtype
如果您的数据类型只有一个构造函数和一个参数,则可以改用 newtype 机制。
以下三种类型声明非常相似:
newtype Nat = N Int
type Nat = Int
data Nat = N Int
特别是第一个和最后一个非常相似。 newtype 虽然更有效。 编译器使用 newtype 进行检查,然后将其替换为运行时的类型同义词
递归类型
请记住,这是无效的
type Tree = (Int, [Tree])
但是使用 newtype 或数据机制声明的类型可以是递归的,所以我可以
data Tree a = Empty | Node a [Tree a]
或者如果我想要一棵二叉树:
data Tree a = Empty | Node (Tree a) a (Tree a)
类Classes
特别看一下 Eq,它在序言中被声明为:
(==), (/=) :: a -> a -> Bool
x /= y = not (x == y)
这意味着,要成为此类的实例的类型,它必须支持相等和不等运算符。
假设我希望能够根据它们的面积订购我的 Shape 类型(如前所述)的项目,我可以指定 shape 类派生该父类:
data Shape = Circle Float | Rect Float Float
deriving (Eq, Ord)
这仅在参数是这些类的成员时才有效
这将为我们提供一个表达式的答案,例如
Rect 1.0 4.0 < Rect 2.0 3.0
因为 Float 的参数本身就派生出 Eq 和 Ord
它并不总是给我们正确的答案:
Rect 1.0 4.0 < Rect 2.0 1.0
True
Rect 1.0 4.0 < Rect 1.0 3.0
False
Rect 1.0 4.0 < Rect 2.0 1.0
True
实例Instances
或者,我们可以明确说明如何解释这些运算符。
使用实例机制:
instance Eq Shape where
Circle r1 == Circle r2 = r1 == r2
Rect a b == Rect c d = (a*b) == (c*d)
instance Ord Shape where
compare (Circle r1) (Circle r2) = compare r1 r2
compare (Rect a b) (Rect c d) = compare (a*b) (c*d)
我们不需要定义每个运算符,就像 Eq 和 Ord 类一样,它们是如何根据我们提供的那些计算出来的