Модули OCaml: приведение (взаимосвязанных) типов из разных модулей в новый модуль

проблема

одна проблема, с которой я сталкиваюсь, - это приведение типов и vals двух модулей в новый комбинированный модуль. Приведу пример. В настоящее время у меня есть следующие две подписи типа

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

до сих пор все хорошо и аккуратно. Но теперь я хотел бы написать функтор, который принимает упорядоченный модуль и модуль стека и создает модуль 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

но в этом есть проблема, что для моего конкретные два модуля выше, как упорядоченные, так и стековые имеют одинаковое type t (хотя они означают разные вещи в каждой из них). Я бы предпочел не изменять исходное определение Ordered и Stacks поскольку они уже используются во многих частях кода, но если вы найдете альтернативную формулировку для исходных двух модулей, которая заставляет его работать, это нормально.

я также видел, что with оператор может быть уместной здесь, но я не мог понять, как это следует использовать для получения желаемого эффекта. Проблема, с которой я сталкиваюсь, заключается в том, что типы t и 'a t из двух модулей Ordered и Stacks и на самом деле связаны.

какие идеи?

1 ответов


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, к мономорфным OrderedStack.t. Эти два типа не могут быть приравнены, они вообще не сопоставимы, поэтому обязательно есть перевод, чтобы сделать вручную здесь.

обратите внимание, что вы можете разложить перемещение из (полиморфного)Stack to OrderedStack в один промежуточный стек, (мономорфный) 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

редактировать

Если вам не нравится дополнительная косвенность использования подмодулей, которая может добавить некоторую синтаксическую нагрузку, можно включить модули вместо ссылки на них. Но проблема, как вы заметили, заключается в конфликте имен. Начиная с 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