Thanks Arnaud! I think I've seen that mail thread before, but didn't really understand much at the time. It makes more sense to me now. A record of functions with parametric type is kinda neat. On Wed, Aug 8, 2012 at 3:21 AM, Arnaud Spiwack < Arnaud.Spiwack@lix.polytechnique.fr> wrote: > In this particular case this is just a case of existentially quantified. > You can do it with first-class module or gadt (both are convenient), or > with a clever encoding which only uses record and polymorphic fields. You > can read all about it here : > http://caml.inria.fr/pub/ml-archives/caml-list/2004/01/52732867110697f55650778d883ae5e9.fr.html > > > On 7 August 2012 03:48, Anthony Tavener wrote: > >> Embedding "database" features into other modules. >> >> First-class modules are allowing me to neatly unpack table >> implementations into >> other modules, using local types. >> >> This is really quite basic, but I wonder if I could have done this before >> first-class modules, and without leveraging the object system? I also >> don't >> recall seeing first-class modules used for something like this. >> >> So, is there another way to do this, aside from the object system? It's >> very >> much like creating a basic object. >> >> This is an extracted and simplified example... >> -------------------- >> >> module Db = struct >> >> (* Signature for a single 'table' in the database, elements of type t. >> *) >> module type S = sig >> (* Full implementation relies on a Key module for different key types >> than 'int' *) >> type t >> val get : int -> t >> val set : int -> t -> unit >> val del : int -> unit >> val iter : (int -> t -> unit) -> unit >> val fold : (int -> t -> 'a -> 'a) -> 'a -> 'a >> end >> >> (* Instantiates storage for a table, and returns FC module to interact >> with the store. *) >> let create_with_default (type s) ?(size=19) default = >> (* Full implementation is parameterized by Key and Table modules *) >> let h = Hashtbl.create size in >> let module H = struct >> type t = s >> let get (id:int) = >> try Hashtbl.find h id >> with Not_found -> default >> let set id (v:t) = Hashtbl.replace h id v >> let del id = Hashtbl.remove h id >> let iter f = Hashtbl.iter f h >> let fold f init = Hashtbl.fold f h init >> end in >> (module H : S with type t = s) >> >> end >> >> (* An example table... *) >> module Location = struct >> let unknown = "Unknown" >> include (val (Db.create_with_default unknown) : S with type t = string) >> (* Location might have a bunch of other functionality as well... *) >> end >> >> (* and basic usage... *) >> # Location.get 1;; >> - : Location.t = "Unknown" >> # Location.set 1 "Mars";; >> - : unit = () >> # Location.get 1;; >> - : Location.t = "Mars" >> >> -------------------- >> Some background on what this is for: (skip unless you're interested!) >> >> I use a "component architecture" with most games -- basically a database >> of >> properties keyed off "game object IDs". I thought this was a very powerful >> feature in old MUDs/MUSHs. It's one of the first things I tried making >> when I >> started in OCaml, but I had some difficulties and ended up explicity >> instantiating hashtables or maps in the global context of various modules. >> Sloppy, but workable. (The reason I had difficulty is because I was >> trying to >> create a database of tables which were created at runtime -- not >> statically >> known.) >> >> Recently I decided to fix this mess. I had many modules, each which >> tended to >> have a corresponding "table". Eg. Characteristics, Size, Wounds, Faction, >> Inventory, etc. So ideally I wanted a convenient way to embed "database" >> functions into such modules while declaring the implementation of the >> underlying datastore (hashtable, map, whatever). >> >> This might seem a bit ugly from a functional-programming perspective, but >> I've >> found components to be quite powerful, and overall helps to constrain >> where >> and how mutation happens. "Game state" is generally in flux -- well, it is >> everything variable, and can be compared closely with save-game state. >> Most code >> which doesn't update game state can be functional. Actually, it feels >> creepy to >> have a variable assignment in the code, since mutation is generally to >> game-state >> and that's handled through a database. So the resulting style is >> functional+database. >> >> -Tony >> >> >