我正在尝试利用GADT来限制类型,但是在编译期间无法处理某些依赖项,例如用户输入。让我们考虑以下AVL树定义:
data Zero
data S a
data AVL depth where
Nil :: AVL Zero
LNode :: AVL n -> Int -> AVL (S n) -> AVL (S (S n))
RNode :: AVL (S n) -> Int -> AVL n -> AVL (S (S n))
MNode :: AVL n -> Int -> AVL n -> AVL (S n)
GADT的魔力确保每棵AVL树都得到很好的平衡。我可以定义一些基本功能,例如
singleton :: a -> AVL (S Zero) x
singleton a = MNode Nil a Nil
insert :: a -> AVL n a -> AVL (S n) a
insert = ...
现在,我想编写一个程序,该程序将读取n
个数字,将它们插入AVL树并按顺序返回(假设已定义这些函数):
main = IO ()
main = do
(n :: Int) <- readInt -- some IO defined somewhere
(inp :: [Int]) <- readInts
let avl = foldl (\tree x -> insert x tree) Nil inp
print $ toList avl
显然我得到了错误:
• Couldn't match type ‘S Zero’ with ‘Zero’
Expected type: AVL Zero
Actual type: AVL (S Zero)
因为树的类型(深度)将随着每个insert
而变化。我了解这里发生的情况,但是在处理“在线”输入时,我看不到任何使用此AVL的合理方法-这完全不知道我要插入多少个元素。
有什么解决方案可以让我为这种情况抽象出树的深度吗?即使AVL太复杂了,这个问题也适用于编译时大小的向量和矩阵。目前,我只能解决硬编码的任务,这使我的程序完全不灵活。
答案 0 :(得分:8)
您可以使用其他GADT隐藏树的深度。 (这被称为存在类型。)
data SomeAVL a where
SomeAVL :: AVL n a -> SomeAVL a
使用包装器可对SomeAVL
进行操作:
insert' :: a -> SomeAVL a -> SomeAVL a
insert' a (SomeAVL t) = SomeAVL (insert a t)