Haskell 类型与数据构造函数 [英] Haskell Type vs Data Constructor
问题描述
我正在从 learnyouahaskell.com 学习 Haskell.我无法理解类型构造函数和数据构造函数.例如,我不太明白这之间的区别:
I am learning Haskell from learnyouahaskell.com. I am having trouble understanding type constructors and data constructors. For example, I don't really understand the difference between this:
data Car = Car { company :: String
, model :: String
, year :: Int
} deriving (Show)
还有这个:
data Car a b c = Car { company :: a
, model :: b
, year :: c
} deriving (Show)
我知道第一个是简单地使用一个构造函数 (Car
) 来构建 Car
类型的数据.我真的不明白第二个.
I understand that the first is simply using one constructor (Car
) to built data of type Car
. I don't really understand the second one.
另外,数据类型是如何定义的:
Also, how do data types defined like this:
data Color = Blue | Green | Red
适合所有这些吗?
据我所知,第三个示例(Color
)是一种可以处于三种状态的类型:Blue
、Green
或 <代码>红色代码>.但这与我对前两个示例的理解相冲突:是不是 Car
类型只能处于一种状态,Car
,它可以使用各种参数来构建?如果是这样,第二个例子如何适合?
From what I understand, the third example (Color
) is a type which can be in three states: Blue
, Green
or Red
. But that conflicts with how I understand the first two examples: is it that the type Car
can only be in one state, Car
, which can take various parameters to build? If so, how does the second example fit in?
本质上,我正在寻找统一上述三个代码示例/结构的解释.
Essentially, I am looking for an explanation that unifies the above three code examples/constructs.
推荐答案
在 data
声明中,类型构造函数是位于等号左侧的东西符号.数据构造器是等号右侧的东西.在需要类型的地方使用类型构造函数,在需要值的地方使用数据构造函数.
In a data
declaration, a type constructor is the thing on the left hand side of the equals sign. The data constructor(s) are the things on the right hand side of the equals sign. You use type constructors where a type is expected, and you use data constructors where a value is expected.
为了简单起见,我们可以从代表颜色的类型的示例开始.
To make things simple, we can start with an example of a type that represents a colour.
data Colour = Red | Green | Blue
这里,我们有三个数据构造函数.Colour
是一个类型,Green
是一个构造函数,它包含一个 Colour
类型的值.类似地,Red
和 Blue
都是构造 Colour
类型值的构造函数.不过,我们可以想象给它加点料!
Here, we have three data constructors. Colour
is a type, and Green
is a constructor that contains a value of type Colour
. Similarly, Red
and Blue
are both constructors that construct values of type Colour
. We could imagine spicing it up though!
data Colour = RGB Int Int Int
我们仍然只有 Colour
类型,但是 RGB
不是一个值——它是一个函数,它接受三个 Ints 并返回一个值!RGB
有类型
We still have just the type Colour
, but RGB
is not a value – it's a function taking three Ints and returning a value! RGB
has the type
RGB :: Int -> Int -> Int -> Colour
RGB
是一个数据构造函数,它是一个函数,它以一些值作为参数,然后使用这些值来构造一个新值.如果你做过任何面向对象的编程,你应该认识到这一点.在 OOP 中,构造函数也将一些值作为参数并返回一个新值!
RGB
is a data constructor that is a function taking some values as its arguments, and then uses those to construct a new value. If you have done any object-oriented programming, you should recognise this. In OOP, constructors also take some values as arguments and return a new value!
在这种情况下,如果我们将 RGB
应用于三个值,我们将得到一个颜色值!
In this case, if we apply RGB
to three values, we get a colour value!
Prelude> RGB 12 92 27
#0c5c1b
我们通过应用数据构造函数构造了一个 Colour
类型的值.数据构造函数要么像变量一样包含一个值,要么将其他值作为其参数并创建一个新的值.如果你之前做过编程,这个概念你应该不会很陌生.
We have constructed a value of type Colour
by applying the data constructor. A data constructor either contains a value like a variable would, or takes other values as its argument and creates a new value. If you have done previous programming, this concept shouldn't be very strange to you.
如果你想构造一个二叉树来存储String
,你可以想象做类似
If you'd want to construct a binary tree to store String
s, you could imagine doing something like
data SBTree = Leaf String
| Branch String SBTree SBTree
我们在这里看到的是一个类型SBTree
,它包含两个数据构造函数.换句话说,有两个函数(即Leaf
和Branch
)将构造SBTree
类型的值.如果您不熟悉二叉树的工作原理,请坚持下去.你实际上并不需要知道二叉树是如何工作的,只需要知道这个二叉树以某种方式存储 String
.
What we see here is a type SBTree
that contains two data constructors. In other words, there are two functions (namely Leaf
and Branch
) that will construct values of the SBTree
type. If you're not familiar with how binary trees work, just hang in there. You don't actually need to know how binary trees work, only that this one stores String
s in some way.
我们还看到两个数据构造函数都接受一个 String
参数——这是它们要存储在树中的字符串.
We also see that both data constructors take a String
argument – this is the String they are going to store in the tree.
但是!如果我们还希望能够存储 Bool
,我们必须创建一个新的二叉树.它可能看起来像这样:
But! What if we also wanted to be able to store Bool
, we'd have to create a new binary tree. It could look something like this:
data BBTree = Leaf Bool
| Branch Bool BBTree BBTree
类型构造函数
SBTree
和 BBTree
都是类型构造函数.但是有一个明显的问题.你看到它们有多相似了吗?这表明您确实需要某个参数.
Type constructors
Both SBTree
and BBTree
are type constructors. But there's a glaring problem. Do you see how similar they are? That's a sign that you really want a parameter somewhere.
所以我们可以这样做:
data BTree a = Leaf a
| Branch a (BTree a) (BTree a)
现在我们引入一个类型变量 a
作为类型构造函数的参数.在这个声明中,BTree
变成了一个函数.它接受一个 type 作为它的参数,并返回一个新的 type.
Now we introduce a type variable a
as a parameter to the type constructor. In this declaration, BTree
has become a function. It takes a type as its argument and it returns a new type.
在这里考虑具体类型之间的区别很重要(示例包括Int
、[Char]
和Maybe Bool
) 是一种可以分配给程序中的值的类型,以及一个类型构造函数,您需要提供一个类型才能分配给一个值.值永远不能是list"类型,因为它必须是list of something".本着同样的精神,值永远不能是二叉树"类型,因为它必须是存储东西"的二叉树.
It is important here to consider the difference between a concrete type (examples include
Int
,[Char]
andMaybe Bool
) which is a type that can be assigned to a value in your program, and a type constructor function which you need to feed a type to be able to be assigned to a value. A value can never be of type "list", because it needs to be a "list of something". In the same spirit, a value can never be of type "binary tree", because it needs to be a "binary tree storing something".
如果我们将 Bool
作为参数传入 BTree
,它会返回类型 BTree Bool
,这是一个二叉树存储 Bool
s.将每次出现的类型变量 a
替换为类型 Bool
,您就可以亲眼看到它是如何正确的.
If we pass in, say, Bool
as an argument to BTree
, it returns the type BTree Bool
, which is a binary tree that stores Bool
s. Replace every occurrence of the type variable a
with the type Bool
, and you can see for yourself how it's true.
如果您愿意,可以将 BTree
视为具有 kind
If you want to, you can view BTree
as a function with the kind
BTree :: * -> *
Kinds 有点像类型——*
表示具体类型,所以我们说BTree
是从具体类型到具体类型.
Kinds are somewhat like types – the *
indicates a concrete type, so we say BTree
is from a concrete type to a concrete type.
退后一步,注意相似之处.
Step back here a moment and take note of the similarities.
数据构造函数是一个函数",它接受0个或多个值并返回一个新值.
A data constructor is a "function" that takes 0 or more values and gives you back a new value.
类型构造函数是一个函数",它接受0个或多个类型并返回一个新类型.
A type constructor is a "function" that takes 0 or more types and gives you back a new type.
如果我们希望我们的值有细微的变化,带参数的数据构造函数很酷——我们把这些变化放在参数中,让创建值的人决定他们要放入什么参数.同样的,类型构造函数如果我们希望我们的类型稍有变化,参数是很酷的!我们将这些变体作为参数,让创建类型的人决定他们要放入哪些参数.
Data constructors with parameters are cool if we want slight variations in our values – we put those variations in parameters and let the guy who creates the value decide what arguments they are going to put in. In the same sense, type constructors with parameters are cool if we want slight variations in our types! We put those variations as parameters and let the guy who creates the type decide what arguments they are going to put in.
作为这里的主线,我们可以考虑 Maybe a
类型.它的定义是
As the home stretch here, we can consider the Maybe a
type. Its definition is
data Maybe a = Nothing
| Just a
这里,Maybe
是一个返回具体类型的类型构造函数.Just
是一个返回值的数据构造函数.Nothing
是一个包含值的数据构造函数.如果我们查看 Just
的类型,我们会看到
Here, Maybe
is a type constructor that returns a concrete type. Just
is a data constructor that returns a value. Nothing
is a data constructor that contains a value. If we look at the type of Just
, we see that
Just :: a -> Maybe a
换句话说,Just
接受一个 a
类型的值并返回一个 Maybe a
类型的值.如果我们看一下 Maybe
的类型,我们会看到
In other words, Just
takes a value of type a
and returns a value of type Maybe a
. If we look at the kind of Maybe
, we see that
Maybe :: * -> *
换句话说,Maybe
接受一个具体类型并返回一个具体类型.
In other words, Maybe
takes a concrete type and returns a concrete type.
再来一次!具体类型和类型构造函数之间的区别.您无法创建 Maybe
的列表 - 如果您尝试执行
Once again! The difference between a concrete type and a type constructor function. You cannot create a list of Maybe
s - if you try to execute
[] :: [Maybe]
你会得到一个错误.但是,您可以创建 Maybe Int
或 Maybe a
的列表.那是因为 Maybe
是一个类型构造函数,但是一个列表需要包含一个具体类型的值.Maybe Int
和 Maybe a
是具体类型(或者,如果需要,可以调用返回具体类型的类型构造函数.)
you'll get an error. You can however create a list of Maybe Int
, or Maybe a
. That's because Maybe
is a type constructor function, but a list needs to contain values of a concrete type. Maybe Int
and Maybe a
are concrete types (or if you want, calls to type constructor functions that return concrete types.)
这篇关于Haskell 类型与数据构造函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!