除了Haskell中的类型构造函数之外,为什么还有值构造函数?

时间:2012-04-20 08:25:33

标签: haskell type-constructor

我是Haskell的新手,目前正在通过Real World Haskell。该书说,类型构造函数仅在类型签名中使用,而值构造函数在实际代码中使用。它还给出了一个声明示例,表明两者的名称彼此独立。为什么首先需要两个构造函数,如果在实际代码中只使用其中一个?由于我们不会在实际代码中使用类型构造函数,因此类型构造函数的用途是什么?

3 个答案:

答案 0 :(得分:22)

也许这些名字有点误导。 类型构造函数表示您要声明的类型的名称。它们之所以被称为是因为它们构建类型,而不是值:实际上,它们(可能)在类型变量上进行参数化,它们定义了一系列类型。它们的行为类似于C ++的模板和Java的泛型。在data MyType a b = Constr a b中,MyType是一种类型构造函数,它使用两种类型ab来构建新类型(MyType a b)

值构造函数是您在其他(面向对象)语言中称为“构造函数”的唯一部分,因为您需要它来构建该类型的值。因此,在前面的示例中,如果您使用值构造函数Constr :: a -> b -> MyType a b,则可以构建值Constr "abc" 'd' :: MyType [Char] Char

答案 1 :(得分:16)

这有点像说“为什么我们需要类对象,如果对象是唯一实际运行的东西?”

两种构造者做不同的工作。类型构造函数进入类型签名。值构造函数使用可运行的代码。

在最简单的情况下,类型“构造函数” 只是一个类型名称。在最简单的情况下,类型只有一个值构造函数。所以你最终会得到像

这样的东西

data Point = Point Int Int

你可能会对自己说“为什么我需要两次写Point?”

但现在考虑一个不那么简单的例子:

data Tree x = Leaf x | Branch (Tree x) (Tree x)

这里Tree是一个类型构造函数。你给它一个类型参数,它“构造”一个​​类型。因此Tree Int是一种类型,Tree String是另一种类型,依此类推。 (与C ++中的模板或Java或Eiffel中的泛型一样。)

另一方面,Leaf是值构造函数。给定一个值,它会生成一个1节点的树。因此Leaf 5Tree Int值,Leaf "banana"Tree String值,依此类推。

同样适用于Branch。它需要两个树值并构造一个树节点,这些树作为子节点。例如,Branch (Leaf 2) (Leaf 7)Tree Int值。

答案 2 :(得分:7)

获得关于类型和值的直觉的一种简便方法是前者是编译时值,而后者是运行时值。换句话说,Type构造函数是Haskell类型集中值的构造函数,唯一的目的是在编译时键入程序。这也意味着您无法在运行时构造类型,并且无法在编译时构造值。

所以,因为你不能在运行时基于类型值显式分支(虽然你可以隐式使用类型类),类型构造函数作为运行时对象完全没用,并且在很多情况下完全没有最终二进制。相反,因为值构造函数允许在运行时在它们的类型集中构造值,所以它们作为编译时对象完全没用。

由于这个简单的属性,Type构造函数和Value构造函数可以明确地共享名称。