Reputation: 153
I'm trying to write a simple tower defense game in OCaml as a bit of a learning project and I'm having a problem with how I structure my code. I've been trying to avoid using classes/objects to represent my towers and enemies mostly because I haven't learnt how ocaml objects work, but mostly because I seem to have picked up an aversion to using them from my reading about OCaml. Anyway, what I've been trying to do is have a module for each type of tower, which all have the same signature (TOWER
), implementing functions that the mainloop uses to update the game. My idea was to use first class modules so I could then have a function like
let create m =
let module M = (val m : TOWER) in
M.create ()
where TOWER
is
module type TOWER = sig
type t
...
val create : unit -> t
end
But now I have a problem in that it seems that when you unwrap a first class module you get a brand new module, whereas I assumed you'd get something like an alias for the module, so at compile time I get an error saying there's a problem with the scope of the type constructor for M.t. From this and the tinkering I've tried to do it seems that my original idea of using first class modules to decide which function to use won't work unless I move the type declaration out of TOWER
and make it a shared type that all towers use. Is there a way to get the effect I'm looking for or would I have to some other technique?
Upvotes: 0
Views: 225
Reputation: 329
If you use a sum type to represent different towers, you don't have to bother with modules or objects.
For example :
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) ] ;;
The properties of such an architecture are dual to the properties of an object oriented architecture (functions are modular here whereas they crosscut in an object architecture).
Upvotes: 2
Reputation: 35260
If you're using a first class module in a function, and there exists a type constructor in a function type, that depends on a type that is defined inside the module, then it means, that the type escape its scope, and you need to put your function into a prenex normal form, by bounding this type to a new type variable. To clarify this here is the example:
let create_tower (module T : TOWER) = T.create ()
This will not compile, since T.t.
escapes the scope. Try to write a type for the function create_tower
: (module TOWER) -> ?
. We don't have a name for the type t
, that is in the scope of the TOWER
signature. So we need to bring it out, to get the following type:
(module TOWER with type t = 'a) -> 'a
To get this, we use the following syntax:
let create_tower (type t) (module T : TOWER with type t = t) =
T.create ()
Now it works.
And a usual rant about modules. There is no need to use modules or objects here. Just use records to represent your towers and enemies.
Upvotes: 3