From: Robert Klemme Date: 2009-03-03T22:23:46+09:00 Subject: Re: Marshal.load does not create new instances? On 03.03.2009 14:00, Rick DeNatale wrote: > [Note: parts of this message were removed to make it a legal post.] > > On Tue, Mar 3, 2009 at 2:29 AM, Robert Klemme wrote: > >> On 03.03.2009 04:38, Mike Gold wrote: >> >>> Robert Klemme wrote: >>> >>>> I believe he wants to evolve the class and be able to load data >>>> written with an older (or just different) version of the class. And >>>> now Ian hits the usual problems of schema migration. >>>> >>>> Ian, you should be aware of one thing: class definitions are not >>>> serialized - no programming language that I know does this. >>>> >>> .. except languages in which code and data are equivalent! >>> >>> Sorry, I had to bite. >>> >> Ouch! ;-) >> >> This is a great example of the power of code-data equivalence. If you >>> store the definitions, things will just work. >>> >> Well, *certain* things will just work. But you'll trade this for different >> issues. For example, all of a sudden you can have different implementations >> of the same class coexist. I wouldn't say that one or the other solution is >> necessarily easier. They both do not change the complexity of the >> underlying problem (evolution of code with data artifacts belonging to >> different versions). Both approaches (i.e. storing code and not storing >> code) make certain things easy and other things hard. > > Ruby has some subtleties in this area when compared to other OO languages. Don't they all have? ;-) > Mike Gold introduced the idea that this was a problem in dealing with schema > migration. To me this implies dealing with layout changes to the object. > This is a problem in most languages like Java, C++ and Smalltalk where > classes, along with whatever other language specific roles they play, act as > a template for understanding which instance variable goes where in a reified > instance. > > This means that if you marshal an object then match it up to a class with > the same template, you run into the danger of misinterpreting the state of > the object. In systems written in these languages, you might be able to get > away with two different versions of a Class which have the same instance > layout template but vary in method implementations, or even have slightly > different method repertoires. > > Ruby falls into the class of languages where classes DON'T act as templates, > instead instance variables are dynamically bound to each instance with a > run-time lookup used to map instance variable names to location. > > So the OP's case shows that you can marshal Ruby objects and the 'schema' is > carried with each object. It's just that accessor methods don't go along. Yes, this is true and it allows to cope with at least some migrations which might be enough for many practical purposes. But strictly speaking this situation is not really better than that of other languages: while this property of Ruby allows for successful deserialization, you can break a class's invariant (as manifested in the implementation of methods) with this, rendering deserialized instances completely unusable. > As it turns out, the MagLev project is trying to figure out how to deal with > a similar problem right now. In Gemstone Smalltalk, which is the code base > on which MagLev is being built, classes and instances are all held in a > shared persistant store. When a process changes a class, and commits a > transaction, other processes see the change when the results of the > transaction become visible to them (i.e. when they start up, or commit or > abort a transaction of their own). > > Now, this was apparently the same model they were planning to follow for > MagLev. However, we had some discussions in the beta-testers forum about how > this might or might not work with many Ruby programs because the Ruby > execution model builds up classes at run time from a known initial state, > and classes change as the code executes, either through 'normal' class > method definition (both of which are execution time events in Ruby) or > through various levels of metaprogramming sophistication. > > Because Ruby classes get built incrementally at run-time, the order of > execution can be important, so starting with a persisted initial set of > class definitions can be problematic at times. > > So currently MagLev allows independent control over whether or not a > transaction commit persists changes to class definitions. A process needs to > explicitly indicate that it want's to put class definition changes into the > state to be committed before committing. > > We'll see how this evolves. Thank you for the abstract, Rick. This sounds interesting. Your explanation is a nice demonstration of the complexity of the problem I was talking about. :-) Kind regards robert