Haskell类型与数据构造函数

Haskell类型与数据构造函数,haskell,Haskell,我正在向你学习哈斯克尔。我很难理解类型构造函数和数据构造函数。例如,我真的不明白这两者之间的区别: data Car = Car { company :: String , model :: String , year :: Int } deriving (Show) 这是: data Car a b c = Car { company :: a

我正在向你学习哈斯克尔。我很难理解类型构造函数和数据构造函数。例如,我真的不明白这两者之间的区别:

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
类型的数据。第二个我不太明白

另外,如何定义这样的数据类型:

data Color = Blue | Green | Red
data BBTree = Leaf Bool
            | Branch Bool BBTree BBTree
data Config = XML_Config {...} | JSON_Config {...}
适合所有这些吗

据我所知,第三个示例(
颜色
)是一种可以处于三种状态的类型:
蓝色
绿色
红色
。但这与我对前两个示例的理解有冲突:是不是类型
Car
只能处于一种状态,
Car
,它可以采用各种参数来构建?如果是这样的话,第二个例子如何适用


本质上,我正在寻找一个统一上述三个代码示例/结构的解释。

它是关于类型的:在第一种情况下,您设置了类型
字符串(对于公司和型号)和
Int
(对于年份)。在第二种情况下,您的应用程序更通用<代码>a
b
c
可能与第一个示例中的类型相同,完全不同。例如,将年份指定为字符串而不是整数可能很有用。如果你愿意,你甚至可以使用你的
颜色
类型。

第二种类型中有“多态性”的概念

abc
可以是任何类型。例如,
a
可以是
[String]
b
可以是
[Int]
c
可以是
[Char]

第一种类型是固定的:公司是
字符串
,型号是
字符串
,年份是
Int

Car示例可能没有显示使用多态性的意义。但假设您的数据是列表类型。列表可以包含
String、Char、Int…
在这些情况下,您需要第二种定义数据的方法

至于第三种方式,我认为它不需要与前一种方式相适应。这只是Haskell中定义数据的另一种方法

这是我作为初学者的拙见


顺便说一句:确保你的大脑得到良好的训练,并对此感到舒适。这是以后理解Monad的关键。

从最简单的例子开始:

data Color = Blue | Green | Red
这定义了一个不带参数的“类型构造函数”
Color
,它有三个“数据构造函数”
Blue
Green
Red
。没有一个数据构造函数接受任何参数。这意味着有三种类型的
颜色
蓝色
绿色
红色

当需要创建某种类型的值时,使用数据构造函数。比如:

myFavoriteColor :: Color
myFavoriteColor = Green
使用
Green
数据构造函数创建值
myFavoriteColor
myFavoriteColor
将为
Color
类型,因为这是数据构造函数生成的值类型

当需要创建某种类型时,使用类型构造函数。在书写签名时通常会出现这种情况:

isFavoriteColor :: Color -> Bool
在本例中,您正在调用
Color
类型构造函数(不带参数)

还和我在一起吗

现在,假设您不仅想要创建红/绿/蓝值,还想要指定“强度”。例如,一个介于0和256之间的值。您可以通过向每个数据构造函数添加一个参数来实现这一点,因此最终得到:

data Color = Blue Int | Green Int | Red Int
现在,三个数据构造函数中的每一个都接受类型为
Int
的参数。类型构造函数(
Color
)仍然不接受任何参数。所以,我最喜欢的颜色是深绿色,我可以写

    myFavoriteColor :: Color
    myFavoriteColor = Green 50
再次,它调用
Green
数据构造函数,我得到一个
Color
类型的值

想象一下,如果你不想支配人们如何表达颜色的强度。有些人可能像我们刚才那样想要一个数值。其他人可能只需要一个表示“亮”或“不太亮”的布尔值就可以了。解决方法是不要在数据构造函数中硬编码
Int
,而是使用类型变量:

data Color a = Blue a | Green a | Red a
现在,我们的类型构造函数接受一个参数(另一种类型,我们称之为
a
!),所有的数据构造函数将接受该类型
a
的一个参数(一个值!)。所以你本来可以

myFavoriteColor :: Color Bool
myFavoriteColor = Green False

请注意,我们如何使用一个参数(另一种类型)调用
Color
类型构造函数,以获得数据构造函数将返回的“有效”类型。这涉及到你可能想边喝一两杯咖啡边读的概念


现在我们了解了什么是数据构造函数和类型构造函数,以及数据构造函数如何将其他值作为参数,类型构造函数如何将其他类型作为参数。HTH.

正如其他人指出的那样,多态性在这里并没有那么可怕的用处。让我们来看另一个您可能已经熟悉的示例:

Maybe a = Just a | Nothing
此类型有两个数据构造函数<代码>没有任何内容有点无聊,它不包含任何有用的数据。另一方面,
只是
包含一个
a
的值-无论
a
可能有什么类型。让我们编写一个使用此类型的函数,例如获取
Int
列表的头部(如果有)(我希望您同意这比抛出错误更有用):

因此在本例中,
a
是一个
Int
,但它也适用于任何其他类型。事实上,您可以使我们的函数适用于每种类型的列表(即使不更改实现):

另一方面,您可以编写只接受某种类型的
的函数,例如

doubleMaybe :: Maybe Int -> Maybe Int
doubleMaybe Just x = Just (2*x)
doubleMaybe Nothing= Nothing
长话短说,通过多态性,您可以让您自己的类型灵活地处理其他不同类型的值

doubleMaybe :: Maybe Int -> Maybe Int
doubleMaybe Just x = Just (2*x)
doubleMaybe Nothing= Nothing
data Colour = Red | Green | Blue
data Colour = RGB Int Int Int
RGB :: Int -> Int -> Int -> Colour
Prelude> RGB 12 92 27
#0c5c1b
data SBTree = Leaf String
            | Branch String SBTree SBTree
data BBTree = Leaf Bool
            | Branch Bool BBTree BBTree
data BTree a = Leaf a
             | Branch a (BTree a) (BTree a)
BTree :: * -> *
data Maybe a = Nothing
             | Just a
Just :: a -> Maybe a
Maybe :: * -> *
[] :: [Maybe]
data Eg1 = One Int | Two String
data Eg2 = Pair Int String
data Config = XML_Config {...} | JSON_Config {...}