我正在通过AST转换来发展一种小型编程语言。也就是说,从VM开始,然后慢慢添加有助于程序员的层。
由于每一层都知道如何转换其新类型,所以我做了这样的事情:
module AST0 = struct
type t = [
| `Halt
| `In
| `Out
| `Add of (int * int * int)
| `Sub of (int * int * int)
]
let lower (ast : t list) = ast
end
module AST1 = struct
type t = [
AST0.t
| `Inc of int
| `Dec of int
| `Cmp of (int * int * int)
]
let lower (ast : t list) =
let lower_one = function
| `Inc a -> [`Add (a, a, `Imm 1)]
| `Dec a -> [`Sub (a, a, `Imm 1)]
| `Cmp (a, b) -> [`Sub (13, a, b)]
| (x : AST0.t) -> AST0.lower [x] (* <--- problem line *)
in
List.concat @@ List.map lower_one ast
end
不幸的是我得到了错误:
File "stackoverflow.ml", line 28, characters 8-20:
Error: This pattern matches values of type AST0.t
but a pattern was expected which matches values of type
[? `Cmp of 'a * 'b | `Dec of 'c | `Inc of 'd ]
The first variant type does not allow tag(s) `Cmp, `Dec, `Inc
我认为,由于编译器足够聪明,可以注意到我没有在任意匹配的情况下处理XY和Z变体,因此它可以告诉
x
中的AST1.lower
实际上不会是Cmp
或Inc
之一或Dec
。似乎并非如此。我是否误解了OCaml的类型系统?我是否缺少明显的东西?这是愚蠢的做法吗?
最佳答案
您不能在本地限制案例模式的类型。类型约束: AST0.t
也将其他模式的类型也强制为AST0.t
。这就是为什么您的代码不键入check的原因; `Inc
不包含在AST0.t
中。
但是,OCaml中确实有一个整洁的功能可以满足您的需求。
使用#AST0.t
模式别名,而不是类型约束。有关详细信息,请参见https://caml.inria.fr/pub/docs/manual-ocaml/lablexamples.html#sec46:
(* I fixed several other trivial typing problems *)
let lower (ast : t list) =
let lower_one = function
| `Inc a -> [`Add (a, a, 1)]
| `Dec a -> [`Sub (a, a, 1)]
| `Cmp (a, b, c) -> [`Sub (13, a, b)]
| #AST0.t as x -> AST0.lower [x] (* <--- problem line *)
in
List.concat @@ List.map lower_one ast
#AST0.t as x
不仅是(`Halt | `In | `Out | `And _ | `Sub _ as x)
的缩写,而且还将x
右侧的->
类型从[> AST1.t]
更改为[> AST0.t]
。您可以在那里将其用作AST0.t
。