Функторы, модули и подмодули Ocaml - PullRequest
4 голосов
/ 02 августа 2011

Извините за публикацию такого длинного не компилируемого кода.Но, несмотря на то, что я прочитал несколько вопросов и ответов о стековом потоке на функторах ocaml, я не понимаю, как это решить:

Предположим, у меня очень абстрактная структура данных:

ads.mli

module type ENTRY = sig
    type t
    val get_index : t -> int
    val compare : t -> t -> int
end

module type T = sig
    type entry
    type t
    val create : unit -> t
    val insert : entry -> t -> t
    val delete : entry -> t -> t
end

Основываясь на этом, я могу создать конкретные структуры данных для этой абстрактной реализации, передав функтор.Например, я сделал:

concrete_ads.mli

module Make (Entry: Ads.ENTRY) : (ads.T with type entry = Entry.t)

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

module AT = Concrete_ads.Make( 
    type t = int * int;; 
    let get_index = fst;; 
    let to_string = (fun (x,y) -> Printf "%i, %i" x y);; 
end);;

И затем используйте реализацию, например:

let at = AT.create () in
let ati = AT.insert (1,2) at in
let atd = AT.delete (1,2) ati in

... и т. Д.

Теперь я хочу написать несколько функций, которые работают с этими структурами данных, в отдельном исходном файле, иони должны быть доступны снаружи.Но я не знаю, как объявить тип этих функций.Примерно так:

search.mli

val search : Int -> Ads.T -> int list

Но при компиляции я получаю:

 Failure: "invalid long identifier type"

Я тогда подумал, что нужно специально объявить модульadt как подмодуль в search.mli, что-то вроде:

search.mli

module AD = Ads;;
 ...
val search : Int -> AD.T -> int list

Но я получаю:

Parse error: [module_declaration] expected after [a_UIDENT] (in [sig_item])

Чего мне здесь не хватает?Я чувствую, что либо ошибаюсь с синтаксисом, либо не полностью понял концепцию функторов, модулей и подмодулей ...

Edit Большое спасибо за ваше объяснение, gasche!На вашем примере я смог написать то, что я имел в виду.Я опубликую это здесь для пояснения, так как, кажется, в ocaml много путаницы с функторами.

На самом деле я хотел сделать функцию абстрактной по отношению к Ads.T, но для Ads.T.t требовался определенный тип.

Теперь у меня есть search.mli:

module Make (T : Ads.T with type entry = int * int) : sig
    val search : T.t -> int -> int
end;;

И, в search.ml:

module Make (T : Ads.T with type entry = int * int) : sig
    val search : T.t -> int -> int 
end = struct
    (* actual implementation of search *)
end;;

И все работает именно так, как я и предполагал.

1 Ответ

6 голосов
/ 02 августа 2011

Что вы пытаетесь сделать именно? Вы хотите, чтобы ваша функция была параметризована по объявлению типа (например, Ads.T.t) или по объявлению модуля (например, Ads.T)?

В обоих случаях вы должны заключить эти общие функции в модули:

module Generic (Ad : Ads.T) : sig
  val search : int -> Ad.t -> int list
end = struct
  let search _ _ = assert false
end

Затем вы можете легко их создать, например. для работы с вашими Conrete_ads модулями:

module AT = Concrete_ads.make(struct ... end)
module Lib = Generic(AT)
let foo = Lib.search 2 (AT.create ())

Конечно, если вы хотите, чтобы ваши функции были параметризованы по конкретному, конкретному типу:

val search : int -> AT.t -> int list

PS: в своем определении AT вы забыли struct в аргументе модуля struct .. end функтора.

PPS: в OCaml 3.12 появилась новая блестящая вещь под названием первоклассные модули , которая позволяет передавать модуль в качестве аргумента значения в функцию

val search : int -> (module Ad.T) -> int list

let search n ad_module =
  let module Ad = (val ad_module : Ad.T) in
  ... Ad.foo ..

... search 2 (module AT : Ad.T) ...

(Объяснение: module S, как выражение типа, это тип значений, которые являются «усовершенствованными модулями» подписи S; (val t : S), как выражение модуля, это модуль, который был упакован в значение t, с подписью S. Здесь я принимаю ad_module в качестве значения и распаковываю его в модуль Ad локально, который затем можно использовать как любой другой модуль внутри функции. Наконец, (module M : S) является выражение-выражение, которое упаковывает модуль M с подписью S в значение.)

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

...