我正在尝试在OCaml中编写一个简单的塔防游戏作为一个学习项目,我遇到了如何构建代码的问题。我一直在努力避免使用类/对象代表我的塔和敌人,主要是因为我还没有学习ocaml对象是如何工作的,但主要是因为我似乎已经厌倦了在阅读OCaml时使用它们。无论如何,我一直在尝试做的是为每种类型的塔都有一个模块,它们都具有相同的签名(TOWER
),实现mainloop用于更新游戏的功能。我的想法是使用一流的模块,这样我就可以拥有像
let create m =
let module M = (val m : TOWER) in
M.create ()
其中TOWER
是
module type TOWER = sig
type t
...
val create : unit -> t
end
但是现在我遇到了一个问题,当你打开一个第一类模块时,你会得到一个全新的模块,而我认为你会得到类似模块的别名,所以在编译时我会得到一个错误说Mt的类型构造函数的范围有问题从这个和我试图做的修改似乎我最初使用第一类模块来决定使用哪个函数的想法将无法工作,除非我将类型声明移出TOWER
并使其成为共享所有塔使用的类型。有没有办法获得我正在寻找的效果,还是我需要其他技术?
答案 0 :(得分:3)
如果您在函数中使用第一类模块,并且函数类型中存在类型构造函数,这取决于模块内部定义的类型,则表示该类型转义为范围,您需要通过将此类型绑定到新类型变量,将函数置于prenex普通形式。这里澄清一下这个例子:
let create_tower (module T : TOWER) = T.create ()
这将无法编译,因为T.t.
会转义范围。尝试为函数create_tower
编写一个类型:(module TOWER) -> ?
。我们没有t
类型的名称,该名称位于TOWER
签名的范围内。所以我们需要把它拿出来,以获得以下类型:
(module TOWER with type t = 'a) -> 'a
为此,我们使用以下语法:
let create_tower (type t) (module T : TOWER with type t = t) =
T.create ()
现在它有效。
关于模块的常见咆哮。这里不需要使用模块或对象。只需使用记录来代表你的塔和敌人。
答案 1 :(得分:2)
如果您使用总和类型来表示不同的塔,则无需为模块或对象烦恼。
例如:
type tower =
| SimpleTower
| SuperTower of int
type position = int * int
type pos_tower = position * tower
let string_of_tower = function
| SimpleTower -> "Simple tower"
| SuperTower i -> "Super Tower level " ^ (string_of_int i)
;;
let my_towers = [ ((0,0) , SimpleTower) ; ( (10,0) , SuperTower 5) ] ;;
这种体系结构的属性与面向对象体系结构的属性是双重的(这里的函数是模块化的,而它们在对象体系结构中是交叉的)。