3

考虑到通用多态性的功能,我试图理解递归代数数据类型的定义、去和编码。例如,我尝试通过实现递归类型的二叉树

data BTAlg x = Empty | Leaf x x
type BT = forall z. ((BTAlg z) -> z) -> z

直觉是二叉树的类型应该在所有T配备常量e: T和二元运算的类型中是初始的m: T -> T -> T,即函子上的“初始模块” BTAlg。换句话说,元素 ofBT是为任何此类模块分配 的元素的一种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于;写作也无济于事。xxinitial_module(x :: ((BTAlg BT) -> BT) -> BT) initial_module

关于定义动机的附录decode_encode我想说服自己BT实际上与标准实现“同构”

data BTStd = StdEmpty | StdLeaf BTStd BTStd

二叉树;虽然我不知道如何在 Haskell 中做到这一点,但首先要构建映射BT -> BTStdBTStd -> BT在两种实现之间来回切换。

的定义是对规范模块结构toStandard: BT -> BTStd的通用属性的应用:BTBTAlgBTStd

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))'

谢谢!

4

1 回答 1

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) -> z见证的同构。只要我们把这些证人放在正确的位置,我们就可以继续前进。BTrunBT

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对应一种量化类型的统一。

于 2014-06-23T12:21:45.260 回答