Модули OCaml: перенос (взаимосвязанных) типов из разных модулей в новый модуль - PullRequest
7 голосов
/ 29 января 2011

Проблема

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

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, и Stack имеют одинаковые type t (хотя в каждом из них они означают разные вещи).Я бы предпочел не менять исходное определение Ordered и Stacks, поскольку они уже используются во многих частях кода, но если вы найдете альтернативную формулировку для двух исходных модулей, которая заставит его работать, это нормально.

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

Есть идеи?

1 Ответ

8 голосов
/ 30 января 2011

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 к 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
...