pattern-matching - OCaml GADTs - pattern matching matches wrong type of argument
问题描述
I have to implement a perfectly balanced binary tree (or PBT for short) using generalized algebraic data types. I have understood how GADTs work in general and the (somewhat ugly) syntax that I have to use in order for it to work in OCaml. Then I have to implement some functions that work with it. Right now, I am having a problem with a function that takes two PBTs that have integers stored in their nodes and returns a PBT which stores in its nodes the sum of each of its corresponding nodes. Ok. Sounds easy enough. This is the code that I have written for the PBT data type:
(*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
It worked so far. The type compiles and I even managed to do a flip function (return the mirror image of a PBT). Now the real issue comes from this little brat:
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))
Right where I do the pattern matching for the empty tree, it gives me the following error:
| 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
I can solve this if I make the function take a pair of trees. It would end up with the inductive case slightly changed and the function would look like this:
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)))
However, this is not what I am supposed to do. My function needs to take two arguments. Even with this modification that solves the pair type conundrum, it will give the following error:
Error: This pattern matches values of type int/1805
but an expression was expected of type int/1
So the first approach I took is the best I can do. My question is: why the pattern matching detects a pair when it is clearly not there? Why can it not detect two arguments like it normally should? Is it the way I wrote the function to detect that pattern?
If I, hypothetically, used the second approach in creating the function (the one where it took a pair of PBTs as an argument instead of two arguments - two PBTs), then why does it not recognise the integer value I am trying to give it to calculate? Is it maybe because it is too instrumented to work with?
I cannot understand what went wrong here and if I tried to modify the type to ease up on this syntactic restriction, then I risk creating a PBT that violates its own definition simply because of the type allowing branches which are unequal in size.
So again, I ask: why does pattern-matching fail to recognise the proper value types that I am giving it in the cases I presented?
解决方案
你得到的最后一个错误:
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))
推荐阅读
- scala - 请求成功时获取“HTTP方法不允许,支持的方法:GET”
- oracle - oracle apex 开发者工具栏说明
- python - 在 python 中转换 Postman/newman 收集请求
- python - 当 Tensorflow 2.2 加载具有 NumericColumn 的 2.1 模型时,会抛出“KeyError: 'partitioner'”
- c - 即使我的条件得到验证,为什么不返回 true
- r - rasterFromXYZ 警告:数据长度不是行数的约数或倍数
- javascript - 如何在全日历中查看基于月份的数据
- angular - Ionic 3 - 带有标题的 Angular Post 不起作用
- javascript - 如何使用 jQuery 对一组元素的值求和?
- symfony - 无法使用 Loader 或 Fileloader 从捆绑包中加载 routes.yaml 配置