pattern-matching - 使用 GADT 建模语法,但类型参数无法统一

标签 pattern-matching ocaml gadt polymorphic-variants

我认为我已经找到了一种使用 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 类型参数不能有差异。这是否意味着这种方法完全没有用?

最佳答案

您只需要使用本地抽象类型就可以细化每个分支的类型:

let rec to_list : type a. a t -> string list = function
  | Open rest -> "(" :: to_list rest
  | Minus rest -> "-" :: to_list rest
  | Char rest -> "a" :: to_list rest
  | Close -> [")"]

为了解释,@octachron 写了 a great answer今天早些时候针对类似问题对此进行了解答。

编辑:

要将此函数限制为案例的子集,我认为您必须应用两种不同的类型签名。一种用于多态变体的外部签名,一种用于局部抽象类型的内部签名。然而,由于该函数是递归的,因此当我们对其进行递归时,我们还需要局部抽象类型是外部的。否则我们可以只注释模式匹配的值。

我可以想出几种方法来做到这一点:

选项 1,将其包装在模块中(或将外部签名放入 .mli 中):

module M : sig
  val to_list : [<`Minus | `Char | `Close] t -> string list
end = struct
  let rec to_list : type a. a t -> string list = function
    | Open rest -> "(" :: to_list rest
    | Minus rest -> "-" :: to_list rest
    | Char rest -> "a" :: to_list rest
    | Close -> [")"]
end

选项 2,使用本地定义的函数来放置“内部”签名:

let to_list : [<`Minus | `Char | `Close] t -> string list =
  let rec aux : type a. a t -> string list = function
    | Open rest -> "(" :: aux rest
    | Minus rest -> "-" :: aux rest
    | Char rest -> "a" :: aux rest
    | Close -> [")"]
  in
  aux

关于pattern-matching - 使用 GADT 建模语法,但类型参数无法统一,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58985116/

相关文章:

javascript 或 jquery 获取图像源的方法(如果......匹配特定模式)

makefile - 如何将makefile目标中的 '$*'值大写?

printing - Ocaml:打印出 int 列表数组中的元素

haskell - Haskell 中 GADT 的枚举

haskell - 为 GADT 中的不可访问代码启用 "-fno-warn-"

java - 改进java模式匹配函数以不同的顺序打印,可能的链表操作

scala - 如何使用模式匹配检查列表是否包含全部 Some 或 None 或两者?

ocaml - OCaml 中的用户定义打印机

ocaml - 使用模块无限循环 OCaml 类型检查器如何工作?

haskell - 如何在镜头样式单板库中为更高种类的类型实现孔和上下文?