From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Original-To: caml-list@sympa.inria.fr Delivered-To: caml-list@sympa.inria.fr Received: from mail1-relais-roc.national.inria.fr (mail1-relais-roc.national.inria.fr [192.134.164.82]) by sympa.inria.fr (Postfix) with ESMTPS id DE3417ED5C for ; Wed, 8 Aug 2012 16:17:34 +0200 (CEST) Received-SPF: None (mail1-smtp-roc.national.inria.fr: no sender authenticity information available from domain of anthony.tavener@gmail.com) identity=pra; client-ip=74.125.82.52; receiver=mail1-smtp-roc.national.inria.fr; envelope-from="anthony.tavener@gmail.com"; x-sender="anthony.tavener@gmail.com"; x-conformance=sidf_compatible Received-SPF: Pass (mail1-smtp-roc.national.inria.fr: domain of anthony.tavener@gmail.com designates 74.125.82.52 as permitted sender) identity=mailfrom; client-ip=74.125.82.52; receiver=mail1-smtp-roc.national.inria.fr; envelope-from="anthony.tavener@gmail.com"; x-sender="anthony.tavener@gmail.com"; x-conformance=sidf_compatible; x-record-type="v=spf1" Received-SPF: None (mail1-smtp-roc.national.inria.fr: no sender authenticity information available from domain of postmaster@mail-wg0-f52.google.com) identity=helo; client-ip=74.125.82.52; receiver=mail1-smtp-roc.national.inria.fr; envelope-from="anthony.tavener@gmail.com"; x-sender="postmaster@mail-wg0-f52.google.com"; x-conformance=sidf_compatible X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: AiUCAFR0IlBKfVI0imdsb2JhbABFsREBiEIIIgEBAQoJDQcSBiOCIAEBAQMBEgITGQEbHQEDAQsGBQsHBi4hAQERAQUBDg4GEyKHWwEDBgYLnAEJA4wjgnGFQAoZJw1XiHEBBQyKIWUaBoZAA4hOiyiBU4EUiXaDKD6EHYE4CA X-IronPort-AV: E=Sophos;i="4.77,733,1336341600"; d="scan'208";a="169398272" Received: from mail-wg0-f52.google.com ([74.125.82.52]) by mail1-smtp-roc.national.inria.fr with ESMTP/TLS/RC4-SHA; 08 Aug 2012 16:17:34 +0200 Received: by wgbfg15 with SMTP id fg15so693959wgb.9 for ; Wed, 08 Aug 2012 07:17:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type; bh=YRDRDxnkvQRtsamOv5g+yrqwZx3plCNg6WZJW8Lp1Nc=; b=CIokZw6Z0g17iKDUmDbPQG9Fswnuw4+rVI+CJdSy986H0iXCxu98K+l0TQJ3/xH+KP 2Nk3onmbzGoBLewesf+KjXb8cbwaiBifbyy+fIg4H5a/HaIWeMkE1eKpWeuAz77NBB4m g4n+WIFQEcuwmxFIOkM+XY2vOzz0pY9lDNWNsCduya0yrFRXgCduYuGbercotctrqntO diyUUsB89i0/IOCZJeKnUQIXRr/P1R8JOz5rVDzMt1pbZYKLjYOATT8GOLESinUA9dHj 8xHLs/srndxDJVf4nVOV6+9SoxtjpgSIVt14UtCOwk/JnDwW/ebpUXRPBHPQg7f5UIA1 Wy1g== MIME-Version: 1.0 Received: by 10.180.100.131 with SMTP id ey3mr3097061wib.15.1344435454178; Wed, 08 Aug 2012 07:17:34 -0700 (PDT) Received: by 10.223.201.72 with HTTP; Wed, 8 Aug 2012 07:17:34 -0700 (PDT) In-Reply-To: References: Date: Wed, 8 Aug 2012 08:17:34 -0600 Message-ID: From: Anthony Tavener To: Arnaud Spiwack Cc: caml-list@inria.fr Content-Type: multipart/alternative; boundary=f46d0444ec19ab73e704c6c1c56f Subject: Re: [Caml-list] A use-case for first-class modules... out of curiosity is there another way? --f46d0444ec19ab73e704c6c1c56f Content-Type: text/plain; charset=ISO-8859-1 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 >> >> > --f46d0444ec19ab73e704c6c1c56f Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Thanks Arnaud!

