Name/symbol/object type clash? What is happening here?

It’s nonsense code, but I’m curious as to what is going on behind the
scenes…

irb(main):001:0> S = Struct.new(:num)
=> S
irb(main):002:0> class S; def change_to n; @num = n; end; end
=> nil
irb(main):003:0> s = S.new(3)
#
irb(main):004:0> s.num = 4
=> 4
irb(main):005:0> s.num
=> 4
irb(main):006:0> s.change_to(5)
=> 5
irb(main):007:0> s.num
=> 4
irb(main):008:0> s.instance_variables
=> [:@num]
irb(main):009:0> s.instance_variable_set(:@num, 5)
=> 5
irb(main):010:0> s.num
=> 4

Same thing happens if I don’t define class S, but just define a method
on the instance… def s.change_to n; @num = n; end

On Wednesday 24 October 2012 Todd B. wrote

=> 4
irb(main):010:0> s.num
=> 4

Same thing happens if I don’t define class S, but just define a method
on the instance… def s.change_to n; @num = n; end

Apparently, classes created using Struct don’t store values in instance
variables (you can see this by calling #instance_variables on s
immediately
after creating it: it returns an empty array); rather they are stored
internally in C-level variables which can’t be accessed from ruby.
Because of
this, setting the @num instance variable doesn’t have any effect on the
value
returned by #num. If you know C, you can see how Struct works internally
looking at the file struct.c in the ruby source code.

I hope this helps

Stefano

No doubt, the following code exhibits unacceptable inconsistencies:

S = Struct.new(:num)
class S; def change_to n; @num = n; end; end
s = S.new(3)
puts s.num #=> 3
s.num = 4
puts s.num #=> 4
s.change_to(5)
puts s.num #=> 4

Due to the problems shown above, perhaps Struct should be taken out of
Ruby paradigm all together.

However, except in limited initialization circumstances, the use of ‘@’
variables should be prohibited, and from the point of view of strict
OOP, it is, by convention! The use of ‘Struct’ should also be
discouraged, and even worse, mixing Struct with class definitions and
usage is ill advised.

The above problem can be solved if you use accessor methods in place of
‘@’ variable:

S = Struct.new(:num)
class S
def show_num; puts “Show:#{self.num}”; end # <<< SOLUTION
def change_num(n); self.num = n; end # <<< SOLUTION
end

s = S.new(100)
puts s.num #=> 100
s.show_num #=> Show:100

def s.change_singleton_num(n); self.num = n; end # <<< SOLUTION

s.num = 1
puts s.num #=> 1
s.show_num #=> Show:1

s.change_num(2)
puts s.num #=> 2
s.show_num #=> Show:2

s.change_singleton_num(2000)
puts s.num #=> 2000
s.show_num #=> Show:2000

Regards, igor

Todd B. wrote in post #1080981:

Okay, I see, thanks.

Okay, you see what? Your example
shows that ruby is completely screwed up somewhere. You can’t have
two constants with the same name, so your class should have overwritten
the Struct.

On Tue, Oct 23, 2012 at 12:44 PM, Igor P. [email protected]
wrote:

‘@’ variable:

s.change_singleton_num(2000)
puts s.num #=> 2000
s.show_num #=> Show:2000

Regards, igor

Okay, I see, thanks.

On Oct 24, 2012, at 18:21 , 7stud – [email protected] wrote:

Todd B. wrote in post #1080981:

Okay, I see, thanks.

Okay, you see what? That was one of the worst responses I’ve ever read
on this forum. It completely missed the big picture.

No… Yours is. Quit being a constant prick on here.

On Wed, Oct 24, 2012 at 9:21 PM, 7stud – [email protected] wrote:

Todd B. wrote in post #1080981:

Okay, I see, thanks.

Okay, you see what? That was one of the worst responses I’ve ever read
on this forum. It completely missed the big picture. Your example
shows that ruby is a completely screwed up somewhere. You can’t have
two constants with the same name, so your class should have overwritten
the Struct.

Ruby is not screwed up, at least not in the ways you say it is. The
class S line isn’t declaring a new class named S, it’s reopening the class
created by Struct.new.

S = Struct.new(:num)
class S
  def show_num; puts "Show:#{self.num}"; end
end

is like

S = Struct.new(:num) do
  def show_num; puts "Show:#{self.num}"; end
end

or similar enough to

