DbC, const and freeze.

J

John Carter

Hmm. I tend to do Test Driven Development and Design by Contract.

I find the two approaches mesh very very well indeed.

Now in C++ I also use "const" very heavily.

It works very very well. It not only enforces certain design decisions,
more importantly it documents them.

If you hunting a bug that is mystikally changing a data value, a
well "const"'d C++ program allows you to ignore so much code and rapidly
zoom in on the real problem.

I was just thinking about the Object freeze method.

Now that is quite a powerful assert really.

It asserts that this object needs not and must not ever change.

A bit more powerful than "const" in away.

In fact too powerful.

What I really want is something like the C++

void myFunc( const obj& foo)
{
// I know foo won't get mangled by this routine...
}

or in Ruby...

def my_func( foo)
foo.freeze


ensure
foo.unfreeze
end


Accept there is no unfreeze.

This would work, but is a bit expensive...

def my_func( foo_p)
foo = foo_p.clone.freeze


end

It also misses the notion of "const" methods.

In C++ you are only allowed to invoke const methods of a const object.

For example, look at this hole...
irb
irb(main):001:0> a = [[1,2],[3,4]]
=> [[1, 2], [3, 4]]
irb(main):002:0> a.freeze
=> [[1, 2], [3, 4]]
irb(main):003:0> a << [9]
RuntimeError: can't modify frozen array
from (irb):3:in `<<'
from (irb):3
from :0
irb(main):004:0> a[0]<<1
=> [1, 2, 1]
irb(main):005:0> a
=> [[1, 2, 1], [3, 4]]
irb(main):006:0>

Even though "a" was frozen, I still could modify the things "a" contained.

Although Ruby has a C++'ish distinct between public and private methods,
it doesn't allow you to use that with "const" to identify which things
this class owns and freeze when this object is frozen.

Hmm. I suppose I could override freeze and freeze my instance variables
and then myself. That would do.

Any other ideas on getting the effect of C++ "const" in Ruby?



John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : (e-mail address removed)
New Zealand

Carter's Clarification of Murphy's Law.

"Things only ever go right so that they may go more spectacularly wrong later."

From this principle, all of life and physics may be deduced.
 
R

Ryan Leavengood

For example, look at this hole...
irb
irb(main):001:0> a =3D [[1,2],[3,4]]
=3D> [[1, 2], [3, 4]]
irb(main):002:0> a.freeze
=3D> [[1, 2], [3, 4]]
irb(main):003:0> a << [9]
RuntimeError: can't modify frozen array
from (irb):3:in `<<'
from (irb):3
from :0
irb(main):004:0> a[0]<<1
=3D> [1, 2, 1]
irb(main):005:0> a
=3D> [[1, 2, 1], [3, 4]]
irb(main):006:0>

Even though "a" was frozen, I still could modify the things "a" contained=
 
R

Robert Klemme

Btw: this posting of Ryan was truncated on the news side. Does anybody
else have it truncated at their news server, too?

