Вам не нужна подпись
То, что происходит, в значительной степени описывает то, что вы описываете: предоставление MyFoo
подписи BaseFoo
в ее определении ограничивает ее подписью.
Почему? Потому что для этого нужно указать подпись в этом месте. Каноническое решение - оставить подпись (обычно, чтобы определение подписи рядом с определением модуля было достаточно ясным для читателя).
Обратите внимание, что при вызове MyFoo
на вашем функторе будет проверена подпись. Мой обычный выбор - полагаться на это.
Несколько обходных путей
Учитывая то, что вы пробовали, я думаю, это может быть вам интересно:
module type BaseFoo = sig ... end
module MyFoo = struct ... end
module MyFooHidden : BaseFoo = MyFoo (* Same as defining MyFoo : BaseFoo *)
module MyFooWithType :
BaseFoo with type t = MyFoo.t
= MyFoo (* What you want *)
Предложение with type t = t'
позволяет аннотировать сигнатуру модуля для добавления информации о типе. Это очень полезно, особенно при работе с функторами. См. здесь для получения дополнительной информации.
MyFooHidden
может показаться бесполезным, но вы можете видеть это как проверку того, что MyFoo имеет правильную подпись. Вы все еще можете использовать MyFoo
так, как хотите. MyFooWithType
на самом деле (немного) менее полезно, потому что если вы измените свою подпись, добавив тип, который вы хотите экспортировать, вам нужно будет добавить экспорт и здесь.
Использование include
Что касается вашей include
попытки. Ну, хорошая попытка! Вы были почти там:
module MyFoo : sig
type t = A of string | B of int
include BaseFoo with type t := t
end
with type t := t'
немного отличается тем, что выполняет не равенство, а замену. Определение типа t
полностью удалено из подписи BaseFoo
, а все экземпляры заменены на ваши собственные t
, таким образом у вас не возникнет проблем с двойным определением. См. здесь для получения более подробной информации.
Как вы указали, это, вероятно, не тот подход, который вам нужен, поскольку вы больше не можете легко понять, что MyFoo
действительно BaseFoo
.