Constant in Ruby

I'm new to Ruby programming and I saw this article which concerns me:

http://www.bitwisemag.com/copy/features/opinion/ruby/ruby_debate.html

Don't get me wrong, I love Ruby. But, the idea that a constant is not a
constant is worrying isn't it? Or have I missed something? Please
comment.

X = 10
Y = "hello world"
Z = "hello world"

X = 20
Y = "byebye" << "abc"
myvar = Z << "xyz"

puts(X)
20
puts(Y)
byebyeabc
puts(Z)
hello worldxyz
myvar
"hello worldxyz"

···

--
Posted via http://www.ruby-forum.com/.

That article is horrible. Don't worry about it.

Classes are constants in Ruby, so it's good that we can modify constants.

Ruby is a scalpel. You can use it to heal or to destroy.

···

On 4/29/06, Charlie <peckcharlie@yahoo.com> wrote:

I'm new to Ruby programming and I saw this article which concerns me:

http://www.bitwisemag.com/copy/features/opinion/ruby/ruby_debate.html

Don't get me wrong, I love Ruby. But, the idea that a constant is not a
constant is worrying isn't it? Or have I missed something? Please
comment.

X = 10
Y = "hello world"
Z = "hello world"

X = 20
Y = "byebye" << "abc"
myvar = Z << "xyz"

puts(X)
20
puts(Y)
byebyeabc
puts(Z)
hello worldxyz
myvar
"hello worldxyz"

Charlie wrote:

I'm new to Ruby programming and I saw this article which concerns me:

http://www.bitwisemag.com/copy/features/opinion/ruby/ruby_debate.html

Don't get me wrong, I love Ruby. But, the idea that a constant is not a
constant is worrying isn't it? Or have I missed something? Please
comment.

X = 10
Y = "hello world"
Z = "hello world"

X = 20
Y = "byebye" << "abc"
myvar = Z << "xyz"

puts(X)
20
puts(Y)
byebyeabc
puts(Z)
hello worldxyz
myvar
"hello worldxyz"

