OCaml 模块:将(互连的)类型从不同模块引入新模块

发布于 2024-10-14 19:36:24 字数 2201 浏览 2 评论 0原文

问题

我遇到的一个问题是将两个模块的类型和值带入一个新的组合模块中。我举个例子。目前我有以下两种类型签名

module type Ordered =
 sig
  type t (* the type of elements which have an order *)
  val eq : t * t -> bool
  val lt : t * t -> bool
  val leq : t * t -> bool
 end

module type Stack =
 sig
  exception Empty 
  type 'a t (* the type of polymorphic stacks *)

  val empty  : 'a t
  val isEmpty : 'a t -> bool

  val cons  : 'a * 'a t -> 'a t
  val head  : 'a t -> 'a
  val tail  : 'a t -> 'a t
 end

,我想创建一个“基本元素已排序的堆栈”模块,即

module type OrderedStack =
 sig 
  exception Empty

  type elem (* the type of the elements in the stack *)
  val eq : elem * elem -> bool
  val lt : elem * elem -> bool
  val leq : elem * elem -> bool

  type t (* the type of monomorphic stacks *)
  val empty  : t
  val isEmpty : t -> bool
  val cons  : elem * t -> t
  val head  : t -> elem
  val tail  : t -> t
 end

到目前为止,一切都很好且整洁。但现在,我想编写一个函子,它接受 Ordered 模块和 Stack 模块并生成 OrderedStack 模块。像这样的东西

module My_functor (Elem : Ordered) (St : Stack): OrderedStack  = 
 struct
  exception Empty

   type elem = Elem.t
  let eq = Elem.eq
  let lt = Elem.lt
  let leq = Elem.leq

  type t = elem St.t
  let empty = St.empty
  let isEmpty = St.isEmpty
  let cons = St.cons
  let head = St.head
  let tail = St.tail
 end

这正是我想要的并且是正确的。但这看起来非常浪费键盘。

我的问题

有没有更紧凑的方式来编写上面的 My_functor

我发现但无法付诸实践的内容

我已经看到了 include 指令,我可以在其中编写类似以下内容的内容:

module my_functor (Elem : Ordered) (St : Stack): OrderedStack  = 
 struct
  include Elem
  include St
 end

但这有一个问题,对于我上面特定的两个模块, Ordered 和堆栈具有相同的类型 t(尽管它们各自的含义不同)。我不想更改 OrderedStacks 的原始定义,因为它们已经在代码的许多部分中使用,但是如果您找到原始两个的替代公式使其工作的模块,那很好。

我还看到 with 运算符 可能与这里相关,但我无法完全弄清楚应该如何使用它来产生所需的效果。我面临的问题是两个模块 OrderedStacks 的类型 t'a t 和实际上已连接。

有什么想法吗?

The problem

One problem I'm having is bringing the types and vals of two module into a new combined module. I'll give an example. Currently I have the following two type signatures

module type Ordered =
 sig
  type t (* the type of elements which have an order *)
  val eq : t * t -> bool
  val lt : t * t -> bool
  val leq : t * t -> bool
 end

module type Stack =
 sig
  exception Empty 
  type 'a t (* the type of polymorphic stacks *)

  val empty  : 'a t
  val isEmpty : 'a t -> bool

  val cons  : 'a * 'a t -> 'a t
  val head  : 'a t -> 'a
  val tail  : 'a t -> 'a t
 end

and I'd like to create a module of "stacks for which the basic elements are ordered", i.e.

module type OrderedStack =
 sig 
  exception Empty

  type elem (* the type of the elements in the stack *)
  val eq : elem * elem -> bool
  val lt : elem * elem -> bool
  val leq : elem * elem -> bool

  type t (* the type of monomorphic stacks *)
  val empty  : t
  val isEmpty : t -> bool
  val cons  : elem * t -> t
  val head  : t -> elem
  val tail  : t -> t
 end

Up to here, everything is nice and neat. But now, I'd like to write a functor which takes an Ordered module and a Stack module and produces an OrderedStack module. Something like

module My_functor (Elem : Ordered) (St : Stack): OrderedStack  = 
 struct
  exception Empty

   type elem = Elem.t
  let eq = Elem.eq
  let lt = Elem.lt
  let leq = Elem.leq

  type t = elem St.t
  let empty = St.empty
  let isEmpty = St.isEmpty
  let cons = St.cons
  let head = St.head
  let tail = St.tail
 end

This is exactly what I want and is correct. But it looks like an awful waste of keyboard.

My question

Is there a more compact way to write My_functor above?

What I found out but couldn't put to work

I've seen the include directive in which I could write something like:

