[plt-scheme] serialization: transient fields
Matthew Flatt wrote:
> At Wed, 09 Aug 2006 22:49:56 -0400, Jon Rafkind wrote:
>
>> I quickly realized this doesnt work with deserialization. The exact
>> problem, if it isn't obvious, seems to be the class body is never
>> evaluated and so any constructors or definitions are not called when the
>> object is deserialized.
>>
>
> It's certainly true that constructors are not called by deserialization
> (except for instances of externalizable<%>, in which case the
> constructor is always called with no initialization arguments).
>
> I'm pretty sure that `define/transient' works like `transient' in Java.
> Maybe you want to perform some work at deserialization time to restore
> values in transient fields?
>
>
Serialization, as it is implemented now in class.ss/serialize.ss, works
the same as Java's serialization but in Java you can hook into the
serialization mechanism by defining readObject()
private void readObject( ObjectInputStream in ){
in.defaultReadObject();
callSomeMethod(); // object is already read in from the stream, do
some arbitrary processing
}
Whereas in mzscheme once serialization occurs you get an object and must
call a method after it is returned to you:
(send (deserialize stream) some-method)
Which isnt very convenient. What would be nice is if you could call a
default-internalize method as well:
(define (internalize v) (begin (default-internalize v) (some-method)))
>> I tried changing the transient definition so
>> that the internalize method would always create the transient class with
>> the proper value, that value passed as v to (define t-id v).
>>
>>
>> (define-serializable-class* transient% object% (externalizable<%>)
>> (init-field [val #f] [create-thunk (lambda () (void))])
>> (define/public (externalize) #f)
>> (define/public (internalize v) (create-thunk))
>> (super-new))
>>
>
> The `internalize' method is called on an object instantiated with no
> initialization arguments. Thus, `create-thunk' above lways has its
> default value when called via `internalize'.
>
> Any information that you want to communicate from serialization to
> deserialization must be in the result from `externalize', and then used
> via the argument to `internalize'. Also, the result from `externalize'
> must be serializable.
>
> So, you can create a variant of `transient%' that keeps an object
> reference instead of a closure. The `externalize' method could return
> that reference instead of #f. Finally, the `internalize' method could
> use the object, supplied as `v', to re-initialize `val'.
>
> (define-serializable-class* transient% object% (externalizable<%>)
> (init-field [val #f] [restorer #f])
> (define/public (externalize) restorer)
> (define/public (internalize v)
> (set! restorer v)
> (set! val (send restorer restore-value)))
> (super-new))
>
>
Thats a nice idea but I was unable to implement it in a reasonable way.
In the end I abandonded serializing entire class's and just serialize
the x/y components since thats all I really cared about anyway.
(define/public (externalize)
(list (get-field x this)
(get-field y this)))
As for the dealing with transient member fields I call (constructor),
which all my classes have, in the `internalize' method of the root
class( the one with x/y in it ), so subclasses have a chance to
initialize fields.