class S < Struct.new(:num)
  def show_num; puts "Show:#{self.num}"; end
end

Robert K. wrote in post #1081146:

I strongly object. Struct is a very useful tool I use all the time to
define data containers quickly.

The above problem can be solved if you use accessor methods in place of
‘@’ variable:

Exactly.

I have no objection if anybody wishes to use the broken Struct as it is
in his/her own short-cuts and sketches. However, if allowed there, who
is going to prevent its use in main-stream programming? The trouble is,
this stuff is so broken in situations becomes totally useless. Its
inconsistencies with regular ruby classes make things unacceptable on
the first place, and I can not believe anyone would defend its
continuous usage in this broken fashion! If it is so bloody useful, fix
the darn thing so it doesn’t introduce unnecessary exceptions to the
impeccable consistent and beautiful Ruby oo language!

Try to fix the following without redefining the accessor for ‘num’
instance variable within the class definition hence invalidating the use
of Struct:

S = Struct.new(:num)
class S
attr_accessor :num
def initialize(n); @num=n+5; end
def num=(n); @num = n+5; end
end

s = S.new(3)
puts s.num #=> 8
s.num = 4
puts s.num #=> 9

Regards,
Igor

Yossef M. wrote in post #1081055:

Ruby is not screwed up, at least not in the ways you say it is. The
class S line isn’t declaring a new class named S, it’s reopening the class
created by Struct.new.

Thanks for the explanation.

On Tue, Oct 23, 2012 at 7:44 PM, Igor P. [email protected]
wrote:

Due to the problems shown above, perhaps Struct should be taken out of
Ruby paradigm all together.

I strongly object. Struct is a very useful tool I use all the time to
define data containers quickly.

irb(main):001:0> S = Struct.new(:num)
=> S
irb(main):002:0> class S; def change_to(n) self.num=n end end
=> nil
irb(main):003:0> s = S.new 3
=> #
irb(main):004:0> s.num
=> 3
irb(main):005:0> s.change_to 123
=> 123
irb(main):006:0> s.num
=> 123

However, except in limited initialization circumstances, the use of ‘@’
variables should be prohibited, and from the point of view of strict
OOP, it is, by convention!

Right, making using accessor methods a habit is a good idea because it
makes up for more module and thus flexible code.

The use of ‘Struct’ should also be
discouraged, and even worse, mixing Struct with class definitions and
usage is ill advised.

Sorry, but this is nonsense. Btw, we can simplify the method definition
a bit:

S = Struct.new :num do
def change_to(n)
self.num = n
end
end

The above problem can be solved if you use accessor methods in place of
‘@’ variable:

Exactly.

Kind regards

robert

7stud – wrote in post #1081205:

Thanks for the explanation.

Maybe next time you ask before making a big fuss about how Ruby is
horribly broken.

On Thu, Oct 25, 2012 at 12:25 PM, Igor P.
[email protected]wrote:

I have no objection if anybody wishes to use the broken Struct as it is
in his/her own short-cuts and sketches. However, if allowed there, who
is going to prevent its use in main-stream programming? The trouble is,
this stuff is so broken in situations becomes totally useless. Its
inconsistencies with regular ruby classes make things unacceptable on
the first place, and I can not believe anyone would defend its
continuous usage in this broken fashion! If it is so bloody useful, fix
the darn thing so it doesn’t introduce unnecessary exceptions to the
impeccable consistent and beautiful Ruby oo language!

You yourself said

However, except in limited initialization circumstances, the use of ‘@’
variables should be prohibited, and from the point of view of strict
OOP, it is, by convention!

However, you went on to say

The use of ‘Struct’ should also be discouraged

which is where we differ. I much prefer to discourage the use of ivars
(‘@’
variables, or “instance variables”) and stick to the object contract/API
internally. I use Struct in production code because it’s fabulously
useful.

Just because something doesn’t work the way you insist it must doesn’t
mean
it’s broken.

Todd B. wrote in post #1080823:

It’s nonsense code, but I’m curious as to what is going on behind the
scenes…

irb(main):001:0> S = Struct.new(:num)
=> S
irb(main):002:0> class S; def change_to n; @num = n; end; end
=> nil
irb(main):003:0> s = S.new(3)
#
irb(main):004:0> s.num = 4
=> 4
irb(main):005:0> s.num
=> 4
irb(main):006:0> s.change_to(5)
=> 5
irb(main):007:0> s.num
=> 4
irb(main):008:0> s.instance_variables
=> [:@num]
irb(main):009:0> s.instance_variable_set(:@num, 5)
=> 5
irb(main):010:0> s.num
=> 4

