On Tue, 17 Feb 2009, TSa wrote:
> I fully agree that immutability is not a property of types in a signature.
> But a signature should have a purity lock :(Int $i is pure) that snapshots
> an object state
> Note that this purity lock doesn't lock the outer object. It is only
> affecting the inner scope controlled by the signature. So there have to be
> extra means of snapshot generation if a mutable object shall be given e.g.
> to multiple invocations with the same snapshot state. This is no problem
> for code downstream of a purity lock because snapshotting a pure object is
> a no-op.
I like the way this is going; ".snapshot" works like ".deepclone" when
you have an ordinary object, but like ".self" on one that already has a
purity lock, OR any time you can prove that the object is actually at
its end of life (*1).
An important class of optimizations in the implementation of pure functional
languages is the destructive re-use of object that have reached their end of
life according to the data-flow analysis; they can be reused to hold some
subsequent value, most typically by doing an in-place update.
So you need an operation that changes a snapshot back into live object.
It's also going to be important that "snapshot" do as little work as
possible even when objects get handed around by impure code; to this end it
would be really useful to be able to pass snapshots to impure code and
guarantee that they won't get mangled, so they can later be passed to more
pure code (*2).
Question: does/should MMD differentiate between :ro and :rw parameters or
invocants that are otherwise identical?
*1: actually it's a bit more complicated; a mutable object can be re-tagged
as immutable iff it's at end-of-life according to data-flow analysis AND all
its contained sub-objects are either already immutable or can in turn be
re-tagged as immutable. Otherwise it has to make a shallow clone, and the
process repeated recursively for all contained mutable objects.
*2: There are at least three ways of doing this:
1. When re-tagging an immutable object as mutable, make a deep clone.
2. When re-tagging an immutable object as mutable, make a shallow clone.
Any time subsequently an immutable sub-object is about to be used an a
potentially mutating way, re-tag or clone it too.
3. Wrap the object in a new proxy (*3) object, that defers the cloning
until its actually needed. The big win on this is that if you subsequently
"snapshot" the proxy, it just gives you back the original object.
Possibly methods 2 & 3 can be combined into a self-mutating proxy that
becomes the clone when needed.
*3: I call it a proxy because it defers all non-mutating method calls to the
original object, and for the mutating ones, does the clone thing and then
defers the method to the clone. Which might be itself.