module my_functor (Elem : Ordered) (St : Stack): OrderedStack  = 
 struct
  include Elem
  include St
 end

but this has the problem that, for my particular two modules above, both Ordered and Stack have the same type t (although they mean different things in each of them). I'd prefer not to change the original definition of Ordered and Stacks as they are already used in many parts in the code but if you find an alternative formulation for the original two modules that makes it work, that's fine.

I've also seen that the with operator may be relevant here but I couldn't quite work out how it should be used to produce the desired effect. The problem I'm facing is that the types t and 'a t of the two modules Ordered and Stacks and actually connected.

Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

与他有关 2024-10-21 19:36:24

OrderedStack 重用有序定义,但类型略有不同(elem 而不是 t)。这是冗余的原因。

您宁愿使用此 OrderedStack 签名,直接重用 Ordered

module type OrderedStack = sig
    module Elem : Ordered

    type t
    val empty       : t
    val isEmpty     : t -> bool
    val cons        : Elem.t * t -> t
    val head        : t -> Elem.t
    val tail        : t -> t
end

冗余的另一个来源是您从参数类型 'a Stack.t< /code>,到单态 OrderedStack.t。这两种类型不能等同,根本没有可比性,所以这里需要手工翻译。

请注意,您可以将从(多态)StackOrderedStack 的移动分解为一个中间堆栈,(单态)MonoStack

module type MonoStack = sig
  type elem
  type t
  val empty       : t
  val isEmpty     : t -> bool
  val cons        : elem * t -> t
  val head        : t -> elem
  val tail        : t -> t
end

module type OrderedStack = sig
  module Elem : Ordered
  module Stack : MonoStack with type elem = Elem.t
end

编辑< /strong>

如果您不喜欢使用子模块的额外间接性(这会增加一些语法负担),则可以包含模块而不是链接到它们。但正如您所注意到的,问题在于名称冲突。从 OCaml 3.12 开始,我们的工具集中有一个新的构造,它允许重命名签名的类型组件以避免冲突。

module type OrderedStack = sig
  type elem
  include Ordered with type t := elem
  include MonoStack with type elem := elem
end

第二次编辑

好的,我想出了以下解决方案来引入 Stack/MonoStack 桥接器。但坦率地说,这是一种黑客行为,我认为这不是一个好主意。

module type PolyOrderedStack = sig
  module Elem : Ordered
  type t
  type 'a const = t
  module Stack : Stack with type 'a t = 'a const
end

(* 3.12 only *)
module type PolyOrderedStack = sig
  type elem
  include Ordered with type t := elem
  type t
  type 'a const = t
  include Stack with type 'a t := 'a const
end

OrderedStack reuse Ordered definitions, with a slightly different type (elem instead of t). This a cause of redundancy.

You could rather use this OrderedStack signature directly reusing Ordered :

module type OrderedStack = sig
    module Elem : Ordered

    type t
    val empty       : t
    val isEmpty     : t -> bool
    val cons        : Elem.t * t -> t
    val head        : t -> Elem.t
    val tail        : t -> t
end

One other source of redundancy is the fact that you move from a parametric type, 'a Stack.t, to the monomorphic OrderedStack.t. The two types cannot be equated, they are not at all comparable, so there necessarily a translation to make by hand here.

Note that you could decompose the move from (polymorphic) Stack to OrderedStack into one intermediary stack, (monomorphic) MonoStack:

module type MonoStack = sig
  type elem
  type t
  val empty       : t
  val isEmpty     : t -> bool
  val cons        : elem * t -> t
  val head        : t -> elem
  val tail        : t -> t
end

module type OrderedStack = sig
  module Elem : Ordered
  module Stack : MonoStack with type elem = Elem.t
end

Edit

If you don't like the additional indirection of using submodules, which can add some syntactic burden, it is possible of including the modules instead of linking to them. But the problem, as you have noticed, are the name conflict. Starting from OCaml 3.12, we have a new construct in our toolset which allows to rename type components of signatures to avoid conflicts.

module type OrderedStack = sig
  type elem
  include Ordered with type t := elem
  include MonoStack with type elem := elem
end

Second Edit

Okay, I came up with the following solution to bring the Stack/MonoStack bridge. But quite frankly, it's a hack and I don't think it's a good idea.

module type PolyOrderedStack = sig
  module Elem : Ordered
  type t
  type 'a const = t
  module Stack : Stack with type 'a t = 'a const
end

(* 3.12 only *)
module type PolyOrderedStack = sig
  type elem
  include Ordered with type t := elem
  type t
  type 'a const = t
  include Stack with type 'a t := 'a const
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文