Same thing happens if I don’t define class S, but just define a method
on the instance… def s.change_to n; @num = n; end

Ruby is not screwed up!

Think about why do you want to use Struct at first place, what Struct
gives you? If you think in this direction, then it makes sense that
those attribute created when you instantiate a Struct are encapsulated.
It is the responsibility of the Struct to handle those data not the
class you created using something like Foo = Struct.new. If you really
need a instance variable with the same name as one of the attribute from
Struct, why on earth would you need Struct at the beginning?

High-levelly speaking, Struct gives you something as simple as
Hash(since you can do things like obj[‘attr’] when obj is a struct, and
you can iterate!), but less than a real class (you are not able to
define a method on struct).

The idea is, Struct is awesome if you don’t need the power of Class, but
you want to bring more domain into your code not just a hash like data
structure.

Remember, Ruby is awesome and will be awesome forever! Just kidding, it
won’t…

Yossef M. wrote in post #1081211:

Just because something doesn’t work the way you insist it must doesn’t
mean it’s broken.

If you claim that using accessors and ‘@’ variables when reopening
Struct classes with class idiom isn’t broken, that’s fine, but my last
example I gave, clearly indicates differently! If you do not need it to
be consistent in those simple circumstances you use Struct idiom, that
by no means discredits a more elaborate use, which clearly is broken,
redundant and inconsistent, as I have shown in my previous sample code
snippet, where you need to annul the Struct by the {{ attr_accessor :num
}} line when you reopen the ‘S’ class, in order to be able to define
more elaborate custom initialization accessor methods.

Let’s not waste any more time on this issue. I think we all can tolerate
this apparent glitch. At the same time reiterating that there should be
as few exceptions as possible in the language grammar and its use
certainly is not an out of line proposition. Our discourse, as far as I
am concerned has pointed to all these points, and any previous as well
as further suggestions that Ruby is broken, have nothing to do with what
was originally intended.

Regards, igor

On Oct 25, 2012, at 13:22 , Igor P. [email protected] wrote:

Yossef M. wrote in post #1081211:

Just because something doesn’t work the way you insist it must doesn’t
mean it’s broken.

exactly.

If you claim that using accessors and ‘@’ variables when reopening
Struct classes with class idiom isn’t broken, that’s fine, but my last
example I gave, clearly indicates differently!

Clearly? No. Your example is wrong, as shown by a simple -w:

9999 % ruby19 -w
S = Struct.new(:num)
class S
attr_accessor :num
def initialize(n); @num=n+5; end
def num=(n); @num = n+5; end
end
-:3: warning: method redefined; discarding old num
-:3: warning: method redefined; discarding old num=
-:5: warning: method redefined; discarding old num=

Reimplementing the accessors breaks the explicit contract of Struct.
There’s no point in doing so at all and your example is simply wrong.
Breaking the contract of initialize is also wrong, you’re not even
calling super.

If you do not need it to
be consistent in those simple circumstances you use Struct idiom, that

Struct is not an idiom. It is a class. Its internals are an
implementation detail. From the very first line of the rdoc on the
class: “A Struct is a convenient way to bundle a number of attributes
together, USING ACCESSOR METHODS” (emphasis mine).

by no means discredits a more elaborate use, which clearly is broken,
redundant and inconsistent, as I have shown in my previous sample code
snippet, where you need to annul the Struct by the {{ attr_accessor :num
}} line when you reopen the ‘S’ class, in order to be able to define
more elaborate custom initialization accessor methods.

“Be able to”? You’re blowing this WAY out of proportion:

% ruby19 -w
S = Struct.new(:num)
class S
def initialize n
super
self.num = n + 5
end
end
p S.new(10).num # => 15

Looks like it is more than able to.

Let’s not waste any more time on this issue. I think we all can tolerate
this apparent glitch. At the same time reiterating that there should be
as few exceptions as possible in the language grammar and its use
certainly is not an out of line proposition.

This has nothing to do with grammar. Again, Struct is a class, not
grammar.

Ryan D. wrote in post #1081229:

This has nothing to do with grammar. Again, Struct is a class, not
grammar.

