ocaml - 为具有泛型类型参数的多态变体编写类型约束

标签 ocaml reason

背景:我正在尝试创建论文中描述的内容,Data Type à la Carte - 但尝试查看 OCaml 的多态变体是否可以导致干净的 ReasonML 实现。

我这里的代码是 ReasonML 语法,但这个问题同样适用于 OCaml。

我首先为 ValAdd 定义了两个模块,它们都实现了 fmap - 使它们成为 haskel 风格的仿函数。

module type Functor = {
  type t('a);
  let fmap: ('a => 'b, t('a)) => t('b);
};

module Val = {
  type t('e) = [ | `Val(int)];
  let fmap = _ =>
    fun
    | `Val(x) => `Val(x);
};

module Add = {
  type t('e) = [ | `Add('e, 'e) ];

  let fmap = f =>
    fun
    | `Add(x, y) => `Add((f(x), f(y)))
};

我可以很容易地创建一个 Algebra 数据类型,将这两个模块合二为一,使用非常简单的 fmap 实现。

module Algebra = {
  type t('t) = [ Val.t('t) | Add.t('t)];

  let fmap = (f, x) =>
    switch (x) {
    | #Val.t as v => Val.fmap(f, v)
    | #Add.t as o => Add.fmap(f, o)
    };
};

这可以在更大的上下文中编译和工作,我可以在其中计算由 ValAdd 值组成的表达式。

但是,作为一个不想编写样板代码的程序员,我的下一步是创建一个仿函数(OCaml 仿函数),它可以从任何两个兼容模块生成这样的模块。

我的第一次尝试是这样的:

module JoinAlgebra = (A1: Functor, A2: Functor) => {
  type t('t) = [ A1.t('t) | A2.t('t)];

  let fmap = (f, x) =>
    switch (x) {
    | #A1.t as v => Val.fmap(f, v)
    | #A2.t as o => Add.fmap(f, o)
    };
};

但这行不通。由于 A1.tA2.t 可以是任何东西,我无法将它们组合为多态变体。

Error: The type A1.t('t) is not a polymorphic variant type

我尝试向 Functor 模块类型添加类型约束:

module type Functor = {
  type t('a) = 'a constraint [> ] = 'a;
  let fmap: ('a => 'b, t('a)) => t('b);
};

module JoinAlgebra = (A1: Functor, A2: Functor) => {
  type t('t) = [ A1.t('t) | A2.t('t)]; // This line fails
}

现在我得到了编译器错误

Error: The type A1.t([> ]) is not a polymorphic variant type

有什么方法可以创建一个自动基于这两个模块的模块仿函数?

关于 OCaml 版本的注意事项:我在这里使用的是 bucklescript v. 5,它使用 OCaml 4.02 编译器。但也欢迎需要 4.06 的解决方案(Bucklescript 应该会支持)

最佳答案

您的Functor 签名定义了一个抽象类型'a t。正如您正确指出的那样,“由于 A1.t 和 A2.t 可以是任何东西,我不能将它们组合为多态变体。”为了解决 Functor 中的 'a t 是抽象的问题,您尝试通过以下方式使其成为多态变体:

module Functor = struct
  type 'a t = 'a constraint 'a = [< ]
end

但是,'a 类型变量不再代表包装值,而是代表多态变体约束。这当然不是你想要的。您收到错误 Error: The type A1.t([> ]) is not a polymorphic variant type 因为您只能将“精确变体类型”替换为多态变体:

The first case is an exact variant type: all possible tags are known, with their associated types, and they can all be present. Its structure is fully known.

...

In all three cases, tags may be either specified directly in the `tag-name [of typexpr] form, or indirectly through a type expression, which must expand to an exact variant type, whose tag specifications are inserted in its place.

( https://caml.inria.fr/pub/docs/manual-ocaml/types.html#polymorphic-variant-type )

您的 JoinAlgebra 不需要多态变体。只是做:

module JoinAlgebra (A1 : Functor) (A2 : Functor) = struct
  type 't t = Left of 't A1.t | Right of 't A2.t

  let fmap f x =
    match x with
    | Left v -> Left (A1.fmap f v)
    | Right o -> Right (A2.fmap f o)
end

好处是 Functor 中的 'a t 仍然是抽象的,代码适用于未定义多态变体的 Functor 模块。

关于ocaml - 为具有泛型类型参数的多态变体编写类型约束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55828648/

相关文章:

syntax-error - OCaml 语法错误通过双分号修复

ocaml - camlzip : "This package relies on external (system) dependencies that may be missing."

reason - 如何在 ReScript 中为任意记录类型实现哈希函数?

ffi - 以下警告是什么意思?

ocaml - Jane Street 的 ‘Base’ 、 ‘Core’ 和 'Core_kernel' 有什么区别?

c - __memcpy_sse2_unaligned - 这是什么意思?

oop - OCaml 中对象内的对象

OCamlbuild 和 camlp4.macro

javascript - 模块名称是绑定(bind)中使用的隐藏全局名称

interop - 如何在 ReasonReact 中绑定(bind)和使用高阶组件