弗雷格:我可以为递归类型推导出“显示”吗?

时间:2013-10-04 08:27:03

标签: frege

我正在尝试在frege中实现经典的树结构,只要我不使用“derive”就可以很好地工作:

data Tree a = Node a (Tree a) (Tree a)
            | Empty
derive Show Tree

给了我

realworld/chapter3/E_Recursive_Types.fr:7: kind error, 
type constructor `Tree` has kind *->*, expected was *

这是不支持还是我必须以不同方式声明它?

1 个答案:

答案 0 :(得分:4)

欢迎来到各种类型的世界!

您必须提供要显示的完整类型的项目。 Tree不是类型(种类*),而是需要类型参数成为一种类型(种类* -> *)。

尝试

derive Show (Tree a)

请注意,这是

的简写
derive Show (Show a => Tree a)

类似于以下事实:要显示树,您还需要知道如何在树中显示值(至少,derive生成的代码需要知道这一点 - 当然,可以编写一个实例手动只打印树的形状,因此不需要它。)

通常,每个类型类的实例所需的类型是固定的。该错误消息告诉您*需要Show


编辑:消除另一种可能的误解

请注意,这与您的类型递归无关。例如,让我们看一下可选值的定义:

data Maybe a = Nothing | Just a 

这种类型不是递归的,但我们仍然不能说:

derive Show Maybe   -- same kind error as above!!

但是,给定以下类型类:

class ListSource c    -- things we can make a list from
    toList :: c a -> [a]

我们需要说:

instance ListSource Maybe where
    toList (Just x) = [x]
    toList Nothing  = []

instancederive在本讨论中是等价的,两个都是实例,区别在于derive自动为某些类型类生成实例函数。)

不可否认,在一种情况下,为什么这种方式不同,另一种情况则不同。关键是,在每种情况下我们想要使用的类操作的类型。例如,在课程Show中,我们有:

class Show s where
    show :: s -> String

现在,我们看到所谓的类类型变量s(表示任何未来的实例化类型表达式)在函数数组的左侧单独出现。当然,这表明s必须是普通类型(种类*),因为我们将值传递给show,并且每个值都有一种类型{{1 }}。我们可以包含*IntMaybe Int类型的值,但没有任何值具有Tree StringMaybe类型。

另一方面,在Tree的定义中,类类型变量ListSource应用于c类型的其他类型变量a,其中也显示为列表元素类型。从后者我们可以得出结论,toList具有种类a(因为列表元素是值)。我们知道,函数箭头左侧和右侧的类型也必须具有类型*,因为函数接受并返回值。因此,*c a种。因此,*单独应用于某种类型c时会产生类型*。这是*

这意味着,用简单的英语,当我们想为* -> *创建一个实例时,我们需要一些用另一种类型参数化的“容器”类型的类型构造函数。这里可以ListSourceTree,但不能Maybe