This is like saying verb is a word not grammar. Obviously, you are on a
crusade without having any reason for it, so you either have to bluff
your way or are blissfully unaware what you are mussing. Try fixing my
code snippet in which I demonstrated the inconsistency when mixing
Struct and class. Get the following to run, without annulling the Struct
with {{ attr_accessor :num }}:

S = Struct.new(:num)
class S
def initialize n
super
self.num = n + 5
end
def num=(n); self.num = n + 5; end
end

s = S.new(10).num # => ./teststruct.rb:9:
stack level too deep (SystemStackError)
p s.num #=>
s.num = 1
p s.num #=>

Am I really blowing this out of proportion? I’m just stating the
obvious. This stuff is broken if used for more serious work than playing
with simplistic examples on some forum.

Regards, igor

2012/10/25 Igor P. [email protected]:

This is like saying verb is a word not grammar.

Verb isn’t grammar. It is a word. Please rethink what you just wrote.

def num=(n); self.num = n + 5; end

Well, this is obviously going to cause infinite recursion.

You should use class S < Struct.new(:num) here, and then properly call
super. I’m sure you can find examples of what it does online, do try.

– Matma R.

Bartosz Dziewoński wrote in post #1081236:

Verb isn’t grammar. It is a word. Please rethink what you just wrote.

According to Ruby grammar {{ class S; def @var = value; end }} is the
definition of instance method and again according to Ruby grammar
convention its use is defined as {{ S#var = value }}. Here, the
accessor method can be considered a verb or operator. Idiom {{ class S;
end }} is according to Ruby grammar the definition of class ‘S’. If for
some word grammar seems inappropriate here, I do not intend to convince
you otherwise. Likewise I never meant to say that verb is grammar, but
it nevertheless represents grammatical conception or term and it is in
this context that I used the word, suggesting otherwise is pretending
ignorance.

You should use class S < Struct.new(:num) here, and then properly call
super. I’m sure you can find examples of what it does online, do try.

As for ‘infinite recursion’, it is I, who is objecting to this use all
along and pointing out that Struct should be fixed so it would
understand, conform and obey the Ruby instance variable grammar and
rules.

Use of ‘@’ instance variables is sometimes unavoidable and necessary.
Ruby grammar is clear about its use, except when it comes to the
conundrum of mixing Struct with class reopening, and using instance
variables in this context. The examples above clearly demonstrate these
problems.

Regards, igor

On 26 October 2012 09:19, Igor P. [email protected] wrote:

Bartosz Dziewoński wrote in post #1081236:
Use of ‘@’ instance variables is sometimes unavoidable and necessary.
Ruby grammar is clear about its use, except when it comes to the
conundrum of mixing Struct with class reopening, and using instance
variables in this context. The examples above clearly demonstrate these
problems.

So the whole discussion can be summed up as: monkey-patching is
dangerous, only do it if you understand the implications.

Let’s all play nice now, 'k?

Matthew K., B.Sc (CompSci) (Hons)
http://matthew.kerwin.net.au/
ABN: 59-013-727-651

“You’ll never find a programming language that frees
you from the burden of clarifying your ideas.” - xkcd

On Thu, Oct 25, 2012 at 6:25 PM, Igor P. [email protected]
wrote:

I have no objection if anybody wishes to use the broken Struct as it is
It’s not broken. It works like designed.

in his/her own short-cuts and sketches. However, if allowed there, who
is going to prevent its use in main-stream programming?

Nobody, why should they?

The trouble is,
this stuff is so broken in situations becomes totally useless. Its
inconsistencies with regular ruby classes make things unacceptable on
the first place, and I can not believe anyone would defend its
continuous usage in this broken fashion! If it is so bloody useful, fix
the darn thing so it doesn’t introduce unnecessary exceptions to the
impeccable consistent and beautiful Ruby oo language!

You are repeating that it is broken but you fail to explain what
exactly is broken. That’s not a basis for discussions.

s = S.new(3)
puts s.num #=> 8
s.num = 4
puts s.num #=> 9

Easy:

S = Struct.new :num do
alias _initialize initialize
def initialize(n)
super
self.num = n
end

alias _num= num=
def num=(n) self._num= n + 5 end
end

s = S.new 0
p s.num

Btw, having an accessor that changes the value in this way is probably
a questionable practice in itself.

Cheers

robert