I think I've seen that mail thread be= fore, but didn't really understand much at the time. It makes more sens= e 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.po= lytechnique.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-lis= t/2004/01/52732867110697f55650778d883ae5e9.fr.html


On 7 August 2012 03:48, Anthony Tavener <anthony.tavener@gmail.com> wrote:
Embedding "database" features into other modules.
=
First-class modules are allowing me to neatly unpack table i= mplementations into
other modules, using local types.

This is really quite basic, but I wonder if I could have don= e this before
first-class modules, and without leveraging the obj= ect system? I also don't
recall seeing first-class modules us= ed for something like this.

So, is there another way to do this, aside from the obj= ect system? It's very
much like creating a basic object.

This is an extracted and simplified example...
--------------------

module Db =3D struct

=A0 (* Signature for a single 'table' in the = database, elements of type t. *)
=A0 module type S =3D sig
<= div> =A0 =A0 (* Full implementation relies on a Key module for different key typ= es than 'int' *)
=A0 =A0 type t
=A0 =A0 val get= : int -> t
=A0 =A0 val set : int -> t -> unit
=A0 =A0 val del : int -> unit
=A0 =A0 val iter : (int -> t -> unit) -> unit
=A0 = =A0 val fold : (int -> t -> 'a -> 'a) -> 'a -> &= #39;a
=A0 end

=A0 (* Instantiates storag= e for a table, and returns FC module to interact with the store. *)
=A0 let create_with_default (type s) ?(size=3D19) default =3D
=A0 =A0 (* Full implementation is parameterized by Key and Table modules = *)
=A0 =A0 let h =3D Hashtbl.create size in
=A0 =A0 let= module H =3D struct
=A0 =A0 =A0 type t =3D s
=A0 =A0 =A0 let get (id:int) =3D=A0=
=A0 =A0 =A0 =A0 try Hashtbl.find h id
=A0 =A0 =A0 =A0 = with Not_found -> default
=A0 =A0 =A0 let set id (v:t) =3D Has= htbl.replace h id v
=A0 =A0 =A0 let del id =3D Hashtbl.remove h i= d
=A0 =A0 =A0 let iter f =3D Hashtbl.iter f h
=A0 =A0 =A0 let = fold f init =3D Hashtbl.fold f h init
=A0 =A0 end in
= =A0 =A0 (module H : S with type t =3D s)

end
=

(* An example table... *)
module Location =3D struct
=A0 let unknown =3D "Unknown= "
=A0 include (val (Db.create_with_default unknown) : S with= type t =3D string)
=A0 (* Location might have a bunch of other f= unctionality as well... *)
end

(* and basic usage... *)
# Loca= tion.get 1;;
- : Location.t =3D "Unknown"
# L= ocation.set 1 "Mars";;
- : unit =3D ()
# Loca= tion.get 1;;
- : Location.t =3D "Mars"

---------= -----------
Some background on what this is for: (skip unless you= 're interested!)

I use a "component archi= tecture" 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 fir= st 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 modu= les.
Sloppy, but workable. (The reason I had difficulty is becaus= e 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 &qu= ot;table". Eg. Characteristics, Size, Wounds, Faction,
Inventory, etc. So ideally I wanted a convenient way to embed "databas= e"
functions into such modules while declaring the implementation of the<= /div>
underlying datastore (hashtable, map, whatever).

This might seem a bit ugly from a functional-programming perspecti= ve, but I've
found components to be quite powerful, and overall helps to constrain = where
and how mutation happens. "Game state" is general= ly in flux -- well, it is
everything variable, and can be compare= d closely with save-game state. Most code
which doesn't update game state can be functional. Actually, it fe= els creepy to
have a variable assignment in the code, since mutat= ion is generally to game-state
and that's handled through a d= atabase. So the resulting style is
functional+database.

=A0-Tony

<= /div>


--f46d0444ec19ab73e704c6c1c56f--