使用 "toList" 函数创建自定义列表数据结构

Create custom list data structure with "toList" function

我想创建自己的名为节点的列表数据结构。然后我将使用我的 ListConverter class,它包含 'toList' 函数,并创建它的一个实例。

data Nodes a = Empty
             | Node a (Nodes a)

class ListConverter a where
    toList :: a -> [Integer]

instance (Integral a) => ListConverter (Nodes a) where
    toList Empty = []
    toList (Node x Empty) = [x]
    toList (Node x y) = x : toList y

GHCi 告诉我,预期的类型是 'Integer' 但目前是 'a'。我很困惑,因为在这个例子中我给出了一个类型(整数)。这是错误消息:

error:
    * Couldn't match expected type `Integer' with actual type `a'
      `a' is a rigid type variable bound by
        the instance declaration
        at main.hs:7:10-48
    * In the expression: x
      In the expression: [x]
      In an equation for `toList': toList (Node x Empty) = [x]
    * Relevant bindings include
        x :: a (bound at main.hs:9:18)
        toList :: Nodes a -> [Integer] (bound at main.hs:8:5)
  |
9 |     toList (Node x Empty) = [x]
  |                              ^

您的 ListConverter 实例应接受任何 class Integral for "a" 的值,但 Integer 是特定类型,而不是 class;你必须这样做:

instance ListConverter (Nodes Integer) where

或者相反,使您的 ListConverter class 能够生成您的节点值包含的任何类型的列表:

class ListConverter f where
    toList :: f a -> [a]

instance ListConverter Nodes where
    toList Empty = []
    toList (Node x y) = x : toList y

(toList 的第二个等式 - (Node x Empty) - 是不必要的)

这个实例的问题很简单。您已提供签名:

toList :: a -> [Integer]

但您尝试的实例实际上具有类型 Nodes a -> [a]。这不起作用,除非 aInteger 类型 - 但您声称它适用于所有 Integral a。这包括其他类型,例如 Int.

一个解决方案就是限制您的实例:

instance ListConverter (Nodes Integer) where...

这会起作用 - 但在我看来并没有真正尊重您可能希望 class 实现的精神。

我认为最好的解决方案是认识到列表和您的 Nodes 类型都是由另一种类型参数化的,并定义 class 以便在通用类型上进行转换基地class。这听起来比实际更复杂,我的意思是:

class ListConverter l where
    toList :: l a -> [a]

然后你可以写一个instance ListConverter Nodes where...,然后只复制你现有的toList定义。 (我顺便指出,中间那条线是多余的。)