Запись полевых заметок не найдена - PullRequest
0 голосов
/ 04 июля 2018

Я пытаюсь определить переменную с помощью взаимно рекурсивного модуля, скажем, у Todo может быть много Note, а Note может принадлежать Todo:

module Sig = {
  module type NoteSig = {type t;};
  module type TodoSig = {type t;};
};

/* same file */
module Same = {
  module rec Note: Sig.NoteSig = {
    type t = {todo: Todo.t};
  }
  and Todo: Sig.TodoSig = {
    type t = {
      text: string,
      notes: array(Note.t),
    };
  };
};

/* different files */
module A = {
  module Note = (T: Sig.TodoSig) => {
    type t = {todo: T.t};
  };
};

module B = {
  module Todo = (N: Sig.NoteSig) => {
    type t = {notes: array(N.t)};
  };
};

module C = {
  module rec NoteImpl: Sig.NoteSig = A.Note(TodoImpl)
  and TodoImpl: Sig.TodoSig = B.Todo(NoteImpl);
};

/* impl */
let todo1: Same.Todo.t = {notes: [||]};
let todo2: C.TodoImpl.t = {notes: [||]};

let todo3 = Same.Todo.{notes: [||]};
let todo4 = C.TodoImpl.{notes: [||]};

Js.log2(todo1, todo2);

Однако я не могу определить переменную с этим типом, компилятор сообщает, что:

36 │
37 │ /* impl */
38 │ let todo1: Same.Todo.t = {notes: [||]};
39 │ let todo2: C.TodoImpl.t = {notes: [||]};
40 │

The record field notes can't be found.

If it's defined in another module or file, bring it into scope by:
- Annotating it with said module name: let baby = {MyModule.age: 3}
- Or specifying its type: let baby: MyModule.person = {age: 3}

Тот же код в Ocaml, если он помогает:

module Sig =
  struct
    module type NoteSig  = sig type t end
    module type TodoSig  = sig type t end
  end
module Same =
  struct
    module rec Note:Sig.NoteSig = struct type t = {
                                           todo: Todo.t;} end
    and Todo:Sig.TodoSig =
      struct type t = {
               text: string;
               notes: Note.t array;} end
  end
module A =
  struct module Note(T:Sig.TodoSig) = struct type t = {
                                               todo: T.t;} end end
module B =
  struct
    module Todo(N:Sig.NoteSig) = struct type t = {
                                          notes: N.t array;} end
  end
module C =
  struct
    module rec NoteImpl:Sig.NoteSig = A.Note(TodoImpl)
    and TodoImpl:Sig.TodoSig = B.Todo(NoteImpl)
  end
let todo1: Same.Todo.t = { notes = [||] }
let todo2: C.TodoImpl.t = { notes = [||] }
let todo3 = let open Same.Todo in { notes = [||] }
let todo4 = let open C.TodoImpl in { notes = [||] }
let _ = Js.log2 todo1 todo2

Извините за длинный код, пожалуйста, отбросьте эти строки ниже.

Ответы [ 2 ]

0 голосов
/ 05 июля 2018

Во-первых, самое простое решение - сделать типы взаимно рекурсивными

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 }) 

и существует больше рисков для возникновения сложных ошибок.

0 голосов
/ 05 июля 2018

Для будущих посетителей у меня есть ответ, если оба находятся в одном файле:

module Same = {
  module rec Note: {
    type t = {
      title: string,
      todo: option(Todo.t),
    };
  } = Note
  and Todo: {
    type t = {
      title: string,
      notes: option(array(Note.t)),
    };
  } = Todo;
};

let todo: Same.Todo.t = {
  title: "helo",
  notes: Some([|Same.Note.{title: "one", todo: None}|]),
};

[@bs.module "util"] external inspect : ('a, 'b) => 'c = "inspect";
Js.log(inspect(todo, {"depth": 10}));

Я все еще ищу решение функтора.

...