It's constant in that you can't reassign a different instance to the
same name (try setting X = 10 after you've set it to 20 and Ruby will
gripe). The instances themselves may still be mutable (see
Object#freeze if you want to change that).

···

--
Posted via http://www.ruby-forum.com/\.

Well, that's not *quite* true. Ruby complains, but allows the reassignment.

In practice, though, it's not a big deal at all.

-austin

···

On 4/29/06, Mike Fletcher <lemurific+rforum@gmail.com> wrote:

It's constant in that you can't reassign a different instance to the
same name (try setting X = 10 after you've set it to 20 and Ruby will
gripe). The instances themselves may still be mutable (see
Object#freeze if you want to change that).

--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca

Mike Fletcher wrote:

Charlie wrote:

I'm new to Ruby programming and I saw this article which concerns me:

http://www.bitwisemag.com/copy/features/opinion/ruby/ruby_debate.html

Don't get me wrong, I love Ruby. But, the idea that a constant is not a
constant is worrying isn't it? Or have I missed something? Please
comment.

X = 10
Y = "hello world"
Z = "hello world"

X = 20
Y = "byebye" << "abc"
myvar = Z << "xyz"

puts(X)
20
puts(Y)
byebyeabc
puts(Z)
hello worldxyz
myvar
"hello worldxyz"

It's constant in that you can't reassign a different instance to the
same name (try setting X = 10 after you've set it to 20 and Ruby will
gripe). The instances themselves may still be mutable (see
Object#freeze if you want to change that).

okay, I just try this:

X = 20
X = 10
puts(X)
10
X.freeze
X = 30
puts(X)
30

   Sorry, but I find it difficult to understand as I'm new to it. Would
be helpful if you can enlighten me with some examples. Thanks.

···

--
Posted via http://www.ruby-forum.com/\.

Charlie wrote:

Sorry, but I find it difficult to understand as I'm new to it. Would be helpful if you can enlighten me with some examples. Thanks.

Try using IRB (type `irb' in the console):

   >> X = 10
   => 10
   >> X = 20
      warning: already initialized constant X
   => 20

Daniel

Charlie wrote:
[...]

It's constant in that you can't reassign a different instance to the
same name (try setting X = 10 after you've set it to 20 and Ruby will
gripe). The instances themselves may still be mutable (see
Object#freeze if you want to change that).

okay, I just try this:

X = 20
X = 10
puts(X)
10
X.freeze
X = 30
puts(X)
30

   Sorry, but I find it difficult to understand as I'm new to it. Would
be helpful if you can enlighten me with some examples. Thanks.

$ cat spork
#!/usr/local/bin/ruby

A_NUMBER = 10
puts "A_NUMBER is #{A_NUMBER}"
A_NUMBER = 20
puts "A_NUMBER is #{A_NUMBER}"

A_STRING = "foo"
puts "A_STRING is #{A_STRING}"
A_STRING.sub!( %r/^f/, "b" )
puts "A_STRING is #{A_STRING}"
A_STRING.freeze

begin
  A_STRING.sub!( %r/^.*$/, "this won't work" )
rescue TypeError => e
  warn "sub! on A_STRING failed: #{e}"
end

A_STRING = "this will work but causes a warning"
print "A_STRING is #{A_STRING}"

$ ruby spork
A_NUMBER is 10
spork:5: warning: already initialized constant A_NUMBER
A_NUMBER is 20
A_STRING is foo
A_STRING is boo
sub! on A_STRING failed: can't modify frozen string
spork:20: warning: already initialized constant A_STRING
A_STRING is this will work but causes a warning

Reassigning to a constant after the initial assignment causes a warning,
but it works.

The String instance in A_STRING has methods which will change the
contents of the instance. The constant name "A_STRING" still points to
the same String instance (so I didn't get the warning that I was
reassigning to an already initialized constant), but its internal
state/contents can still be modified in place. Using freeze makes it so
that that internal state can't be changed and the second in-place
substitution call throws an exception. Since I placed begin / rescue /
end around that call I can recovered, but A_STRING would still be "boo".

Lastly, even though I've frozen the particular instance A_STRING points
to I can still reassign a new String instance to A_STRING. However this
produces the warning that I'm altering an already initialized constant
just as the first examples with A_NUMBER did. And A_STRING is once
again mutable since the new instance hasn't been frozen.

···

--
Posted via http://www.ruby-forum.com/\.

Variables -- and by implication, constants -- aren't themselves
objects. You're not freezing X. You're calling #freeze on the object
referenced by X, essentially 10.freeze.

Honestly, it's not worth panicking over.

-austin

···

On 4/30/06, Charlie <peckcharlie@yahoo.com> wrote:

X = 20
X = 10
puts(X)
10
X.freeze
X = 30
puts(X)
30

--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca

Hi Charlie,

[...]
> It's constant in that you can't reassign a different instance to the
> same name (try setting X = 10 after you've set it to 20 and Ruby will
> gripe). The instances themselves may still be mutable (see
> Object#freeze if you want to change that).

okay, I just try this:

X = 20
X = 10
puts(X)
10
X.freeze
X = 30
puts(X)
30

    The "freeze" doesn't let the object change, but in this case you're not
changing the object, you're changing the reference.

    Let's see if an example clears it up:

------------------------------------- 8< -------------------------------------
irb(main):001:0> X = "hi"
=> "hi"
irb(main):002:0> X = "hello"
(irb):2: warning: already initialized constant X
=> "hello"
irb(main):003:0> X.sub!(/$/, "!")
=> "hello!"
irb(main):004:0> X.freeze
=> "hello!"
irb(main):005:0> X.sub!(/e/, "a")
TypeError: can't modify frozen string
        from (irb):5:in `sub!'
        from (irb):5
irb(main):006:0>
------------------------------------- >8 -------------------------------------

You can't reassign a constant (without a warning), and you can't change an
_object_ (possibly, but not necessarily, pointed to by a constant) that has
been frozen.

    HTH,

···

On Sun, Apr 30, 2006 at 03:57:41PM +0900, Charlie wrote:

--
Esteban Manchado Velázquez <zoso@foton.es> - http://www.foton.es
EuropeSwPatentFree - http://EuropeSwPatentFree.hispalinux.es

You're freezing the Object that is assigned to X. If you want to freeze the
class that owns X, you can try:
class Foo
  X = "Hello".freeze # nobody can change the string out from under us
end

Foo.freeze # now, you can't change the Foo class

That should do what you want.

···

On 4/29/06, Charlie <peckcharlie@yahoo.com> wrote:

Mike Fletcher wrote:
> Charlie wrote:
>> I'm new to Ruby programming and I saw this article which concerns me:
>>
>> http://www.bitwisemag.com/copy/features/opinion/ruby/ruby_debate.html
>>
>> Don't get me wrong, I love Ruby. But, the idea that a constant is not a
>> constant is worrying isn't it? Or have I missed something? Please
>> comment.
>>
>> X = 10
>> Y = "hello world"
>> Z = "hello world"
>>
>> X = 20
>> Y = "byebye" << "abc"
>> myvar = Z << "xyz"
>>
>> puts(X)
>> 20
>> puts(Y)
>> byebyeabc
>> puts(Z)
>> hello worldxyz
>> myvar
>> "hello worldxyz"
>
> It's constant in that you can't reassign a different instance to the
> same name (try setting X = 10 after you've set it to 20 and Ruby will
> gripe). The instances themselves may still be mutable (see
> Object#freeze if you want to change that).

okay, I just try this:

X = 20
X = 10
puts(X)
10
X.freeze
X = 30
puts(X)
30

   Sorry, but I find it difficult to understand as I'm new to it. Would
be helpful if you can enlighten me with some examples. Thanks.

--
Posted via http://www.ruby-forum.com/\.

--
--------
David Pollak's Ruby Playground
http://dppruby.com

Mike Fletcher wrote:
$ cat spork
#!/usr/local/bin/ruby

A_NUMBER = 10
puts "A_NUMBER is #{A_NUMBER}"
A_NUMBER = 20
puts "A_NUMBER is #{A_NUMBER}"

A_STRING = "foo"
puts "A_STRING is #{A_STRING}"
A_STRING.sub!( %r/^f/, "b" )
puts "A_STRING is #{A_STRING}"
A_STRING.freeze

begin
   A_STRING.sub!( %r/^.*$/, "this won't work" )
rescue TypeError => e
   warn "sub! on A_STRING failed: #{e}"
end

A_STRING = "this will work but causes a warning"
print "A_STRING is #{A_STRING}"

$ ruby spork
A_NUMBER is 10
spork:5: warning: already initialized constant A_NUMBER
A_NUMBER is 20
A_STRING is foo
A_STRING is boo
sub! on A_STRING failed: can't modify frozen string
spork:20: warning: already initialized constant A_STRING
A_STRING is this will work but causes a warning

Reassigning to a constant after the initial assignment causes a warning,
but it works.

The String instance in A_STRING has methods which will change the
contents of the instance. The constant name "A_STRING" still points to
the same String instance (so I didn't get the warning that I was
reassigning to an already initialized constant), but its internal
state/contents can still be modified in place. Using freeze makes it so
that that internal state can't be changed and the second in-place
substitution call throws an exception. Since I placed begin / rescue /
end around that call I can recovered, but A_STRING would still be "boo".

Lastly, even though I've frozen the particular instance A_STRING points
to I can still reassign a new String instance to A_STRING. However this
produces the warning that I'm altering an already initialized constant
just as the first examples with A_NUMBER did. And A_STRING is once
again mutable since the new instance hasn't been frozen.

Thanks Mike for clearing things up. That helps.

···

--
Posted via http://www.ruby-forum.com/\.

Thank you everyone for your input. I got this simple example to share:

A = 10 => 10
X = A.freeze => 10
A = 20 => 20
X => 10

···

--
Posted via http://www.ruby-forum.com/.

I'm not really sure you're understanding what was said.

Line 2:

  X = A.freeze

Does nothing except assignment.

10.freeze does nothing. That's what A.freeze actually does.

Assignment changes the reference for a particular variable.

  x = "foo"
  a = x
  x.gsub(/foo/, "bar")
  a # -> "bar"

  x = "foo"
  a = x
  x = "bar"
  a # -> "foo"

This is true whether you're dealing with variables or constants.

-austin

···

On 5/5/06, Charlie <peckcharlie@yahoo.com> wrote:

Thank you everyone for your input. I got this simple example to share:

A = 10 => 10
X = A.freeze => 10
A = 20 => 20
X => 10

--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca

Thank you everyone for your input. I got this simple example to share:

A = 10 => 10
X = A.freeze => 10
A = 20 => 20
X => 10

Check the following irb session to see what's happening:

  >> A = 10
  => 10

  >> A.object_id
  => 21

  >> X = A.freeze
  => 10

  >> A.object_id
  => 21

  >> X.object_id
  => 21

  >> A = 20
  warning: already initialized constant A
  => 20

  >> A.object_id
  => 41

  >> X.object_id
  => 21

  >> X
  => 10

A and X are just references to objects. Setting X equal to A makes X
reference the same object as A referenced at the time, but X does not
reference A directly. So changing what A references (assignment) has
no impact on X -- X just keeps on referencing what it was told to
reference. However, if A and X continue to reference the same object
and you perform an in place modification on either, both will see the
change since they still reference that modified object:

  >> A = "foo"
  => "foo"

  >> A.object_id
  => 1661894

  >> X = A
  => "foo"

  >> A.object_id
  => 1661894

  >> X.object_id
  => 1661894

  >> A << "bar"
  => "foobar"

  >> A.object_id
  => 1661894

  >> X.object_id
  => 1661894

  >> X
  => "foobar"

Jacob Fugal

···

On 5/5/06, Charlie <peckcharlie@yahoo.com> wrote:

  >> A = "foo"
  => "foo"

  >> A.object_id
  => 1661894

  >> X = A
  => "foo"

  >> A.object_id
  => 1661894

  >> X.object_id
  => 1661894

  >> A << "bar"
  => "foobar"

  >> A.object_id
  => 1661894

  >> X.object_id
  => 1661894

  >> X
  => "foobar"

Jacob Fugal

Okay, just one more go ...

A = "Foo" => "Foo"
A.object_id => 29971016
B = A => "Foo"
B.object_id => 29971016
A.freeze => "Foo"
B << "Bar"
TypeError: can't modify frozen string
  from (irb):7:in `<<'
  from (irb):7

···

from :0

--
Posted via http://www.ruby-forum.com/\.

Do you understand *why* that worked? Consider it from:

  A = "Foo"
  B = A
  B.freeze
  A << "Bar"

-austin

···

On 5/5/06, Charlie <peckcharlie@yahoo.com> wrote:

Okay, just one more go ...

A = "Foo" => "Foo"
A.object_id => 29971016
B = A => "Foo"
B.object_id => 29971016
A.freeze => "Foo"
B << "Bar"
TypeError: can't modify frozen string
        from (irb):7:in `<<'
        from (irb):7
        from :0

--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca

Austin Ziegler wrote:

        from (irb):7
        from :0

Do you understand *why* that worked? Consider it from:

  A = "Foo"
  B = A
  B.freeze
  A << "Bar"

-austin

I show the object_id just for clarity, to show that A and B is
'referencing' or 'pointing' to the same object, although they do
nothing. I thought I understand it until I try the example below for
integers. Instead of showing errors, it gave very large bogus numbers,
although results remain intact in the end. So, the answer to your
question is that I don't. I think I might have missed out something
here.

A = 10 => 10
B = A => 10
B.freeze => 10
A << 20 => 10485760
B << 40 => 10995116277760
A => 10
B => 10
A.object_id => 21
B.object_id => 21

···

On 5/5/06, Charlie <peckcharlie@yahoo.com> wrote:

--
Posted via http://www.ruby-forum.com/\.

Charlie wrote:

Austin Ziegler wrote:

        from (irb):7
        from :0

Do you understand *why* that worked? Consider it from:

  A = "Foo"
  B = A
  B.freeze
  A << "Bar"

-austin

I show the object_id just for clarity, to show that A and B is
'referencing' or 'pointing' to the same object, although they do
nothing. I thought I understand it until I try the example below for
integers. Instead of showing errors, it gave very large bogus numbers,
although results remain intact in the end. So, the answer to your
question is that I don't. I think I might have missed out something
here.

A = 10 => 10
B = A => 10
B.freeze => 10
A << 20 => 10485760
B << 40 => 10995116277760
A => 10
B => 10
A.object_id => 21
B.object_id => 21

Sorry, the leftshift results were consistent, but why doesn't the
shifted result get stored back into pointer A like in the case of a
string?

A = "foo" => "foo"
X = A => "foo"
A << "bar" => "foobar"
A => "foobar"
X => "foobar"
A.object_id => 29897204
X.object_id => 29897204

···

On 5/5/06, Charlie <peckcharlie@yahoo.com> wrote:

--
Posted via http://www.ruby-forum.com/\.

I'm going to start from a few basic principles, because Ruby works
differently than you think it does, and that's kinda obvious from how
you're not getting this.

Before I get started, I think that the article you read has done you a
great disservice because the authors raised a point that isn't actually
a problem in practice in Ruby. (And even in C++, it is pretty easy to
subvert the const-ness of a variable. It's a little harder to subvert
the const-ness of a reference, but not impossible.)

Most of what I'm going to say here is similar to what Jim Weirich said
in his "Shoeboxes and Bindings"[1] essay. You probably came from the
class of languages descending from C, where each variable is a place in
memory. For non-pointer operations, simple assignment is duplication:

  std::string a, b;
  a = b = "Hello";

In this C++ example, you have two shoeboxes that each contain a copy of
a string, "Hello":

  a [ "Hello" ]
  b [ "Hello" ]

In C and C++ you can use pointers and references to alias these
shoeboxes.

  std::string& c;
  std::string* d;

  c = a;
  d = &b;

  c += ", world!";
  (*d) += ", program!";

In this C++ example, you have two more shoeboxes. These are special
shoeboxes in that they don't contain strings; they contain the addresses
of strings.

  c [ address-of a ] -> a [ "Hello, world!" ]
  d [ address-of b ] -> b [ "Hello, program!" ]

In C++, the variable d has its own storage. Because it's a pointer to
std::string, it's going to be sizeof(void*). It's even possible to have
a pointer to d:

  std::string** e = &d;

Which is:

  e [ address-of d ] -> d [ address-of b ] -> b [ "Hello, program!" ]

It's turtles all the way down. The only time that it gets a little dicey
is when you allocate memory on the heap:

  std::string* f = new std::string("Greetings, program!");
  std::string** g = &f;

But even that is pretty easy to deal with, conceptually. In this case,
we're given a handle to the memory -- the address. It might look like:

  f [ address-of g ] -> g [ address ] -> [ "Greetings, program!" ]

Like I said, pretty basic stuff.

...

And completely *not* the way that Ruby works.

Ruby variables are closer to the behaviour of C/C++ pointers and
references, except that they are not shoeboxes themselves. This is an
important thing to remember. Jim calls them "bindings." I call them
"labels." Either way, they're essentially local names for values.

The fundamental in Ruby is not a variable, but an object:

  "Hello"

This is an object. It can have things done to it and it can be passed
around. However, this object is anonymous. We don't know its name, or it
doesn't have one. Because it is anonymous and isn't bound anywhere, it
may end up going out of scope quickly and be eligible for garbage
collection. So, we give an object a name:

  x = "Hello"

Now, we can manipulate the object without fear that it's going to go out
of scope and be garbage collected when we need to use it. The
representation is something like:

  x -> [ "Hello" ]

Note the difference in the way that I showed this from the way that I
showed the C++ example:

  x -> [ "Hello" ]
  c [ address-of a ] -> a [ "Hello, world!" ]
  g -> [address of f ] -> f [ address ] -> [ "Greetings, program!" ]

The form with 'f' is a little closer to the Ruby view of variables, but
it's still a piece of storage that can have its address taken as well,
as the 'g' shows. Well, that won't work with Ruby.

  x = y = "Hello, muddah."

That would look something like:

  x ------v
          [ "Hello, muddah." ]
  y ------^

We now have the object "Hello" referred to by two different names. But
if I assign to y again:

  y = "Hello, faddah."

I the two separate names pointing to two objects.

  x -> [ "Hello, muddah." ]
  y -> [ "Hello, faddah." ]

Pretty simple so far, and clear. Ruby throws one loop at us, but it's
not nearly as bad as the object/primitive distinction that Java gives.
Some Ruby objects exist exactly once and are never garbage collected.
This is partially for performance reasons, but it also makes sense. The
values that do this are Fixnums (31-bit signed integer values), Symbols,
and true, false, and nil. These values are called "immediate" values.
These objects are immutable. It is possible for a class to simulate
immediacy without the object itself being a full-on immediate value. I
believe that Bignums are implemented this way.

99% of the time, the distinction between immediate and reference values
with the binding approach that Ruby takes to variables. You simply bind
a variable to an object; you don't make a storage space that you then
put an object or a reference to that object to. There are no shoeboxes.
Ruby only has to worry about memory management of objects; you don't
have to worry about any of it.

Now, how does this apply to constants? Well, in Ruby, constants are just
special variables. The bindings *can* be changed, but Ruby will complain
on binding them to different objects. Unless a mutable object is frozen,
though, the contents of the object referred to by object can be
modified.

Note the important words on that last sentence: mutable object.
Immediate values -- Fixnums -- are immutable. That's why 10.freeze
(which is what x = 10; x.freeze does) is a no-op.

Hopefully, that helps.

-austin
[1] http://onestepback.org/index.cgi/2003/12/15

···

--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca

Because it's not leftshift on a string. And A isn't a pointer. It's a binding.

See my latest post to you that goes into the details. Make sure you
read Jim's shorter treatment, too.

Fixnums are, by the way, immutable. And assignment doesn't affect the object.

-austin

···

On 5/5/06, Charlie <peckcharlie@yahoo.com> wrote:

Sorry, the leftshift results were consistent, but why doesn't the
shifted result get stored back into pointer A like in the case of a
string?

--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca