Freezing an arbitary object

  • Thread starter Aryeh M. Frierdman
  • Start date
A

Aryeh M. Frierdman

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.
 
S

Shashank Date

Aryeh M. Frierdman said:
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
 
B

Brian Candler

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

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.
 
A

Aryeh M. Frierdman

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.
 
B

Brian Candler

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.
 
J

Joel VanderWerf

Brian said:
- 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?
 
A

Aryeh M. Frierdman

Joel said:
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????
 
A

Aredridel

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
 
R

Robert Klemme

Aryeh M. Frierdman said:
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
 

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,768
Messages
2,569,575
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top