OCaml 函数子程序、模块和子模块

5

很抱歉发了这么长的非可编译代码。尽管在stackoverflow上阅读了几个关于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"

我认为我需要在search.mli中明确声明adt模块作为子模块,类似于:

search.mli

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

但是,我收到的是:
Parse error: [module_declaration] expected after [a_UIDENT] (in [sig_item])

我在这里缺少什么?我感觉我要么语法失败了,要么没有完全掌握函数对象、模块和子模块的概念... 编辑 非常感谢您的解释,gasche!通过您的示例,我能够写出自己想要的代码。由于ocaml中对functor的概念存在很多混淆,因此我将在此发布以进行澄清。
实际上,我想将该函数与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
你想要做什么?你希望你的函数能够针对广告类型(例如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的定义中,忘记了functor的struct .. end模块参数中的struct

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)是一个项表达式,它将带有S签名的模块M打包成一个值。)

在某些情况下,它可以补充使用一个函数器,但由于它还比较新,有点复杂(对一级模块使用存在非平凡限制),而且可能在下一个语言版本中发生变化,因此我建议保持传统的函数器构造方式。


@ndbd:我添加了一些关于一等模块的内容,这可能会满足你的好奇心。但是要远离那些新的闪亮东西! - gasche

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接