OCaml模块:include和open的区别?

13

我对OCaml模块相当陌生,使用自己的模块时我没有成功地将"include"和"open"分开使用。我试过把签名放在单独的.mli文件中,但没有成功。

下面是一个最小(不)工作示例,我正在尝试编译它:

ocamlc -o main Robot.ml main.ml

我应该怎么做才能只使用"open"或者只使用"include",而不是同时使用它们?


文件"Robot.ml":

module type RobotSignature =
sig 
   val top: unit -> unit
end

module Robot =
struct
   let top () = 
      begin
         Printf.printf "top\n"
      end
   (* Should not be visible from the 'main' *)
   let dummy () = 
      begin
         Printf.printf "dummy\n"
      end
end

无法工作的文件"main.ml":

open Robot;;

top();

文件 "main.ml"(正在工作):

include Robot;;
open Robot;;

top();

我认为你已经得到了你的问题的答案。你可能还想阅读有关编译单元的内容。但是请注意,一旦你理解了open的作用,请不要使用它,因为它会使你的代码难以理解。 - Daniel Bünzli
我通常会同意,但在这种情况下,目标是为初学者(特别是OCaml)提供一个简单的“机器人库”来教授基本编程知识。因此,我尽可能避免使用Robot.top()语法。 - Loïc Février
我认为对于初学者来说,显式地渲染他们正在操作的对象实际上会使它更易于理解。无论如何,您可能还想查看openinclude的文档。 - Daniel Bünzli
2个回答

14

你有两个级别的机器人。由于你在robot.ml文件中明确调用了你的模块名为"Robot",所以你需要打开Robot然后调用Robot.top()。任何在robot.ml文件中的内容已经隐式地放在Robot模块中。

你可以摆脱robot.ml中的额外的'module Robot'声明。

robot.ml将变为:

module type RobotSignature =
sig 
   val top: unit -> unit
end


let top () = 
   begin
       Printf.printf "top\n"
   end

那么你在main.ml中已经有了的代码应该就可以工作。

根据下方评论的更新: 如果你担心在打开Robot模块时会看到robot.ml中的所有内容,你可以定义一个robot.mli文件,该文件指定可在外部使用的函数。例如,假设你在robot.ml中添加了一个名为helper的函数:

let top () =
  begin
     Printf.printf "top\n"
  end

let helper () =
  Printf.printf "helper\n"

然后你可以按照以下方式定义你的robot.mli:

val top: unit -> unit

那么假设你试图从main.ml调用helper:

open Robot;;

top();
(* helper will not be visible here and you'll get a compile error*)
helper ()

然后当你尝试编译时会收到一个错误:

$ ocamlc -o main robot.mli robot.ml main.ml
File "main.ml", line 4, characters 0-6:
Error: Unbound value helper

确实,但现在签名没有效果,因为一切都可以从主函数中看到。我理解了“两个级别的机器人”,但不知道如何修复它,同时保持有用的签名。 - Loïc Février
如果您想确保只有Robot模块内的内容在main中可见,则定义一个robot.mli文件,仅导出您想要导出的内容(我将编辑上面的响应以显示此内容)。 - aneccodeal

6
你有两种方法可以做到这一点:
  • First, you can constrain your sub-structure to be of the right signature:

    module Robot : RobotSignature = struct ... end
    

    Then in main.ml, you can do open Robot.Robot: the first Robot means the compilation unit associated to robot.ml, the second Robot is the submodule you have defined inside robot.ml

  • You can also remove one level and create robot.mli containing:

    val top: unit -> unit
    

    and robot.ml containing:

    let top () = 
      Printf.printf "top\n"
    
    (* Should not be visible from the 'main' *)
    let dummy () = 
      Printf.printf "dummy\n"
    

    You can compile the modules using ocamlc -c robot.mli && ocamlc -c robot.ml and then in main.ml simply use open Robot.


最好不要打开Robot,而是调用Robot.top()。 - Daniel Bünzli
如果“Robot.Robot.top”使用频率过高,则可以写成“let module R = Robot.Robot in R.top”。 - lambdapower

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