caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
From: Alain Frisch <alain.frisch@lexifi.com>
To: Christophe Papazian <christophe.papazian@gmail.com>
Cc: Caml List <caml-list@inria.fr>,
	 Sebastien Briais <sebastien.briais@lexifi.com>
Subject: Re: [Caml-list] Memoize GADT
Date: Wed, 06 Feb 2013 10:50:10 +0100	[thread overview]
Message-ID: <51122752.9040503@lexifi.com> (raw)
In-Reply-To: <A0D04133-A54F-458E-A584-8139F90C2711@gmail.com>

We have encountered exactly the same problem recently.  A slightly more 
general version of your question is how to memoize a function of type

   'a t -> 'a s

for two type parametrized constructors t and s (and similarly with 
higher arities).  We want an hash table which can hold key/value 
bindings ('a t * 'a s) for any 'a.  The equality of keys must be such 
that when (key1 : 'a t) is equal to (key2 : 'b t), we can deduce that 'a 
and 'b are the same type (i.e. the value must hold enough information to 
deduce the type from the content), which guarantees that 'a s and 'b s 
are also the same type (and so the existing value associated to key1 can 
be returned for key2).  This assumption often holds when 'a t is a GADT 
representing "expressions which evaluate to values of type 'a".

Our solution (implemented by my colleague Sebastien, in Cc:) has been to 
create a functor with the following signature:

======================================================================
module type ParametricType = sig
   type 'a t
end

module ParametricEquality : sig
   type (_, _) t =
     | Eq: ('a, 'a) t
     | Ne: ('a, 'b) t
end

module type ParametricHashedType = sig
   type 'a t
   val equal: 'a t -> 'b t -> ('a, 'b) ParametricEquality.t
   val hash: 'a t -> int
end

module ParametricHashtbl : sig
   module type S = sig
     type 'a key
     type 'a value
     type binding = Binding: 'a key * 'a value -> binding
     type t
     val create: int -> t
     val clear: t -> unit
     val reset: t -> unit
     val copy: t -> t
     val add: t -> 'a key -> 'a value -> unit
     val remove: t -> 'a key -> unit
     val find: t -> 'a key -> 'a value
     val find_all: t -> 'a key -> 'a value list
     val replace: t -> 'a key -> 'a value -> unit
     val mem: t -> 'a key -> bool
     val length: t -> int
     val iter: (binding -> unit) -> t -> unit
     val fold: (binding -> 'a -> 'a) -> t -> 'a -> 'a
   end
   module Make(X:ParametricHashedType)(Y:ParametricType): S with type 'a 
key = 'a X.t and type 'a value = 'a Y.t
end
======================================================================

Note that the first input of the function (ParametricHashedTyped) looks 
like the standard argument to Hashtbl.Make, except that the equality 
function returns a dynamic witness of equality between 'a and 'b in case 
of equality between two values of types 'a t and 'b t.  This is 
implemented using a GADT.  We also use a GADT to introduce an 
existential on 'a for key/value pairs of type ('a t * 'a s) to be used 
for iterators (iter/fold).  Instead we could have defined those 
iterators as taking a polymorphic function as argument (through 
polymorphic methods or record fields, or with a first-class module), but 
they would have been more heavy to use on the call site (although this 
would avoid the allocation of the Binding constructor).

On the implementation side, there are three choices:

  - Create a type-safe implementation by instantiating Hashtbl, and 
store values which keep a copy of the key, with an existential both on 
keys and on values.  This requires extra comparisons and boxing. (Good 
exercise left to the reader!)

  - Copy and adapt the implementation of Hashtbl, which can avoid some 
of the extra comparisons. (Painful!)

  - The quicker way (and the one we have chosen for now) is to 
instantiate Hashtbl on type Obj.t for keys and values, and using unsafe 
operations (Obj.magic) to cast its result to the signature above.  I 
don't think it is good style to share here code using Obj.magic, so I 
will refrain from doing so :-)

We have done the same for Set, Map, association lists and Weak (for 
such, I believe, the unsafe implementation is the only choice, because 
if we wrap the values with freshly allocated existential constructors, 
they can immediately be garbage-collected).

It would probably make sense to provide such functors as part of the stdlib.



Alain







On 02/06/2013 09:19 AM, Christophe Papazian wrote:
> Hi,
>
> this must be a very basic question for some of you, sorry for the inconvenience :
>
> When I have function "f" of type (a -> b), I can easily add a layer to that function to memoize the result by using a (a,b) Hashtbl.t to store the results of the expensive to compute "f".
>
> let mf = let e = Hashtbl.create 0 in ( fun x -> try Hashtbl.find e x with Not_found -> let res = f x in Hashtbl.add e x res; res )
>
> But now, I have a function "g"
> 	let g (type a) : a gadt -> a = ....
>
> And If I apply the same method, type a becomes weak (_'a).
>
> Is there something simple to memoize that function as easy as the previous example and keep its polymorphism ? I think not, but I hope to be wrong.
>
> Thanks
>
> 	Christophe
>
>


  reply	other threads:[~2013-02-06  9:50 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-02-06  8:19 Christophe Papazian
2013-02-06  9:50 ` Alain Frisch [this message]
2013-02-06  9:55 ` Gabriel Scherer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=51122752.9040503@lexifi.com \
    --to=alain.frisch@lexifi.com \
    --cc=caml-list@inria.fr \
    --cc=christophe.papazian@gmail.com \
    --cc=sebastien.briais@lexifi.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).