On Wed, Mar 7, 2012 at 2:10 AM, Frank Shearar <[email protected]>wrote:

> On 6 March 2012 22:22, Eliot Miranda <[email protected]> wrote:
> > Hi Frank,
> >
> > On Tue, Mar 6, 2012 at 1:49 PM, Frank Shearar <[email protected]>
> > wrote:
> >>
> >> On 6 March 2012 18:27, Eliot Miranda <[email protected]> wrote:
> >> >
> >> >
> >> > On Tue, Mar 6, 2012 at 3:05 AM, Henrik Johansen
> >> > <[email protected]> wrote:
> >> >>
> >> >>
> >> >> On Mar 6, 2012, at 12:03 PM, Henrik Johansen wrote:
> >> >>
> >> >> >>
> >> >> >
> >> >> > Parcels
> >> >> > - Check a hash based on class layout vs equivalent hash for the
> >> >> > stored
> >> >> > method's class (stored with the method).
> >> >> > If different:
> >> >> > 1) check for existence of a backwards-compatability reader method,
> >> >> > hand
> >> >> > the old instance to it, and expect the old instance to be #become'd
> >> >> > into
> >> >> > something current,
> >> >> > 2) raise an error if no such method exists.
> >> >> >
> >> >> > Now, the main problem with this scheme is it's left as an exercise
> to
> >> >> > the user to come up with a procedure to ensure said
> backwards-compat
> >> >> > reader
> >> >> > method stays up to date as additional changes are made.
> >> >> >
> >> >> > Is it a big problem?
> >> >> > Depends on your tests, and programmer diligence at any given day.
> >> >> >
> >> >> > Added inst vars are not a big deal, as if you forget them, there
> will
> >> >> > usually be a nil #DNU somewhere down the line. (or you use lazy
> >> >> > initialization, and everything works as expected even for existing
> >> >> > instances)
> >> >> >
> >> >> > Removals/reorderings are a bigger issue, as the detection of
> failure
> >> >> > (inst vars in wrong slots) are often far removed from the source of
> >> >> > problems
> >> >> > (forgetting to update the reader method),
> >> >> > If seldomly used, it may even go unnoticed until the instance is
> next
> >> >> > saved, at which time you're in real trouble (especially if saved
> >> >> > alongside
> >> >> > newly created ones, in which case there is no consistency).
> >> >> >
> >> >> > In general I think it's an ok scheme, but would like to see a
> >> >> > solution
> >> >> > more resilient to user-error.
> >> >> > How to achieve that is an interesting topic, which I haven't yet
> >> >> > found
> >> >> > time to think through as thoroughly as I had wished/intended some
> >> >> > months
> >> >> > ago. :(
> >> >>
> >> >> Errr, BOSS, not Parcels.
> >> >
> >> >
> >> > Right. What Parcels do is shape-change instances *and* rescan methods
> to
> >> > fix
> >> > up inst var offsets.  When a parcel is saved the "signature" of
> classes
> >> > of
> >> > instances there-in are saved so that the parcel contains all the inst
> >> > var
> >> > names of the class and its superclass.  If on load the superclass
> chain
> >> > has
> >> > a different set of inst vars then lost inst vars are omitted and added
> >> > inst
> >> > vars are nil in the materialized instances.  If a Parcel contains a
> full
> >> > class (not just instances) that has methods, and the superclass chains
> >> > inst
> >> > vars have changed  then these methods are "rescanned" (disassembled,
> and
> >> > reassembled, not using the compiler) and inst var offsets are fixed
> up,
> >> > missing inst vars getting changed into undeclared variable references
> (I
> >> > think; at least this is what *should* happen; its what happens when
> one
> >> > removes an inst var that is still referenced from methods).
> >> >
> >> > What's missing in the parcel scheme is any hook to allow the user to
> >> > process
> >> > shape-changing instances with the values of lost inst vars in hand.
> >> >  Presumably there will be cases when those values are essential to any
> >> > schema migration.  Personally I would want to decouple schema
> migration
> >> > and
> >> > add it as a post-processing step, which would imply that the
> >> > materializer
> >> > would offer a service (materialize in a special mode) where it
> >> > constructed a
> >> > dictionary from materialized and shape-changed instance to in parcel
> >> > state
> >> > (e.g. an Array of inst var values as they occurred in the parcel).
>  Then
> >> > the
> >> > materialized instance could be migrated after the fact, with the
> default
> >> > behaviour being analogous to what the system does now on class
> >> > redefinition
> >> > (values of deleted inst vars are lost, aded inst vars are nil).
> >>
> >> This sounds a bit like CLOS'  UPDATE-INSTANCE-FOR-REDEFINED-CLASS [1],
> >> which is a generic function (for our purposes just think "something a
> >> bit like a method") that you define and is invoked by the machinery
> >> involved in changing/redefining classes.
> >>
> >> A colleague told me about this part of CLOS the other day when
> >> discussing class-based OO: it's the sort of thing we need in at least
> >> the Browser, so one can change the shape of existing instances and
> >> ensure the new class invariants are maintained.
> >
> >
> > But Smalltalk has done without this for 40 years.  It has supported
> existing
> > instance redefinition but no-one has felt the need to provide a scheme
> for
> > redefinition in the IDE.  In any case it is relatively trivial to script
> it,
> > e.g.:
> >
> > | instanceState |
> > instanceState := IdentityDictionary new.
> > myClass allInstancesDo: [:i| instanceState at: i put: ((1
> > to: myClass instSize) collect: [:ivi| i instVarAt: ivi])].
> > myClass superclass subclass: myClass name instanceVariableNames: myClass
> > instVarNamesString, ' theExtraInstVar' [...].
> > myClass compile: sourceForupdateToNewFormatFrom.
> > myClass allInstancesDo: [:i| i updateToNewFormatFrom: (instanceState at:
> i]
> >
> > So if and when you need it you can roll your own (and I've never needed
> it;
> > but I have wanted the refactoring browser to preserve inst vars on push
> > up/push dwn).
>
> Indeed, it is trivial. Using #allInstancesDo: is exactly what I used
> when my colleague and I were discussing the issue. The only difference
> is that _I_ did the migration, not my tools.
>
> > However, collections of persistent objects are a different kettle of
> fish.
> >  It is much more likely to need instance migration on load, especially
> for
> > library objects that the user is merely using and whose implementation
> has
> > evolved over time (e.g. UI elements such as morphs).
> >
> >>
> >> (For instance, for
> >> "purely functional" objects, you really don't want to use reflection
> >> to initialise new instvars from outside the object.)
> >
> >
> > But the class builder uses an unprotected interface (instVarAt: and
> > instVarAt:put:).  So this turns out not to be an issue.
>
> That's exactly to what I was referring: I lump #instVarAt: in the
> reflection bucket. I realise its _utility_, but it's strong medicine.
> Which is pretty much why you're pushing mirrors, right? So that some
> things - the class builder, as an example - can make use of such a
> (powerful and dangerous) tool while some random library can't.
>

Certainly one can't have security and unfettered reflection, and hence
mirrors are a way of providing reflection safely.  But the complications a
full mirror framework introduce shouldn't be underestimated.  And how
mirrors are made available safely is something I don't yet fully
understand.  In an IDE setting they're pretty much freely available so for
me I'm not convinced of the utility.  Deployment is another thing
altogether.

However, albeit a small point, I do think that light-weight mirror methods
like object:instVarAt:put: are the interface the ClassBuilder should use,
not instVarAt: on instances.  The latter breaks with proxies, and the
change from one to the other is pretty trivial.


>
> frank
>
> >> So you'd change
> >> the class definition, supply a way of handling the schema migration,
> >> and then the image would change the class definition and run the
> >> migration.
> >>
> >> frank
> >>
> >> [1] http://clhs.lisp.se/Body/f_upda_1.htm
> >>
> >> > This reminds me of a bug with the ClassBuilder/RefactoringBrowser
> >> > combination.  If one refactors pushing an inst var up to a superclass
> or
> >> > down to subclasses, the values of te inst var in instances are lost
> >> > because
> >> > the class change is in fact done as two changes, a deletion followed
> by
> >> > an
> >> > addition.  This again could be fixed in a wrapper, building a
> dictionary
> >> > from instance to values, performing the set of class changes, and then
> >> > restoring the inst var state from the dictionary.
> >> >
> >> >
> >> >>
> >> >> My bad.
> >> >>
> >> >> Cheers,
> >> >> Henry
> >> >>
> >> >>
> >> >
> >> >
> >> >
> >> > --
> >> > best,
> >> > Eliot
> >> >
> >>
> >
> >
> >
> > --
> > best,
> > Eliot
> >
>
>


-- 
best,
Eliot

Reply via email to