关键是想出正确的类型。
如果你想要类似的东西[1, [2, [3, [4]]]]
,那么这样做是行不通的,因为所有列表元素都必须是相同的类型。
这是有道理的,因为当我从列表中抓取一个元素时,我需要知道它是什么类型,然后才能对它做任何事情(这是类型的全部意义,它们告诉你你能做什么和能做什么'不做某事)。
但是由于 Haskell 的类型系统是静态的,我需要知道它是什么类型,即使不知道它是列表的哪个元素,因为在程序运行之前我可能不知道我正在抓取的列表索引。所以无论我使用什么索引,我几乎都必须得到相同类型的东西。
但是,可以做一些非常像您想要的事情:您想要一个可能是整数或可能是列表的数据类型:
type IntegerOrList a = Either Integer [a]
如果您不熟悉Either
类型,则值Either l r
可以是Left x
somex :: l
或Right y
some y :: r
。IntegerOrList a
其值为整数或某物列表的类型也是如此。所以我们可以列出这些东西:以下是 type 的值[IntegerOrList Bool]
:
[Left 7, Left 4, Right [True, False], Left 8, Right [], Right [False]]
好的,这是列表中的一级列表,但是我们还不能将列表放入列表中的列表中——内部列表包含Bool
s,它不能是列表。如果我们改为使用[IntegerOrList (IntegerOrList Bool)]
,我们将能够在列表中的列表中包含列表,但我们仍然无法获得进一步的结果。在我们的示例中,我们有一个列表,其中包含整数或列表的值,列表是包含整数或列表的值的列表,并且......我们真正想要的是类似的东西IntegerOrList (IntegerOrList (IntegerOrList ...
,或者更简单地说,像:
type IntegerOrLists = Either Integer [IntegerOrLists]
但这是不允许的——类型同义词不能是递归的,因为这会产生无限大的类型,这会让糟糕的编译器感到困惑。但是,正确的数据类型可以是递归的:
data IntegerOrLists = I Integer | L [IntegerOrLists]
现在您可以构建这样的列表,混合整数和您类型的列表:
L [I 1, L [I 2, L [I 3, L [I 4]]]]
关键是每个项目是整数还是列表都必须使用I
orL
构造函数来标记。现在列表的每个元素都是 type IntegerOrLists
,我们可以通过查看构造函数来区分它是什么。所以类型检查器终于高兴了。