在data
声明中,类型构造函数是等号左侧的内容。该数据构造(S)是在右手边的东西的等号。在需要类型的地方使用类型构造函数,在期望值的地方使用数据构造函数。
数据构造器
为简单起见,我们可以从代表一种颜色的类型的示例开始。
data Colour = Red | Green | Blue
在这里,我们有三个数据构造函数。Colour
是一个类型,并且Green
是一个包含type值的构造函数Colour
。同样,Red
和Blue
都是构造type值的构造函数Colour
。我们可以想象加香料!
data Colour = RGB Int Int Int
我们仍然只有type Colour
,但RGB
没有值-这是一个接受三个Ints并返回值的函数!RGB
有类型
RGB :: Int -> Int -> Int -> Colour
RGB
是一个数据构造函数,它是一个将某些值作为参数的函数,然后使用这些值构造一个新值。如果您已完成任何面向对象的编程,则应认识到这一点。在OOP中,构造函数还将一些值作为参数并返回一个新值!
在这种情况下,如果我们应用RGB
三个值,我们将获得一个颜色值!
Prelude> RGB 12 92 27
#0c5c1b
我们通过应用数据构造函数构造了类型的值Colour
。数据构造函数要么包含一个像变量一样的值,要么将其他值用作其参数并创建一个新值。如果您之前已经进行过编程,那么这个概念对您来说应该并不陌生。
中场休息
如果您想构造一个二叉树来存储String
s,您可以想像这样做
data SBTree = Leaf String
| Branch String SBTree SBTree
我们在这里看到的是一个SBTree
包含两个数据构造函数的类型。换句话说,有两个函数(即Leaf
和Branch
)将构造该SBTree
类型的值。如果您不熟悉二叉树的工作原理,请挂在那里。您实际上不需要知道二叉树如何工作,只需知道二叉树String
以某种方式存储。
我们还看到两个数据构造函数都接受一个String
参数–这是它们将存储在树中的String。
但!如果我们还希望能够存储该怎么办Bool
,我们必须创建一个新的二叉树。它可能看起来像这样:
data BBTree = Leaf Bool
| Branch Bool BBTree BBTree
类型构造器
这两个SBTree
和BBTree
是类型构造。但是有一个明显的问题。您看到它们有多相似吗?这表明您确实需要在某个地方添加参数。
因此,我们可以这样做:
data BTree a = Leaf a
| Branch a (BTree a) (BTree a)
现在,我们将类型变量 a
作为类型构造函数的参数。在此声明中,BTree
已成为一个函数。它以类型作为参数,并返回一个新类型。
在此重要的考虑之间的区别具体类型(例子包括Int
,[Char]
和Maybe Bool
),这是一个可以在你的程序被分配到一个值类型和类型构造函数,你需要养活一个类型能成为分配给一个值。值永远不能是“列表”类型,因为它必须是“ 某物列表”。本着同样的精神,值永远不能是“二叉树”类型,因为它必须是“存储某些东西的二叉树”。
例如,如果我们传入,Bool
作为其参数BTree
,它将返回type BTree Bool
,它是存储Bool
s 的二叉树。替换类型变量的每次出现a
与类型Bool
,你可以看到自己是如何的真实。
如果你愿意,你可以查看BTree
与该功能样
BTree :: * -> *
种类有点像类型- *
表示具体类型,所以我们说的BTree
是从具体类型到具体类型。
包起来
退后一步,注意相似之处。
如果我们想要值的微小变化,则带有参数的数据构造函数很酷–我们将这些变化放入参数中,然后让创建值的人决定要放入哪些参数。同理,带参数的类型构造函数很酷如果我们希望我们的类型稍有变化!我们将这些变体作为参数,然后让创建类型的人决定要输入哪些参数。
案例研究
作为这里的房屋,我们可以考虑Maybe a
类型。它的定义是
data Maybe a = Nothing
| Just a
这里Maybe
是一个返回具体类型的类型构造函数。Just
是一个返回值的数据构造函数。Nothing
是包含值的数据构造函数。如果我们看一下的类型Just
,就会看到
Just :: a -> Maybe a
换句话说,Just
采用type a
的值并返回type 的值Maybe a
。如果我们看一下Maybe
,我们会看到
Maybe :: * -> *
换句话说,Maybe
采用具体类型并返回具体类型。
再来一次!具体类型和类型构造函数之间的区别。您无法创建Maybe
s 的列表-如果尝试执行
[] :: [Maybe]
你会得到一个错误。不过,您可以创建Maybe Int
或的列表Maybe a
。那是因为Maybe
是类型构造函数,但是列表需要包含具体类型的值。Maybe Int
并且Maybe a
是具体类型(或者,如果需要,可以调用返回具体类型的类型构造函数。)
Car
它既是类型构造函数(位于的左侧=
)又是数据构造函数(位于右侧)。在第一个示例中,Car
类型构造函数不接受任何参数,在第二个示例中,它接受三个参数。在这两个示例中,Car
数据构造函数都使用三个参数(但是,这些参数的类型在一种情况下是固定的,而在另一种情况下是固定的)。