On 6 March 2012 22:22, Eliot Miranda <
eliot.miranda@gmail.com> wrote:
> Hi Frank,
>
> On Tue, Mar 6, 2012 at 1:49 PM, Frank Shearar <
frank.shearar@gmail.com>
> wrote:
>>
>> On 6 March 2012 18:27, Eliot Miranda <
eliot.miranda@gmail.com> wrote:
>> >
>> >
>> > On Tue, Mar 6, 2012 at 3:05 AM, Henrik Johansen
>> > <
henrik.s.johansen@veloxit.no> 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).