Freezing an arbitary object

Lets say I am doing design by contract and want to do a call like this:

test=UnitTest::new

foo=1
test.invariant(foo)
foo+=1 # should produce an error and exit

in UnitTest::invariant I want something like this (this code doesn’t
work):

class UnitTest

def invariant(aObj)
aObj.freeze
end

end

What am I missing, yes I know aObj is a reference thus freezing it does
freeze the actual param.

This last line can be rewritten …

foo = foo + 1

So you add 1 to a integer object which returns a new integer value (it
never modifies the original integer, it “creates” a new one). This new
integer value is then bound to the name “foo”. The previous foo object,
the frozen one, is no long accessable (through the name “foo”).

···

On Sun, 2003-07-06 at 15:32, Aryeh M. Frierdman wrote:

Lets say I am doing design by contract and want to do a call like this:

test=UnitTest::new

foo=1
test.invariant(foo)
foo+=1 # should produce an error and exit


– Jim Weirich jweirich@one.net http://onestepback.org

“Beware of bugs in the above code; I have only proved it correct,
not tried it.” – Donald Knuth (in a memo to Peter van Emde Boas)

“Aryeh M. Frierdman” aryeh@m-net.arbornet.org

foo=1
test.invariant(foo)
foo+=1 # should produce an error and exit

This will work like you want if you make

foo = [1,2,3]
test.invariant(foo)
foo[0] = 2

Now, with foo = 1 even foo.freeze will not work:

···

#-------------------------------------------------------------
C:>ruby -ve "foo = 1; foo.freeze; foo = 2; puts foo; "
ruby 1.8.0 (2003-06-23) [i386-mswin32]
2

C:>ruby -e "foo = [1]; foo.freeze; foo = 2; puts foo; "
2

C:>ruby -e "foo = [1]; foo.freeze; foo[0] = 2; puts foo; "
-e:1:in `=': can’t modify frozen array (TypeError)
from -e:1
#-------------------------------------------------------------

HTH,
– shanko

Local variables are not objects, and you can’t freeze them. They contain a
reference to an object, so you can freeze the object they refer to, but not
the variable itself.

In Ruby, Integers are immutable anyway, so 1.freeze is legal but doesn’t
actually do anything.

But if you use instance variables instead:

@foo = 1
freeze # or “self.freeze”
@foo = 2 # >> TypeError: can’t modify frozen object

Regards,

Brian.

···

On Mon, Jul 07, 2003 at 04:32:50AM +0900, Aryeh M. Frierdman wrote:

foo=1
test.invariant(foo)
foo+=1 # should produce an error and exit

Thanks for the answers so far but “none” of them actually do what I want.
What I want to do is declare some instance/var/whatever as being invariant
and any attemt to vary it will result in an error. I am convinced their
has to be some generic way to do this without the caller to the "test"
being “aware” that they are asking for it to be froozen. The int example
I gave was only that an example it could of very well been a String,
MyClass, EvilDictator, etc.

Thanks for the answers so far but “none” of them actually do what I want.
What I want to do is declare some instance/var/whatever as being invariant

Well, you should know what you mean by “whatever”

  • local variables: cannot be frozen, since they don’t belong to an object
    and are not an object themselves. They can always be reassigned to point
    to another object. Sorry, that’s a Ruby fact of life.

  • instance variables: are frozen when the object containing them is frozen

    class Foo
    attr_accessor :x
    end
    a = Foo.new
    a.x = 99
    a.freeze
    a.x = 100 # TypeError: can’t modify frozen object

and any attemt to vary it will result in an error. I am convinced their
has to be some generic way to do this without the caller to the “test”
being “aware” that they are asking for it to be froozen. The int example
I gave was only that an example it could of very well been a String,
MyClass, EvilDictator, etc.

Taking String as an example:

foo = “hello”
foo.freeze
foo << “x” # Error: object referenced by foo is frozen
foo = “bye” # Not error: foo now references a completely different object

And it can be done at a distance:

def my_test_function(x)
x.freeze
end

foo = “a”
my_test_function(foo)
foo << “b” # Error: object has been frozen

But like I say, local variable “foo” cannot be frozen. I hope that’s clear
enough…

Brian.

···

On Mon, Jul 07, 2003 at 06:53:20AM +0900, Aryeh M. Frierdman wrote:

“Aryeh M. Frierdman” aryeh@m-net.arbornet.org schrieb im Newsbeitrag
news:0N0Oa.44153$5h.15219260@news4.srv.hcvlny.cv.net

Thanks for the answers so far but “none” of them actually do what I
want.
What I want to do is declare some instance/var/whatever as being
invariant
and any attemt to vary it will result in an error. I am convinced their
has to be some generic way to do this without the caller to the “test”
being “aware” that they are asking for it to be froozen. The int
example
I gave was only that an example it could of very well been a String,
MyClass, EvilDictator, etc.

Apparently you want two things:

  • freeze an instance
  • make an object reference unchangeable

It’s important to not confuse these two issues. The first is a property
of an instance while the latter is a property of an object reference. In
Ruby there are only references. There is no distinction between pointers,
references and values like one might know from C++. And there’s a subtle
difference between C++ references and Ruby references: C++ references
can’t be changed and always refer to the instance they were initialized
with, Ruby references can be changed. Thus Ruby references do have a
little similarity with C++ pointers - but not too much, since pointer
arithmetic is missing.

The first is achieved by doing obj.freeze as you determined correctly,
while the latter is not possible with local references as Brian pointed
out. The closest you can get is to declare a constant (first letter
uppercase) and then get a warning on reassignment like in:

irb(main):001:0> This_is_const = “foo”
“foo”
irb(main):002:0> This_is_const.freeze
“foo”
irb(main):003:0> This_is_const << “foo”
TypeError: can’t modify frozen string
from (irb):3:in `<<’
from (irb):3
irb(main):004:0> This_is_const = “bar”
(irb):4: warning: already initialized constant This_is_const
“bar”
irb(main):005:0>

