Dom
Dom

Reputation: 153

Avoiding object

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

Answers (2)

Julien C.
Julien C.

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

ivg
ivg

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

Related Questions