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
