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 6894E7ED5C for ; Wed, 8 Aug 2012 11:21:44 +0200 (CEST) Received-SPF: None (mail1-smtp-roc.national.inria.fr: no sender authenticity information available from domain of arnaud.spiwack@gmail.com) identity=pra; client-ip=209.85.213.54; receiver=mail1-smtp-roc.national.inria.fr; envelope-from="arnaud.spiwack@gmail.com"; x-sender="arnaud.spiwack@gmail.com"; x-conformance=sidf_compatible Received-SPF: Pass (mail1-smtp-roc.national.inria.fr: domain of arnaud.spiwack@gmail.com designates 209.85.213.54 as permitted sender) identity=mailfrom; client-ip=209.85.213.54; receiver=mail1-smtp-roc.national.inria.fr; envelope-from="arnaud.spiwack@gmail.com"; x-sender="arnaud.spiwack@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-yw0-f54.google.com) identity=helo; client-ip=209.85.213.54; receiver=mail1-smtp-roc.national.inria.fr; envelope-from="arnaud.spiwack@gmail.com"; x-sender="postmaster@mail-yw0-f54.google.com"; x-conformance=sidf_compatible X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: AqICAKYuIlDRVdU2k2dsb2JhbABFhXyrFgGITggiAQEBAQkJCwkUBCOCIAEBAQMBEgIPBBkBEiYBAwELAQUDAgsHMAICIQESAQUBDg4GEyKHXAMGBgucGwkDi1SDQIViJw2JSAEFDIofZCCFLoESA5N1gVOBFIl1gyY+gVSCLIFV X-IronPort-AV: E=Sophos;i="4.77,732,1336341600"; d="scan'208";a="169371389" Received: from mail-yw0-f54.google.com ([209.85.213.54]) by mail1-smtp-roc.national.inria.fr with ESMTP/TLS/RC4-SHA; 08 Aug 2012 11:21:43 +0200 Received: by yhfs35 with SMTP id s35so804485yhf.27 for ; Wed, 08 Aug 2012 02:21:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:sender:in-reply-to:references:from:date :x-google-sender-auth:message-id:subject:to:cc:content-type; bh=mYrVXMHQRGqPkif/o0e8QQf+MUbQVTmUj0RxuVaDlgE=; b=CiXUv84OGL/PZbnBCoBhjqUfsynrxgNI2hvDM6swB39b86UgRw8A31rTZSp8iS9479 KxYDJkKNj5P3kJsGMxdY1bHTi/nI6JQnzbH76O+M7iBplIbL44vAvBiJAav1Nkk5TJNy 8Kdld0OU3GiUf/klPe2Jbwmy1lix/3PaPFH2AINKHu0Jz+t6h5jEZymmnzJyyB7/RpQ0 lvghyQ4NsQyQ3jJ8GxVERa46WCo4n7+NFA0S4oz6B5xd0stTe0DNjv1qp7OUsXkF5de4 osYX1zODT0gxJNrXkVsC/ik+taske1rriD8KWGCQNdjx+qkmxtP1jjeZsrEYzjysPmLH SGWQ== Received: by 10.50.182.231 with SMTP id eh7mr239425igc.42.1344417702202; Wed, 08 Aug 2012 02:21:42 -0700 (PDT) MIME-Version: 1.0 Sender: arnaud.spiwack@gmail.com Received: by 10.64.106.231 with HTTP; Wed, 8 Aug 2012 02:21:02 -0700 (PDT) In-Reply-To: References: From: Arnaud Spiwack Date: Wed, 8 Aug 2012 11:21:02 +0200 X-Google-Sender-Auth: OULZBlIi6pBfecfKzJZmjhSnzDk Message-ID: To: Anthony Tavener Cc: caml-list@inria.fr Content-Type: multipart/alternative; boundary=14dae93406eb91cecd04c6bda31e Subject: Re: [Caml-list] A use-case for first-class modules... out of curiosity is there another way? --14dae93406eb91cecd04c6bda31e Content-Type: text/plain; charset=UTF-8 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 > > --14dae93406eb91cecd04c6bda31e Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable In this particular case this is just a case of existentially quantified. Yo= u 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 re= ad all about it here : h= ttp://caml.inria.fr/pub/ml-archives/caml-list/2004/01/52732867110697f556507= 78d883ae5e9.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

=C2=A0 (* Signature for a single 'table' in t= he database, elements of type t. *)
=C2=A0 module type S =3D sig<= /div>
=C2=A0 =C2=A0 (* Full implementation relies on a Key module for different k= ey types than 'int' *)
=C2=A0 =C2=A0 type t
=C2= =A0 =C2=A0 val get : int -> t
=C2=A0 =C2=A0 val set : int ->= ; t -> unit
=C2=A0 =C2=A0 val del : int -> unit
=C2=A0 =C2=A0 val iter : (int -> t -> unit) -> unit
=C2=A0 =C2=A0 val fold : (int -> t -> 'a -> 'a) -> = 9;a -> 'a
=C2=A0 end

=C2=A0 (* In= stantiates storage for a table, and returns FC module to interact with the = store. *)
=C2=A0 let create_with_default (type s) ?(size=3D19) default =3D
=
=C2=A0 =C2=A0 (* Full implementation is parameterized by Key and Table= modules *)
=C2=A0 =C2=A0 let h =3D Hashtbl.create size in
<= div>=C2=A0 =C2=A0 let module H =3D struct
=C2=A0 =C2=A0 =C2=A0 type t =3D s
=C2=A0 =C2=A0 =C2=A0 let g= et (id:int) =3D=C2=A0
=C2=A0 =C2=A0 =C2=A0 =C2=A0 try Hashtbl.fin= d h id
=C2=A0 =C2=A0 =C2=A0 =C2=A0 with Not_found -> default
=C2=A0 =C2=A0 =C2=A0 let set id (v:t) =3D Hashtbl.replace h id v
=C2=A0 =C2=A0 =C2=A0 let del id =3D Hashtbl.remove h id
=C2=A0 =C2=A0 =C2=A0 let iter f =3D Hashtbl.iter f h
=C2=A0 = =C2=A0 =C2=A0 let fold f init =3D Hashtbl.fold f h init
=C2=A0 = =C2=A0 end in
=C2=A0 =C2=A0 (module H : S with type t =3D s)

end

(* An example table... *)=
module Location =3D struct
=C2=A0 let unknown =3D "Unkn= own"
=C2=A0 include (val (Db.create_with_default unknown) : = S with type t =3D string)
=C2=A0 (* Location might have a bunch o= f other functionality 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.

=C2=A0-Tony

--14dae93406eb91cecd04c6bda31e--