Один и тот же тип в другом модуле ocaml не совпадает - PullRequest
1 голос
/ 10 января 2020

Мне жаль, что это выглядит как вопрос "пустяков", но это самый минимальный пример, который я могу придумать:

module type Foo = sig
  type 'a t

  val foo : 'a -> 'a t
end

module Foo : Foo = struct
  type 'a t = T of 'a

  let foo x = T x
end

(* Rewrap foo, add one function *)
module type Bar = sig
  include Foo

  val bar : int -> int t
end

module Bar : Bar = struct
  include Foo

  let bar n = foo (n + 1)
end

(* Specialization of Baz for integers *)
module type Baz = sig
  type t
  val bar : int -> t
end

module Baz : Baz = struct
  type t = int Foo.t
  let bar = Bar.bar
end

В этом примере Foo.t и Bar.t являются точно такой же тип. Поэтому int Foo.t - это то же самое, что и int Bar.t.

. Однако я получаю следующую ошибку компиляции:

File "example.ml", lines 31-34, characters 19-3:
31 | ...................struct
32 |   type t = int Foo.t
33 |   let bar = Bar.bar
34 | end
Error: Signature mismatch:
       Modules do not match:
         sig type t = int Foo.t val bar : int -> int Bar.t end
       is not included in
         Baz
       Values do not match:
         val bar : int -> int Bar.t
       is not included in
         val bar : int -> t
       File "example.ml", line 28, characters 2-20: Expected declaration
       File "example.ml", line 33, characters 6-9: Actual declaration

, если я заменю type t = int Foo.t, но type t = int Bar.t, внезапно подборка компиляции. В моем исходном коде у меня много вещей, которые зависят от обоих типов. В моем примере это было бы следующим:

module type Baz = sig
  type t
  val foo : int -> t
  val bar : int -> t
end

module Baz : Baz = struct
  type t = int Foo.t
  let foo n = Foo.foo n
  let bar = Bar.bar
end

Как я могу сказать компиляции, что Foo.t и Bar.t одинаковы, не делая их "публично видимыми"?

1 Ответ

5 голосов
/ 10 января 2020

В вашем примере Foo.t и Bar.t являются совершенно не связанными типами с точки зрения проверки типов. Действительно, когда вы пишете

module Bar: Bar

, вы ограничиваете тип модуля Bar, чтобы он был точно Bar.t. А в модуле тип Bar, Bar.t является абстрактным типом без какой-либо известной связи с любыми другими типами.

Если вы хотите сохранить информацию о том, что Bar.t совпадает с Foo.t, вам необходимо добавить эту информацию обратно в ограничение типа модуля:

module Bar : Bar with type 'a t = 'a Foo.t

В общем, когда вы начинаете писать типы модулей в OCaml, лучше начинать с типов модулей, определяемых средством проверки типов, и только удаляйте информацию экономно. В противном случае люди часто ограничивают модули совершенно непрозрачными типами модулей, которые удаляют из модулей столько информации, что они делают модуль бесполезным.

Например, с вашим упрощенным определением,

module type Foo = sig
  type 'a t
  val foo : 'a -> 'a t
end

module Foo : Foo = struct
  type 'a t = T of 'a
  let foo x = T x
end

Модуль Foo в основном бесполезен, потому что у вас есть конструктор типа черного ящика Foo.t, для которого вы можете только создавать значения этого типа, но никогда не разрушать и не использовать их.

...