我认为我找到了一种使用 GADT 对语法进行建模的巧妙方法,方法是给每个构造函数(例如Char
)一个返回类型参数,该参数是一个多态变体[`Char] t
(例如[<`Minus | `Char] t
)。
(* Grammar ::= Open (Minus? Char)+ Close *)
type _ t =
| Open: [<`Minus | `Char] t -> [`Open] t
| Minus: [<`Char] t -> [`Minus] t
| Char: [<`Minus | `Char | `Close] t -> [`Char] t
| Close: [`Close] t
这按预期工作,只允许符合语法的序列:
let example =
Open (Minus (Char (Char (Minus (Char (Close))))))
(* let counter_example =
Open (Minus (Char (Minus (Minus (Char (Close)))))) *)
但是,似乎不可能为这种类型编写任何微不足道的函数:
let rec to_list = function
| Open rest -> "(" :: to_list rest
| Minus rest -> "-" :: to_list rest
| Char rest -> "a" :: to_list rest
| Close -> [")"]
File "./tmp.ml", line 21, characters 4-14:
21 | | Minus rest -> "-" :: to_list rest
^^^^^^^^^^
Error: This pattern matches values of type [ `Minus ] t
but a pattern was expected which matches values of type [ `Open ] t
Type [ `Minus ] is not compatible with type [ `Open ]
据我了解,多态变体并不统一,因为 GADT 类型参数不能有差异。这是否意味着这种方法完全没用?