List Haskell:递归地嵌套任意深度的进程列表

List Haskell:递归地嵌套任意深度的进程列表,list,haskell,recursion,types,List,Haskell,Recursion,Types,我正在学习Haskell,希望编写递归处理嵌套任意深度的列表的函数 例如,我想编写recurReverse,在基本情况下,它的行为就像内置的reverse,但是当传递一个嵌套列表时,也会递归地反转子列表的所有元素: recurReverse [1,2] >> [2,1] recurReverse [[1],[2],[3,4]] >> [[4,3],[2],[1]] recurReverse [[[1,2]]] >> [[[2,1]]] 目前我有基本的反向向下

我正在学习Haskell,希望编写递归处理嵌套任意深度的列表的函数

例如,我想编写
recurReverse
,在基本情况下,它的行为就像内置的
reverse
,但是当传递一个嵌套列表时,
也会递归地反转子列表的所有元素:

recurReverse [1,2]
>> [2,1]
recurReverse [[1],[2],[3,4]]
>> [[4,3],[2],[1]]
recurReverse [[[1,2]]]
>> [[[2,1]]]
目前我有基本的<代码>反向<代码>向下:

rev [] = []
rev (h:t) = rev t ++ [h]
但我需要的不止这些——如果head
h
也是一个列表(与LISP意义上的atom相反),我希望能够
反转
h
,并返回类似
revt++[revh]
的内容。当我尝试这样做时,我得到一个编译器错误,说了一些类似于我不能
revh
的话,因为
rev
[t]->[t]
类型,但我试图在
t
类型上调用它,这是有意义的。我怎样才能避开这件事

与LISP意义上的原子相反

哈斯凯尔没有这样的事。任何你事先不知道的类型(如果你对类型进行递归,你就不能知道)都可能是一个列表本身。没有原子性的概念,也没有“非列表存在”的概念,您可以将其用作此递归的基本情况

也就是说,除非你明确区分。这可以在Haskell中通过GADT很好地完成:

data Nest t where
   Egg :: t -> Nest t
   Nest :: [Nest t] -> Nest [t]

nestReverse :: Nest t -> Nest t
nestReverse (Egg x) = Egg x
nestReverse (Nest xs) = Nest . reverse $ map nestReverse xs
如果你不喜欢这个。。。嗯,还有另一种方法,但它被认为是丑陋的/非哈斯卡尔式的

class Atomeous l where
  recurReverse :: l -> l

instance {-# OVERLAPPABLE #-} Atomeous l where
  recurReverse = id
instance Atomeous l => Atomeous [l] where
  recurReverse = reverse . map recurReverse

现在,
recurReverse
有了您想要的行为。第一种是“原子”类型;因为它被标记了,所以编译器只有在找不到“更好的”实例时才会使用这个实例——这正是针对列表的;这些元素可以对所有元素进行递归调用。

归根结底,您需要为列表编写一个类型,在类型级别对嵌套深度进行编码,这样您就可以在嵌套深度上而不是在列表上编写递归函数。可能最简单的方法是使用类型族。谢谢!所以,如果我想绕过Haskell的类型系统并使用“动态”递归数据结构(例如树),那么可以安全地说多态性几乎总是需要的吗?一点也不!多态性对于编写泛型代码来说非常有用,但您可以单独将递归数据结构编写为单态ADT。请注意,Haskell中的树与嵌套列表完全不同。列表特别具有扁平/同质结构,这与Lisp中的列表非常不同。