Haskell派生显示实例

时间:2013-08-29 18:48:00

标签: haskell show deriving

我正在玩红黑树:

-- Taken from Okasaki 1999
module RedBlackTree where

--node coloring data
--a node is R (red) or B (black)
data Color = R | B

--tree constructor
--a RBT can be E (empty) or be T (a non empty tree)
data RBT e = E | T Color (RBT e) e (RBT e)

--set operations on tree
type Set a = RBT a

--define an empty set
empty :: Set e
empty = E

--define a member of a set
--Sees if an item of type e is
--in a set if type e elements
member :: (Ord e) => e -> Set e -> Bool
member x E = False
member x (T _ a y b) | x <  y = member x a -- if less, go left
                     | x == y = True
                     | x >  y = member x b -- if more, go right


--tree operations
--Insert an element
insert :: (Ord e) => e -> Set e -> Set e
insert x s = makeBlack (ins s)
    where ins E = T R E x E --basically the typical BST insert
          ins (T color a y b) | x <  y = balance color (ins a) y b
                              | x == y = T color a y b
                              | x >  y = balance color a y (ins b)
          makeBlack (T _ a y b) = T B a y b --inserts a black node

-- balance operations
--case 1:
balance B (T R (T R a x b) y c) z d = T R (T B a x b) y (T B c z d)
--case 2:
balance B (T R a x (T R b y c)) z d = T R (T B a x b) y (T B c z d)
--case 3:
balance B a x (T R (T R b y c) z d) = T R (T B a x b) y (T B c z d)
--case 4:
balance B a x (T R b y (T R c z d)) = T R (T B a x b) y (T B c z d)
--generic balancing operation
balance color a x b = T color a x b

如果我在GHCi中执行以下语句:

> RedBlackTree.insert ('b') (RedBlackTree.T R E ('a') E)

以下错误消息告诉我,Set Char没有show的实例:

<interactive>:116:1:
No instance for (Show (Set Char)) arising from a use of `print'
Possible fix: add an instance declaration for (Show (Set Char))
In a stmt of an interactive GHCi command: print it

我知道树正在工作,因为通过调用member 'b' ... ...是先前执行的语句,返回的值是True。我一直在阅读关于这个问题的其他SO帖子,但找到的解决方案(例如:Haskell: Deriving Show for custom type)不起作用。

例如,添加:

instance Show Set where:
    show (Set Char) = show Char

当我尝试使用:l加载时收到以下错误消息:

  

:l red-black-tree.hs       [1 of 1]编译RedBlackTree(red-black-tree.hs,解释)

red-black-tree.hs:54:11: Not in scope: data constructor `Set'

red-black-tree.hs:54:15: Not in scope: data constructor `Char'

red-black-tree.hs:54:28: Not in scope: data constructor `Char'
Failed, modules loaded: none.

我认为我正在尝试做的事情有一些问题,但我似乎无法从可用的文档中找到答案。

3 个答案:

答案 0 :(得分:5)

要将值转换为字符串,Haskell使用所谓的类型类。简化后,类型类只提供根据其参数类型而行为不同的函数。这种方法与面向对象编程语言中已知的方法重载非常相似。类类Show提供了一个名为show的函数,它将某种类型的值转换为字符串。例如,表达式show 1生成字符串"1"。如果有一个函数show将某种类型的值转换为字符串,我们就说这种类型有一个类型为Show的实例。换句话说,对于整数,存在类型类Show的实例。

当您在ghci中计算表达式时,也会使用此show函数。因此,它告诉您没有实例(Show (Set Char)),换句话说,它不知道如何将类型Set Char的值转换为字符串。对于类型SetRBTColor等自定义类型,您必须提供类型类Show的实例,以便在控制台上显示这些类型的值。要为类型Show定义类型类Color的实例,可以使用以下定义。

instance Show Color where:
  show R = "Red"
  show B = "Black"

也就是说,如果您将R写入ghci,它将打印Red。对于简单的Haskell数据类型,存在Show类型类的规范定义。例如,Show的{​​{1}}的规范定义如下。

Color

为了减轻用户定义这样的实例,Haskell提供了一种所谓的“派生机制”。也就是说,如果你写

instance Show Color where:
  show R = "R"
  show B = "B"

在定义数据类型之后,编译器将为您的数据类型生成 deriving (Show) 类型类的规范实例。

如果数据类型使用其他数据类型,则派生前者的Show实例将需要后者的Show实例。例如,请考虑以下数据类型。

Show

数据类型data RBT e = E | T Color (RBT e) e (RBT e) 在其定义中使用类型RBTColor构造函数的规范Show实例将以“T”开头,然后显示类型T的值。因此,为Color导出Show实例需要RBT Show的实例。

答案 1 :(得分:4)

您的实例代码已损坏:

instance Show Set where
    show (Set Char) = show Char
  1. 编译器抱怨Set不是数据构造函数,而不是 - 它是类型的同义词名称。所以你在模式中错误地使用了Set。您可以在那里使用数据构造函数 - RBT数据构造函数为TE

  2. 你的实例声明是错误的:SetRBT的同义词,RBT有一个类型参数,也就是说它是一个从一个类到另一个类型的函数, * -> *的亲切签名。但是Show实例需要一个没有参数的类型,这是一个类型,而不是一个类型构造函数,类型*而不是您提供的* -> *。因此,您应该通过提供instance Show (RBT Char)instance (Show a) => RBT a

  3. 来解决这个问题

    你可能想要的是写“通过在其中显示字符来显示一组字符”。

    所以要修复你的实例:

    instance Show (RBT Char) where
        show a = "something"
    

    但它没有显示任何有用的东西。你需要在RBT的构造函数上进行模式匹配才能完成工作:

    instance Show (RBT Char) where
        show E = "something"
        show (T a b c d) = "something else"
    

    但是对于您的任务,仅使用ShowRBT a的派生Color实例会更简单。

答案 2 :(得分:1)

您没有使用任何花哨的扩展程序,因此您应该能够使用deriving的内置Show机制。

为了使其自动派生数据类型的Show实例,类型定义中使用的所有类型也必须具有Show个实例。只需将deriving (Show)添加到data定义的所有的末尾即可。您可能希望养成将deriving (Eq, Show)添加到所有data类型的习惯,因为您几乎总是需要结构相等性测试和类型的可显示性。

此外,您不能为类型别名创建任何类的类实例,仅适用于类型。关键字type定义了类型别名,而不是新类型。如果您为Show类型设置RBT a个实例,它会自动用于您声明为Set a的任何内容,因为Set a只是{{1}的别名}}