我是Haskell的新手,目前正在通过Real World Haskell。该书说,类型构造函数仅在类型签名中使用,而值构造函数在实际代码中使用。它还给出了一个声明示例,表明两者的名称彼此独立。为什么首先需要两个构造函数,如果在实际代码中只使用其中一个?由于我们不会在实际代码中使用类型构造函数,因此类型构造函数的用途是什么?
答案 0 :(得分:22)
也许这些名字有点误导。 类型构造函数表示您要声明的类型的名称。它们之所以被称为是因为它们构建类型,而不是值:实际上,它们(可能)在类型变量上进行参数化,它们定义了一系列类型。它们的行为类似于C ++的模板和Java的泛型。在data MyType a b = Constr a b
中,MyType是一种类型构造函数,它使用两种类型a
和b
来构建新类型(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 5
是Tree Int
值,Leaf "banana"
是Tree String
值,依此类推。
同样适用于Branch
。它需要两个树值并构造一个树节点,这些树作为子节点。例如,Branch (Leaf 2) (Leaf 7)
是Tree Int
值。
答案 2 :(得分:7)
获得关于类型和值的直觉的一种简便方法是前者是编译时值,而后者是运行时值。换句话说,Type构造函数是Haskell类型集中值的构造函数,唯一的目的是在编译时键入程序。这也意味着您无法在运行时构造类型,并且无法在编译时构造值。
所以,因为你不能在运行时基于类型值显式分支(虽然你可以隐式使用类型类),类型构造函数作为运行时对象完全没用,并且在很多情况下完全没有最终二进制。相反,因为值构造函数允许在运行时在它们的类型集中构造值,所以它们作为编译时对象完全没用。
由于这个简单的属性,Type构造函数和Value构造函数可以明确地共享名称。