具有约束的内联记录构造函数的存在类型

时间:2018-04-23 14:02:57

标签: polymorphism ocaml type-systems gadt existential-type

我试图在OCaml中表示一组语法的产生,而存在类型对于语法规则的语义动作的建模非常有用。我一直在研究Menhir源代码,存在类型也用于模拟语义动作。请考虑以下事项:

type 'a 'b production = { name: string; rule: 'a expr; action: 'a -> 'b }

作品具有名称,返回'a的规则以及接收'a并返回'b的操作。简单参数类型的问题在于,生产列表根本不具有多态性,因此我可以在同一列表中执行string_of_float和另一个string_of_int的操作;这是一个可以通过显式解决的常见问题,例如,操作可能是:

action: 'a 'b. 'a -> 'b

但我还需要根据expr的参数设置约束,'a的{​​{1}}与action的{​​{1}}统一,因此,只要我们可以在代数数据类型中使用内联记录构造函数,我们就应该能够将它们放在广义代数数据类型中,对吗?

'a

但这不是一个有效的表格。我现在找到的解决方案是使规则也具有操作类型,例如:

expr

但是这种单态仍然是非常严格的。如何在具有共享约束的记录内对存在类型进行建模,因此我可以在不同的产品中使用不同的返回类型,其中仍然可以进行应用程序的编译时检查(通过详尽的模式匹配)?

使用相同的逻辑,如果我想从type production = (* Ɐ a. b. *) | Production: { name: string; rule: 'a expr; action: 'a -> 'b } -> production 获得一个函数列表,我是否需要使用存在类型构造函数来执行此操作(如在type 'a expr = | Terminal of string * ('a -> 'a) | Sequence of 'a expr list * ('a list -> 'a) 中)?

我相信我需要某种类型的限制(它们都是'a 'b. -> 'a -> 'b),但我来自Haskell,但仍然没有弄清楚如何在OCaml中做到这一点。

2 个答案:

答案 0 :(得分:4)

我不确定你想要什么。假设有一个函数'a expr -> 'a,如果你想要一个具有不同内部表达式的生产列表,你可以将生产定义为

type 'r production = 
 | P: { name: string; rule: 'a expr; action: 'a -> 'r } -> 'r production

然后您可以获得[P string_of_float; P string_of_int]的列表,同时仍然知道操作的结果类型。 (此外,您之前的定义是有效的。)

其他普遍量化让我觉得有问题:类型∀'a∀'b. 'a -> 'b的唯一函数是

的变体
let fail _ = assert false

类似地,存在性量化返回类型的操作意味着您永远无法恢复有关此返回类型的信息,换句话说,它只有类型的操作一样有用 'a -> unit

答案 1 :(得分:1)

生产规则的语义操作应该有一些效果,否则根本不需要操作。您可以使用某种状态monad或直接在OCaml中对此效果进行编码,即作为可变引用或直接I / O。

在你的情况下,存在主义

type production = (* Ɐ a. b. *)
  | Production: { name: string; rule: 'a expr; action: 'a -> 'b } -> production

具有生成类型'b的值的效果。这与存在主义的定义直接相矛盾,因为在这种情况下b需要逃避上下文。所以正确的定义是

type production = (* Ɐ a. *)
  | Production: { name: string; rule: 'a expr; action: 'a -> unit }

或者,如果你愿意使用一些monad,那么你需要将monad构造函数放在prenex位置

type 'm production = (* Ɐ a. *)
  | Production: { name: string; rule: 'a expr; action: 'a -> 'm }

或在模块级别绑定它:

type 'a m = ...
type production = (* Ɐ a. *)
  | Production: { name: string; rule: 'a expr; action: 'a -> unit m }

您可以使用新操作扩展存在性,前提是它们不会泄漏存在量化的类型变量。

事实上,您可以使用一流的模块,因为它们have existential type。如,

module type Semantics = sig 
   type t 
   val action : t -> unit
   val rule : t expr
end

type production = (module Semantics)

作为最后一点,请考虑Oleg Kiselyov'tagless embedding作为GADT表示的替代方案。请记住,解析器只是一个解释器:)