在结构上强制执行无红色 Children 红色节点

Structurally Enforcing No Red Children Of Red Node

在研究 Learn You A Haskell For Great Good and Purely Functional Data Structures, I thought to try to reimplement a Red Black tree 的同时尝试在结构上强制执行另一个树不变量。

解释 Okasaki 的代码,他的节点看起来像这样:

import Data.Maybe

data Color = Red | Black

data Node a = Node {
    value :: a,
    color :: Color,
    leftChild :: Maybe (Node a),
    rightChild :: Maybe (Node a)}

红黑树的一个属性是a red node cannot have a direct-child red node,所以我尝试将其编码如下:

import Data.Either

data BlackNode a = BlackNode {
    value :: a,
    leftChild :: Maybe (Either (BlackNode a) (RedNode a)),
    rightChild :: Maybe (Either (BlackNode a) (RedNode a))}
data RedNode a = RedNode {
    value :: a,
    leftChild :: Maybe (BlackNode a),
    rightChild :: Maybe (BlackNode a)}

这会输出错误:

Multiple declarations of `rightChild'
Declared at: :4:5
             :8:5


Multiple declarations of `leftChild'
Declared at: :3:5
             :7:5


Multiple declarations of `value'
Declared at: :2:5
             :6:5

我试过对之前的代码进行多次修改,但都编译失败。正确的做法是什么?

不同的记录类型必须有不同的字段名称。例如,这是不允许的:

data A = A { field :: Int }
data B = B { field :: Char }

虽然这没问题:

data A = A { aField :: Int }
data B = B { bField :: Char }

前者会尝试定义两个投影

field :: A -> Int
field :: B -> Char

但是,唉,我们不能有两种类型的名称。 (至少,没那么容易……) 这个问题在 OOP 语言中不存在,在 OOP 语言中,字段名称永远不能单独使用,但它们必须立即应用于某个对象,如 object.field ——这是明确的,前提是我们已经知道类型object。 Haskell 允许独立投影,使这里的事情变得更加复杂。

后一种方法反而定义了

aField :: A -> Int
bField :: B -> Char

并避免了这个问题。

正如@dfeuer 上面的评论,GHC 8.0 可能会放宽此限制。