2005/10/20 said:
For example, look at this hole...
irb
irb(main):001:0> a =3D [[1,2],[3,4]]
=3D> [[1, 2], [3, 4]]
irb(main):002:0> a.freeze
=3D> [[1, 2], [3, 4]]
irb(main):003:0> a << [9]
RuntimeError: can't modify frozen array
from (irb):3:in `<<'
from (irb):3
from :0
irb(main):004:0> a[0]<<1
=3D> [1, 2, 1]
irb(main):005:0> a
=3D> [[1, 2, 1], [3, 4]]
irb(main):006:0>

Even though "a" was frozen, I still could modify the things "a" contain=
ed.

Fair enough. How about this:
<snip/>

Apart from the fact that deep_unfreeze is missing...

I still think that the concept of constness doesn't fit Ruby well.=20
There are too many places where you can fiddle with state where you
normally (i.e. in other PL's) aren't allowed to. Adding #unfreeze
wouldn't help either because it would break the contract of freeze
that is used today - IOW code may break.

In some cases where modification of an argument must be prevented a
delegation based approach may work.

I wouldn't go as far as to state that const is overrated but the fact
that C++ needed to introduce keyword "mutable" shows that it's not
about simply freezing state of an instance. You rather want to freeze
observed state... Which brings up an idea....

module Kernel
private
def const(obj)
meth =3D {}
sc =3D class<<obj;self;end
obj.methods.grep(/=3D$/).each do |m|
meth[m]=3Dobj.method m
sc.send:)define_method, m) { raise "ConstError" }
end

begin
yield obj
ensure
meth.each do |m,mm|
sc.send:)define_method, m, &mm)
end
end
end
end
RuntimeError: ConstError
from (irb):8:in `name=3D'
from (irb):24
from (irb):12:in `const'
from (irb):23
from :0"hello"
=3D> nil"new 2"
=3D> nil

This can easily be extended to multiple instances... Maybe
modification of instance_variable_set does the job, too.
Does anyone else love that name? ;)

Can you do #deep_think, too? We then could have #deep_thought?... :)

Kind regards

robert
 
E

Eric Hodel

For example, look at this hole...
irb
irb(main):001:0> a = [[1,2],[3,4]]
=> [[1, 2], [3, 4]]
irb(main):002:0> a.freeze
=> [[1, 2], [3, 4]]
irb(main):003:0> a << [9]
RuntimeError: can't modify frozen array
from (irb):3:in `<<'
from (irb):3
from :0
irb(main):004:0> a[0]<<1
=> [1, 2, 1]
irb(main):005:0> a
=> [[1, 2, 1], [3, 4]]
irb(main):006:0>

Even though "a" was frozen, I still could modify the things "a"
contained.

Fair enough. How about this:

class Object
def deep_freeze
self.freeze
if self.respond_to? :each
self.each do |i|
i.deep_freeze
end
end
end
end

Does not freeze instance variables.

instance_variables.each do |ivar|
instance_variable_get(ivar).deep_freeze
end

I think.
 
E

Eric Hodel

2005/10/20 said:
For example, look at this hole...
irb
irb(main):001:0> a = [[1,2],[3,4]]
=> [[1, 2], [3, 4]]
irb(main):002:0> a.freeze
=> [[1, 2], [3, 4]]
irb(main):003:0> a << [9]
RuntimeError: can't modify frozen array
from (irb):3:in `<<'
from (irb):3
from :0
irb(main):004:0> a[0]<<1
=> [1, 2, 1]
irb(main):005:0> a
=> [[1, 2, 1], [3, 4]]
irb(main):006:0>

Even though "a" was frozen, I still could modify the things "a"
contained.

Fair enough. How about this:
<snip/>

Apart from the fact that deep_unfreeze is missing...

No, its not (well, you don't get the original back).

Marshal.load(Marshal.dump(obj))
I still think that the concept of constness doesn't fit Ruby well.
There are too many places where you can fiddle with state where you
normally (i.e. in other PL's) aren't allowed to. Adding #unfreeze
wouldn't help either because it would break the contract of freeze
that is used today - IOW code may break.

Of course, Marshal.load(Marshal.dump(obj)) works for regular unfreeze
as well.
 
R

Robert Klemme

Eric Hodel said:
2005/10/20 said:
For example, look at this hole...
irb
irb(main):001:0> a = [[1,2],[3,4]]
=> [[1, 2], [3, 4]]
irb(main):002:0> a.freeze
=> [[1, 2], [3, 4]]
irb(main):003:0> a << [9]
RuntimeError: can't modify frozen array
from (irb):3:in `<<'
from (irb):3
from :0
irb(main):004:0> a[0]<<1
=> [1, 2, 1]
irb(main):005:0> a
=> [[1, 2, 1], [3, 4]]
irb(main):006:0>

Even though "a" was frozen, I still could modify the things "a"
contained.


Fair enough. How about this:
<snip/>

Apart from the fact that deep_unfreeze is missing...

No, its not (well, you don't get the original back).

Marshal.load(Marshal.dump(obj))

I meant, it's missing from the sample implementation. But: Marshal does
something completely different: creating a copy is something different from
temporary freezing an instance's (or instance graph's) state. It starts
with different identities and doesn't stop at performance... You *can* use
that in certain scenarios but I doubt that it's a full replacement for
constness.

Kind regards

robert
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top