Во-первых, самое простое решение - сделать типы взаимно рекурсивными
type todo = { text:string, type todos:array(todo) }
and note = { todo:todo }
Если вам действительно нужно разделить два типа на отдельные модули, то эти рекурсивные модули действительно необходимы.
В этом случае ключевая идея заключается в том, что подписи представляют полную спецификацию содержимого модуля, другими словами, подпись
module type T = { type t }
- это спецификация для модуля, который реализует тип черного ящика t
и ничто остальное.
Следовательно, ограничения подписи Note:Sig.NoteSig
и Todo:TodoSig
в
module rec Note:Sig.NoteSig = { type t = { todo: Todo.t} }
and Todo:Sig.TodoSig = {
type t = { text: string, notes: array(Note.t)}
}
фактически стирает всю информацию о фактической реализации Note.t и Todo.t.
Что вы хотите, чтобы сначала написать полную подпись:
module type NoteSig = { type todo; type t = {todo: todo} }
module type TodoSig = {
type note;
type t = { text: string, notes: array(note)}
}
тогда вы можете написать реализацию как
module rec Note: NoteSig with type todo := Todo.t = { type t = { todo: Todo.t} }
and Todo: TodoSig with type note := Note.t =
{ type t = { text: string, notes: array(Note.t)} }
Если в вашем модуле есть только типы, вы можете использовать следующую версию
module rec Note: NoteSig with type todo := Todo.t = Note
and Todo: TodoSig with type note := Note.t = Todo
Для версии с функтором, если вам не нужны функции, определенные в модуле, простейшая реализация - просто
module Make_Todo(Note: { type t;}) = {
type t = { text:string, notes:array(Note.t) }
}
module Make_Note(Todo: { type t;}) = { type t = { todo:Todo.t} }
(как правило, для начинающих обычно лучше, чтобы средство проверки типов выводило функторы типа результата.)
тогда вы можете создать экземпляр с помощью
module rec Todo: TodoSig with type note := Note.t = Make_Todo(Note)
and Note : NoteSig with type todo := Todo.t = Make_Note(Todo)
Если вам нужно больше, чем тип других модулей внутри функтора make, вы можете пойти еще дальше, указав, что аргумент функторов реализует
полная подпись
module Make_Todo(Note: NoteSig) = {
type t = { text:string, notes:array(Note.t) }
}
module Make_Note(Todo: TodoSig) = { type t = { todo:Todo.t} }
но тогда создание экземпляра модуля становится чуть более сложным
module rec Todo: TodoSig with type note := Note.t =
Make_Todo({ include(Note); type todo = Todo.t })
and Note : NoteSig with type todo := Todo.t =
Make_Note({ include(Todo); type note = Note.t })
и существует больше рисков для возникновения сложных ошибок.