OCaml GADT-模式匹配匹配错误的参数类型

时间:2018-12-08 02:45:44

标签: pattern-matching ocaml gadt

我必须使用广义代数数据类型实现完美平衡的二叉树(或简称PBT)。我已经了解了GADT的总体工作原理,以及为了使其在OCaml中工作所必须使用的(有点难看)语法。 然后,我必须实现一些与其配合使用的功能。现在,我遇到一个函数问题,该函数接受两个PBT,这些PBT在其节点中存储了整数,然后返回一个PBT,该PBT在其节点中存储了其相应节点的总和。好。听起来很容易。 这是我为PBT数据类型编写的代码:

(*Important in order to define the index of the level of our perfectly 
balanced tree*)
(*Natural numbers, Peano style (now with GADTs)*)
type zero = Zero
type _ succ = Succ : 'natural -> 'natural succ

(*Implementing the perfectly balanced binary tree - or PBTree - with GADTs*)
(*'a -> type of tree | _ -> index of level*)

type ('a, _) pbtree =
(*Base case: empty tree - level 0, rock-bottom*)
EmptyTree : ('a, zero) pbtree
(*Inductive case: regular PBTree - tree on the left, value, tree on the 
right*)          
| PBTree : ('a, 'natural) pbtree * 'a * ('a, 'natural) pbtree -> ('a, 
'natural succ) pbtree

到目前为止有效。该类型进行编译,我什至设法执行了翻转功能(返回PBT的镜像)。 现在真正的问题来自这个小问题:

let rec add : type int natural. 
(int, natural) pbtree -> (int, natural) pbtree -> (int, natural) pbtree =
function
| EmptyTree, EmptyTree -> EmptyTree
| PBTree(left1, value1, right1), PBTree(left2, value2, right2) -> 
PBTree((add left1 left2), value1 + value2, (add right1, right2))

在我对空树进行模式匹配的地方,它给了我以下错误:

| EmptyTree, EmptyTree -> EmptyTree

Error: This pattern matches values of type 'a*'b
       but a pattern was expected which matches values of type
         (int, natural) pbtree

如果我使函数采用一对树,则可以解决此问题。最终,归纳条件会稍有变化,函数将如下所示:

let rec add : type int natural. (int, natural) pbtree * (int, natural)         
pbtree -> (int, natural) pbtree =
function
| EmptyTree, EmptyTree -> EmptyTree
| PBTree(left1, value1, right1), PBTree(left2, value2, right2) -> 
PBTree((add (left1, left2)), value1 + value2, (add (right1, right2)))

但是,这不是我应该做的。我的函数需要接受两个参数。即使使用此修改解决了结对类型难题,它也会给出以下错误:

Error: This pattern matches values of type int/1805
        but an expression was expected of type int/1

因此,我采取的第一种方法是我能做的最好的方法。 我的问题是:为什么模式匹配在明显不存在时会检测到一对?为什么不能像通常那样检测两个参数?是我编写用于检测该模式的函数的方式吗?

假设我在创建函数时使用了第二种方法(将一对PBT作为参数而不是两个参数作为参数-两个PBT),那么为什么它不能识别我尝试的整数值给它计算?可能是因为它太适合使用了吗?

我不明白这里出了什么问题,如果我尝试修改类型以缓解这种语法限制,那么我冒着创建违反其定义的PBT的风险,这仅仅是因为类型允许分支大小不相等。

再次,我问:为什么在我提出的案例中,模式匹配无法识别出我提供的正确值类型?

1 个答案:

答案 0 :(得分:1)

您遇到的最后一个错误:

Error: This pattern matches values of type int/1805
       but an expression was expected of type int/1

表示存在两种不兼容的int类型,它们提供了它们的内部ID,因此您可以区分它们。发生此错误的原因是type int natural.创建了两个作用于该函数的新的本地抽象类型,它们将对该范围内具有这些名称的任何其他类型进行阴影处理。解决此错误的方法很简单,就是不创建名为int的本地抽象类型。然后,它将按预期将类型变量约束为实际的int类型。

另一个错误:

Error: This pattern matches values of type 'a*'b
       but a pattern was expected which matches values of type
         (int, natural) pbtree
出现

是因为EmptyTree, EmptyTree实际上 是与一对匹配的模式。在OCaml中,如果以其他方式分隔元组,则不需要括号。因此,例如,这是类型(int * int) list[1, 2; 3, 4; 5, 6]的有效(且非常令人困惑)文字。

function也仅限于一个参数,因此要匹配多个参数,您需要使用fun a b -> match a, b with...。或者,您可以只add直接取一个元组。

最后,您将得到一个错误,因为您使用逗号来分隔add right1, right2中的函数参数,该参数将再次被解释为元组。应该是add right1 right2

然后,最终工作(或至少编译)的add函数是:

let rec add : type natural. 
(int, natural) pbtree -> (int, natural) pbtree -> (int, natural) pbtree =
fun a b -> match a, b with
| EmptyTree, EmptyTree -> EmptyTree
| PBTree(left1, value1, right1), PBTree(left2, value2, right2) -> 
PBTree((add left1 left2), value1 + value2, (add right1 right2))
相关问题