[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

marshalling constants

Brian Buckley

2/7/2006 7:38:00 PM

Hello,

When one marshals a constant and later unmarshals it, a brand new object
appears to be created during unmarshalling. This seems wasteful, especially
in cases when constants are large (contain many attributes). Is it possible
to customize marshal for a class (or for singleton objects of the class) so
that unmarshalling a constant simply returns the already existing constant
rather than creating a new one? I've noticed Fixnum's exhibit this
behavior.

--Brian

class A;
A1 = A.new
end

before = A::A1
after = Marshal.load(Marshal.dump(before)) #marshal and back
puts before == after #darn -- it is false
puts before.object_id == after.object_id #same here

# it works for Fixnums
before = 123
after = Marshal.load(Marshal.dump(before))
puts before == after # true
puts before.object_id == after.object_id #also true
11 Answers

David Vallner

2/7/2006 8:39:00 PM

0

That's because Fixnums are immediate values, and thus they compare by value.
You can't possibly have two distinct Fixnum objects representing for example
123 short of doing some very sick core interpreter hacks.

The comparing of the objects fails, because by default, objects compare by
reference unless overridden.

Also, Marshal also has completely no way of telling if the object is a
constant or not. For that matter, I don't think there even is such a thing as
a constant object in Ruby, you can only design objects as immutable, and have
constant object _references_.

I also don't think Marshal is even supposed at all to return completely the
same object if possible on loading, it's supposed to recreate an object with
the same structure (optionally between two runs of an interpreter). If
anything, the change you propose might end up breaking code that could use
Marshal for simple deep copying of Objects as is common in Java / .NET. (I
don't recall at the moment if there's any Ruby-specific deep-copy
functionality)

David Vallner

Dna Utorok 07 Február 2006 20:38 Brian Buckley napísal:
> Hello,
>
> When one marshals a constant and later unmarshals it, a brand new object
> appears to be created during unmarshalling. This seems wasteful,
> especially in cases when constants are large (contain many attributes). Is
> it possible to customize marshal for a class (or for singleton objects of
> the class) so that unmarshalling a constant simply returns the already
> existing constant rather than creating a new one? I've noticed Fixnum's
> exhibit this behavior.
>
> --Brian
>
> class A;
> A1 = A.new
> end
>
> before = A::A1
> after = Marshal.load(Marshal.dump(before)) #marshal and back
> puts before == after #darn -- it is false
> puts before.object_id == after.object_id #same here
>
> # it works for Fixnums
> before = 123
> after = Marshal.load(Marshal.dump(before))
> puts before == after # true
> puts before.object_id == after.object_id #also true


Joel VanderWerf

2/7/2006 8:39:00 PM

0

Brian Buckley wrote:
> Hello,
>
> When one marshals a constant and later unmarshals it, a brand new object
> appears to be created during unmarshalling. This seems wasteful, especially
> in cases when constants are large (contain many attributes). Is it possible
> to customize marshal for a class (or for singleton objects of the class) so
> that unmarshalling a constant simply returns the already existing constant
> rather than creating a new one? I've noticed Fixnum's exhibit this
> behavior.
>
> --Brian
>
> class A;
> A1 = A.new
> end
>
> before = A::A1
> after = Marshal.load(Marshal.dump(before)) #marshal and back
> puts before == after #darn -- it is false
> puts before.object_id == after.object_id #same here

class A
A1 = new

def self.name_for_constant obj
@name_for_constant ||= {}
@name_for_constant[obj] ||= constants.find {|c| const_get(c) == obj}
end

def _dump(limit)
self.class.name_for_constant(self)
end

def self._load(str)
const_get str
end
end

before = A::A1
after = Marshal.load(Marshal.dump(before)
puts before == after # ==> true
puts before.object_id == after.object_id # ==> true


--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407


David Vallner

2/7/2006 8:59:00 PM

0

Dna Utorok 07 Február 2006 21:39 Joel VanderWerf napísal:
> Brian Buckley wrote:
> class A
> A1 = new
>
> def self.name_for_constant obj
> @name_for_constant ||= {}
> @name_for_constant[obj] ||= constants.find {|c| const_get(c) == obj}
> end
>
> def _dump(limit)
> self.class.name_for_constant(self)
> end
>
> def self._load(str)
> const_get str
> end
> end
>
> before = A::A1
> after = Marshal.load(Marshal.dump(before)
> puts before == after # ==> true
> puts before.object_id == after.object_id # ==> true

Ignore the nonsensical rant that follows after the explanation about the
Fixnums, I thought Brian wanted to change Marshal code for some reason.

David Vallner


Joel VanderWerf

2/7/2006 9:06:00 PM

0

David Vallner wrote:
> Ignore the nonsensical rant that follows after the explanation about the
> Fixnums, I thought Brian wanted to change Marshal code for some reason.
>
> David Vallner
>

Your post seemed fine to me. It's true that normally dump/load should do
a deep copy. But in some cases you want to make an exception...

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407


Brian Buckley

2/7/2006 9:38:00 PM

0

>
>
> class A
> A1 = new
>
> def self.name_for_constant obj
> @name_for_constant ||= {}
> @name_for_constant[obj] ||= constants.find {|c| const_get(c) == obj}
> end
>
> def _dump(limit)
> self.class.name_for_constant(self)
> end
>
> def self._load(str)
> const_get str
> end
> end



Whoa. I see it works (for the A constants only as written, getting
TypeErrors on A non-constants). I don't understand why it works just yet.
Will examine further tonight.

Eric Hodel

2/7/2006 9:40:00 PM

0

On Feb 7, 2006, at 11:38 AM, Brian Buckley wrote:

> Hello,
>
> When one marshals a constant and later unmarshals it, a brand new
> object
> appears to be created during unmarshalling. This seems wasteful,
> especially
> in cases when constants are large (contain many attributes). Is it
> possible
> to customize marshal for a class (or for singleton objects of the
> class) so
> that unmarshalling a constant simply returns the already existing
> constant
> rather than creating a new one? I've noticed Fixnum's exhibit this
> behavior.

How do you know that a Marshal string is identical to a constant
without loading it?

Note that Ruby doesn't really have constants. Instead it has frozen
objects which aren't quite the same thing.

$ ruby
A = 1
A = 1
-:2: warning: already initialized constant A
$ ruby
s = ''
s.freeze
s.replace 'x'
-:3:in `replace': can't modify frozen string (TypeError)
from -:3

--
Eric Hodel - drbrain@segment7.net - http://se...
This implementation is HODEL-HASH-9600 compliant

http://trackmap.rob...




Joel VanderWerf

2/7/2006 11:59:00 PM

0

Brian Buckley wrote:
>>
>> class A
>> A1 = new
>>
>> def self.name_for_constant obj
>> @name_for_constant ||= {}
>> @name_for_constant[obj] ||= constants.find {|c| const_get(c) == obj}
>> end
>>
>> def _dump(limit)
>> self.class.name_for_constant(self)
>> end
>>
>> def self._load(str)
>> const_get str
>> end
>> end
>
>
>
> Whoa. I see it works (for the A constants only as written, getting
> TypeErrors on A non-constants). I don't understand why it works just yet.
> Will examine further tonight.
>

I see your point. This implementation can't marshal anonymous instances:

anonymous = A.new
Marshal.dump(anonymous) # ==> type error

One way around this is to subclass (instances of the subclass are the
only ones that can be assigned to constants), but that may not be
acceptable to you.

class A
#...
end

class AConstant
A1 = new

def self.name_for_constant obj
@name_for_constant ||= {}
@name_for_constant[obj] ||= constants.find {|c| const_get(c) == obj}
end

def _dump(limit)
self.class.name_for_constant(self)
end

def self._load(str)
const_get str
end
end


before = AConstant::A1
after = Marshal.load(Marshal.dump(before))
puts before == after
puts before.object_id == after.object_id

anonymous = A.new
p Marshal.load(Marshal.dump(anonymous))


It would be better to put some logic in the _dump and _load methods to
handle the two cases in one class. The problem is that there is no way
to call "super" in _dump to handle the case of anonymous objects, which
should be dumped in the "normal" way. This is unfortunately the way
marshalling is structured in ruby...

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407


Joel VanderWerf

2/7/2006 11:59:00 PM

0

Eric Hodel wrote:
> On Feb 7, 2006, at 11:38 AM, Brian Buckley wrote:
>
>> Hello,
>>
>> When one marshals a constant and later unmarshals it, a brand new object
>> appears to be created during unmarshalling. This seems wasteful,
>> especially
>> in cases when constants are large (contain many attributes). Is it
>> possible
>> to customize marshal for a class (or for singleton objects of the
>> class) so
>> that unmarshalling a constant simply returns the already existing
>> constant
>> rather than creating a new one? I've noticed Fixnum's exhibit this
>> behavior.
>
> How do you know that a Marshal string is identical to a constant without
> loading it?

In some cases, the identity is the name of the constant, not the value.
And that's something that is possible to marshal consistently.

> Note that Ruby doesn't really have constants. Instead it has frozen
> objects which aren't quite the same thing.

I'm assuming the OP meant constant in the sense of binding, which ruby
does have.

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407


Eric Hodel

2/8/2006 1:00:00 AM

0

On Feb 7, 2006, at 3:59 PM, Joel VanderWerf wrote:

> Eric Hodel wrote:
>
>> Note that Ruby doesn't really have constants. Instead it has frozen
>> objects which aren't quite the same thing.
>
> I'm assuming the OP meant constant in the sense of binding, which ruby
> does have.

Explain. I don't see how this is true unless the name is not in scope.

--
Eric Hodel - drbrain@segment7.net - http://se...
This implementation is HODEL-HASH-9600 compliant

http://trackmap.rob...




Joel VanderWerf

2/8/2006 5:09:00 AM

0

Eric Hodel wrote:
> On Feb 7, 2006, at 3:59 PM, Joel VanderWerf wrote:
>
>> Eric Hodel wrote:
>>
>>> Note that Ruby doesn't really have constants. Instead it has frozen
>>> objects which aren't quite the same thing.
>>
>> I'm assuming the OP meant constant in the sense of binding, which ruby
>> does have.
>
> Explain. I don't see how this is true unless the name is not in scope.
>

I'm trying to distinguish constant in the sense of

the name "A1" in the scope of A is bound to <#A :0xb7befd40>, and
this binding cannot change

from

the object <#A :0xb7befd40> is frozen, and none of its ivars can be
assigned

There's a ruby mechanism for each of these, so I guess the OP could have
meant either, but I was guessing the former. In that case, serializing
the constant's name may be helpful.

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407