这是我的代码:
data TC a = DC1 a | DC2 a
getDC :: TC a -> String
getDC (DC1 x) = "created by first data constructor"
getDC (DC2 x) = "created by second data constructor"
在拥抱中:
Main> getDC (DC1 10)
“由第一个数据构造函数”
创建Main> getDC (DC2 10)
“由第二个数据构造函数”
创建因此,解释器可以确定使用哪个数据构造函数。 据我所知,每个值都有一个相关的类型。我们来看看:
Main> :t (DC1 10)
DC1 10 :: Num a => TC a
Main> :t (DC2 10)
DC2 10 :: Num a => TC a
只能在那里看到类型构造函数(TC)。
解释器为什么,在何处以及如何保存有关数据构造函数的其他信息?
答案 0 :(得分:6)
虽然类型提供了重要的编译时信息,但您仍然在运行时操作值。使用的构造函数只是值的属性 - 特别是,您使用模式匹配来确定值中使用的构造函数的选择。
getDC :: TC a -> String
getDC (DC1 x) = "created by first data constructor"
getDC (DC2 x) = "created by second data constructor"
-- or, to be more clear about the pattern matching
getDC :: TC a -> String
getDC dc = case dc of
(DC1 x) -> "created by first data constructor"
(DC2 x) -> "created by second data constructor"
为了更加清晰,让我们尝试制作自然数而不是使用抽象的TC类型。
data Nat = Zero | Succ Nat
换句话说,我们写为0的内容可以表示为Nat
Zero :: Nat
我们写为3的内容可以表示为
Succ (Succ (Succ Zero))
我们可以在`Nat
上写一个函数isThree :: Nat -> Bool
isThree (Succ (Succ (Succ Zero))) = True
isThree _ = False
并且此类型的行为未在其类型(Nat -> Bool
)中指示,因此必须由该值执行该行为。实际上,我们使用模式匹配来破坏价值并强制执行行为。
答案 1 :(得分:4)
将值视为包含标记字段的框,该字段标识用于创建它的数据构造函数,以及该构造函数所需的其他参数。由于Haskell是静态类型的,因此运行时不必区分不同类型的值,因此标记只需要标识该类型中的数据构造函数。您只能通过模式匹配间接检查标记,但知道类型和标记,您可以确定数据构造函数,并可以安全地访问参数。
注意,如果类型只有一个数据构造函数,那么该类型的值不需要标记。