Regards

robert

Brian Candler wrote:

  • local variables: cannot be frozen, since they don’t belong to an object
    and are not an object themselves. They can always be reassigned to point
    to another object. Sorry, that’s a Ruby fact of life.

I wonder why this doesn’t work? I didn’t expect it to, but I’m curious.

irb(main):005:0> b = binding
#Binding:0x401d7e7c
irb(main):006:0> eval “y=1”, b
1
irb(main):007:0> eval “y”, b
1
irb(main):008:0> b.freeze
#Binding:0x401d7e7c
irb(main):009:0> eval “y=2”, b
2

Would it be useful to be able to freeze the current binding? Disastrous?
Inefficient?

Side question:

is there anyway to make the warning an error?

Guillaume.

···

On Mon, 2003-07-07 at 04:36, Robert Klemme wrote:

“Aryeh M. Frierdman” aryeh@m-net.arbornet.org schrieb im Newsbeitrag
news:0N0Oa.44153$5h.15219260@news4.srv.hcvlny.cv.net

Thanks for the answers so far but “none” of them actually do what I
want.
What I want to do is declare some instance/var/whatever as being
invariant
and any attemt to vary it will result in an error. I am convinced their
has to be some generic way to do this without the caller to the “test”
being “aware” that they are asking for it to be froozen. The int
example
I gave was only that an example it could of very well been a String,
MyClass, EvilDictator, etc.

Apparently you want two things:

  • freeze an instance
  • make an object reference unchangeable

It’s important to not confuse these two issues. The first is a property
of an instance while the latter is a property of an object reference. In
Ruby there are only references. There is no distinction between pointers,
references and values like one might know from C++. And there’s a subtle
difference between C++ references and Ruby references: C++ references
can’t be changed and always refer to the instance they were initialized
with, Ruby references can be changed. Thus Ruby references do have a
little similarity with C++ pointers - but not too much, since pointer
arithmetic is missing.

The first is achieved by doing obj.freeze as you determined correctly,
while the latter is not possible with local references as Brian pointed
out. The closest you can get is to declare a constant (first letter
uppercase) and then get a warning on reassignment like in:

irb(main):001:0> This_is_const = “foo”
“foo”
irb(main):002:0> This_is_const.freeze
“foo”
irb(main):003:0> This_is_const << “foo”
TypeError: can’t modify frozen string
from (irb):3:in `<<’
from (irb):3
irb(main):004:0> This_is_const = “bar”
(irb):4: warning: already initialized constant This_is_const
“bar”
irb(main):005:0>

Regards

robert

Joel VanderWerf wrote:

Brian Candler wrote:

  • local variables: cannot be frozen, since they don’t belong to an object
    and are not an object themselves. They can always be reassigned to
    point to another object. Sorry, that’s a Ruby fact of life.

I wonder why this doesn’t work? I didn’t expect it to, but I’m curious.

irb(main):005:0> b = binding
#Binding:0x401d7e7c
irb(main):006:0> eval “y=1”, b
1
irb(main):007:0> eval “y”, b
1
irb(main):008:0> b.freeze
#Binding:0x401d7e7c
irb(main):009:0> eval “y=2”, b
2

Would it be useful to be able to freeze the current binding? Disastrous?
Inefficient?

Being new to ruby I would not know the exact reasons but I susbect that
at the interpreter level a local is implimented as a ptr into a stack frame
vs. a ptr to the free store thus it would be dangerous to freeze a tempurary
object. On second thought this might not be true since how whould you
handle doing garbage collection on a long running method that “free’s” a
local???

Doesn’t a constant do what’s needed here?

irb(main):001:0> Foo = 1
=> 1
irb(main):002:0> Foo = 2
(irb):2: warning: already initialized constant Foo
=> 2
irb(main):003:0>

Ari