Haskell中通过多态的递归代数数据类型

时间:2014-06-23 08:44:49

标签: haskell algebraic-data-types parametric-polymorphism

考虑到通用多态的功能,我试图理解递归代数数据类型的定义,解和编码。作为一个例子,我试图通过

实现递归类型的二叉树
data BTAlg x = Empty | Leaf x x
type BT = forall z. ((BTAlg z) -> z) -> z

直觉是二叉树的类型应该是所有类型T中的初始值,它们配备有常量e: T和二进制操作m: T -> T -> T,即"初始模块& #34;在仿函数BTAlg上。换句话说,BT元素是为任何此类模块T分配T元素的一种方式。

BT本身的模块结构可以通过

定义
initial_module :: (BTAlg BT) -> BT
initial_module = \s -> case s of
  Empty -> (\f -> (f Empty))
  Leaf x y -> (\f -> (f (Leaf (x f) (y f))))

作为迈向BT的模式匹配的一步,我现在想要将一个元素x:BT应用于类型BT本身,我认为它是某种编码解码x

decode_encode :: BT -> BT
decode_encode x = x initial_module

但是,此代码会导致我无法处理的类型错误:

Couldn't match expected type `(BTAlg z -> z) -> z'
            with actual type `BT'
Expected type: BTAlg ((BTAlg z -> z) -> z) -> (BTAlg z -> z) -> z
  Actual type: BTAlg BT -> (BTAlg z0 -> z0) -> z0
In the first argument of `x', namely `initial_module'
In the expression: x initial_module

这里有什么问题?我不知道为什么类型检查器无法识别通用类型参数z必须专门用于BT x才能使x成为initial_module适用于(x :: ((BTAlg BT) -> BT) -> BT) initial_module;撰写decode_encode也不会有任何帮助。

关于定义BT的动机的附录: 我想说服自己data BTStd = StdEmpty | StdLeaf BTStd BTStd 实际上是'同构&#39 ;标准的实现

BT -> BTStd

二叉树;虽然我不知道如何在Haskell内部进行精确处理,但首先要在两个实现之间来回构建地图BTStd -> BTtoStandard: BT -> BTStd

BT的定义是将BTAlg的通用属性应用于BTStd上的规范std_module :: (BTAlg BTStd) -> BTStd std_module s = case s of Empty -> StdEmpty Leaf x y -> StdLeaf x y toStandard: BT -> BTStd toStandard x = x std_module 模块结构:

fromStandard: BTStd -> BT

对于解码函数fromStandard :: BTStd -> BT fromStandard s = case s of StdEmpty -> initial_module Empty StdLeaf x y -> initial_module (Leaf (fromStandard x) (fromStandard y)) ,我想执行以下操作:

decode_encode

但是,这会产生与上面Couldn't match expected type `BT' with actual type `(BTAlg z0 -> z0) -> z0' In the return type of a call of `fromStandard' Probable cause: `fromStandard' is applied to too few arguments In the first argument of `Leaf', namely `(fromStandard x)' In the first argument of `initial_module', namely `(Leaf (fromStandard x) (fromStandard y))' 的直接定义相同的打字问题:

{{1}}

谢谢!

1 个答案:

答案 0 :(得分:4)

如果查看decode_encode

的推断类型
:t decode_encode
> decode_encode :: ((BTAlg BT -> (BTAlg z -> z) -> z) -> t) -> t
很明显,GHC已经失去了相当多的多态性。我不完全确定这里的细节 - 这段代码需要ImpredicativeTypes来编译,这通常是我理解开始崩溃的地方。但是,有一种保持多态性的标准方法:使用newtype

newtype BT = BT { runBT :: forall z. (BTAlg z -> z) -> z }

newtype建立了由BT ~ forall z . (BTAlg z -> z) -> zBT见证的同构runBT。只要我们把这些证人放在正确的位置,我们就可以继续前进。

data    BTAlg x = Empty    | Leaf    x     x
data    BTStd   = StdEmpty | StdLeaf BTStd BTStd
newtype BT      = BT { runBT :: forall z. ((BTAlg z) -> z) -> z }

initial_module :: BTAlg BT -> BT
initial_module s = case s of
  Empty -> BT $ \f -> (f Empty)
  Leaf x y -> BT $ \f -> (f (Leaf (runBT x f) (runBT y f)))

std_module :: (BTAlg BTStd) -> BTStd
std_module s = case s of 
  Empty -> StdEmpty
  Leaf x y -> StdLeaf x y

toStandard :: BT -> BTStd
toStandard x = runBT x std_module

fromStandard :: BTStd -> BT
fromStandard s = case s of
  StdEmpty -> initial_module Empty
  StdLeaf x y -> initial_module (Leaf (fromStandard x) (fromStandard y))

特别值得注意的是,我们使用runBT来控制BT中类型lambda的应用时间和次数

decode_encode :: BT -> BT
decode_encode x = runBT x initial_module

runBT的一种用法对应于量化类型的一种统一。