caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
From: Jeremy Yallop <jeremy.yallop@ed.ac.uk>
To: Christopher L Conway <cconway@cs.nyu.edu>
Cc: caml-list@inria.fr
Subject: Re: [Caml-list] Immutable cyclic data structures via a
Date: Sat, 13 Dec 2008 23:30:10 +0000	[thread overview]
Message-ID: <49444582.9070004@ed.ac.uk> (raw)
In-Reply-To: <loom.20081213T213803-424@post.gmane.org>

Christopher L Conway wrote:
> --- In ocaml_beginners@yahoogroups.com, Andrew Myers <andru@...> wrote:
>> type node = edge list
>> and edge = {from: node; to_: node}
>>
>> You'll have trouble initializing that data structure, though. Maybe
>> better to add mutability somewhere, e.g.
>>
>> type node = {mutable outgoing: edge list}
>> and edge = {from: node; to_: node}
>>
>> Then you can create all the nodes first and backpatch them with all the
>> edges.
> 
> This makes me wonder: if you add mutable fields to a type in order to
> build a cyclic data structure, but you intend to use the data
> structure immutably once it is built, is there any way to "freeze" the
> value, producing an immutable cyclic structure? This is a common
> pattern, similar to Bloch's Builder pattern for Java.[1]

I believe that private types can do what you want.  Private record
types allow only read access, even to mutable fields, so if you have a
type

   type node = private { mutable outgoing: edge list }

then you can't update the "outgoing" field of any values of type node,
even though it's declared mutable.  You can then create a module which
exports such a declaration in the signature:

   module Graph :
   sig
     type node = private { mutable outgoing : edge list }
     and  edge =         { from : node ; to_ : node }
   end =
   struct
     type node =         { mutable outgoing : edge list }
     and  edge =         { from : node ; to_ : node }
     ...
   end

Now you can make use of the mutability of the field within the
implementation of the module (marked "...") but not outside the
module: the module boundary acts as the "freeze" operation that
converts mutable to immutable structures.

Of course, this isn't particularly satisfactory, since you have to
either anticipate all possible graph values when you write the module,
or provide a clunky interface for constructing cyclic graphs.  What
you really want is to provide two versions of the graph type, both of
which are visible to clients of the module, and an explicit "freeze"
operation for converting mutable graphs to immutable graphs.  I
believe that the following code meets all your requirements, although
it may be possible to make it more succinct.

   module Graph :
   sig
     module Mutable :
     sig
       type node =         { mutable outgoing : edge list }
       and  edge =         { from : node ; to_ : node }
     end

     module Immutable :
     sig
       type node = private { mutable outgoing : edge list }
       and  edge =         { from : node ; to_ : node }
     end

     module Freeze :
     sig
       val node : Mutable.node -> Immutable.node
       val edge : Mutable.edge -> Immutable.edge
     end
   end =
   struct
     module Graph =
     struct
       type node = { mutable outgoing : edge list }
       and  edge = { from : node ; to_ : node }
     end
     module Mutable   = Graph
     module Immutable = Graph
     module Freeze =
     struct
       let node n = n
       let edge e = e
     end
   end

Now you can, outside the module, create cyclic values using mutability:

   open Graph.Mutable

   let medge = {
     from = {outgoing = []};
     to_  = {outgoing = []};
   }
   let () = medge.from.outgoing <- [medge]
   let () = medge.to_ .outgoing <- [medge]

and then freeze them so that they can no longer be updated.

   let iedge = Graph.Freeze.edge medge

(Of course, you can still update the graph via the "medge" binding,
but it's easy enough to prevent access to that, if necessary.)

Hope this helps,

Jeremy.

-- 
The University of Edinburgh is a charitable body, registered in
Scotland, with registration number SC005336.


  reply	other threads:[~2008-12-13 23:24 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-12-13 21:45 Christopher L Conway
2008-12-13 23:30 ` Jeremy Yallop [this message]
2008-12-13 23:43   ` [Caml-list] " Jeremy Yallop
2008-12-14  1:47 ` Edgar Friendly
2008-12-14  9:10   ` Francois Pottier
2008-12-14 14:29     ` blue storm
2008-12-14 20:06 ` Richard Jones

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=49444582.9070004@ed.ac.uk \
    --to=jeremy.yallop@ed.ac.uk \
    --cc=caml-list@inria.fr \
    --cc=cconway@cs.nyu.edu \
    /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).