2017-07-26 51 views
4

我正在通过AST变换增长一种小型编程语言。也就是说,从虚拟机开始,慢慢添加一些帮助程序员的层。模式匹配中的OCaml多态变体

由于每一层都知道如何将其新的类型,我做了这样的事情:

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变体,它可以说明xAST1.lower将永远不会是CmpIncDec之一。这似乎并非如此。

我误解了OCaml的类型系统吗?我错过了明显的东西吗?这是一种愚蠢的做法吗?

回答

6

您不能在本地限制案例模式的类型。类型约束: AST0.t也强制其他模式的类型为AST0.t。这就是为什么你的代码不输入检查; `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

+0

所以这似乎已经修复了我的一个层,但没有另一个。我现在正在考虑... – tekknolagi

+0

正如我预料的那样,另一层因为我做了一些愚蠢的事情而被打破了。感谢您的帮助! – tekknolagi