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)
#<struct S num=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

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

···

On Wednesday 24 October 2012 Todd Benson wrote

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)
#<struct S num=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

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 #=> s
  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
    def change_num(n); self.num = n; end
  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

···

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

Robert Klemme 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 redefining 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

···

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

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)
#<struct S num=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...

···

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

Yossef Mendelssohn 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

···

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

Ryan Davis 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

···

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

Robert Klemme wrote in post #1081267:

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

I thought my statement "I believe, we all can tolerate this glitch" was
clear enough. But if you insist to define what is broken, the reply is:
"Ruby grammar consistency when it comes to mixing classes with Structs".
Every solution that you come up with introduces more complications and
weird or even unacceptable run-arounds, like your underscore aliasing.
Try using your '_initialize' and then straight 'initialiye' and you'll
create two different behaviours both of whom are wrong. One
instantiating {{ s = S.new(0) }} with @num to 0, and the other adding 5
twice making @num 10, when using {{ self.num = n + 5 }} idiom rather
than {{ @num = n + 5 }}, which Struct does not recognize (this should be
fixed, namely, Struct should honour '@ semantics'; resorting to
'self#var' is not sufficient in all circumstances).

Struct does not honour Ruby's variable inheritance and class
initialization grammar with respect to inheritance, i.e.: all subclasses
have a single set of instance variables in the inheritance hierarchy. If
the idiom {{ class A < Struct.new(:num); end }} makes Struct a
superclass of A, then class A and indeed all its subclasses should
inherit @num instance method from Struct. Accessing Struct's @num via {{
self.num }} works only when you are using straight assignment, however
if you need to invoke any kind of computation, you have to resort to
tricks like aliasing which works only sometimes.

  S = Struct.new :num do
    alias _initialize initialize
  # def _initialize(n) #=> @num==0
    def initialize(n) #=> @num==10
      super
      self.num = n + 5
    end

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

  s = S.new 0
  p s.num #=> 10; ## with: '_initialize' #=>0
  s.num = 100
  p s.num #=> 105

The trouble with Struct is that there is no way to implement
initialization of instance variables in base class (ie. in Struct) that
require more elaborate initialization skims than straight assignment.
You can accomplish this only with regular classes and their inheritance
hierarchies!

When you have to resort to tricks to accomplish things that are not out
of the ordinary, you better avoid those features when working outside of
your quick and dirty domain or "research lab", and Struct certainly
qualifies for that!

Cheers, igor

···

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

Robert, you continue pretending that you proved my point of view wrong
by always providing only half of the solution, because you simply can
not satisfy both requirements simultaneously. Namely, I require that
both 'initialize' method as well as the attribute accessor (i.e.: the
setter) method work correctly in tandem when an expression rather than
the simple assignment is required.

This code of yours simply doesn't work when using Struct, (nor does it
work when you try to reopen 'S' as class, as was initially argued at the
beginning of this thread):

  S = Struct.new :a do
    def initialize(a); self.a = a + 5; end
    alias _a= a=
    def a=(n) self._a= n + 5; end
  end

  s = S.new(0)
  p s.a #=> 10 WRONG!!!
  s.a = 100
  p s.a #=> 105

With the exception of 'to_s' method, you see here the equivalent of the
above code snippet of yours, this time using only classes (which BTW
works correctly):

  class A
    attr_accessor :num
    def initialize(n); @num=n; end
    def to_s; "#{self.class}: num=#@num"; end
  end

  class B < A
    def initialize(n); super; @num = n + 5; end
    def num=(n) @num = n + 5; end
  end

  a = A.new(0)
  p "After: a=A.new(0) ... #{a}" #=> ... A: num=0
  a.num = 100
  p "After: a.num=100 .... #{a}" #=> ... A: num=100

  b = B.new(0)
  p "After: b=B.new(0) ... #{b}" #=> ... B: num=5
  b.num = 100
  p "After: b.num=100 .... #{b}" #=> ... B: num=105

Once again, Struct does not provide satisfactory solution to both above
stated initialization requirements, because it does not obey Ruby's '@'
instance variable semantics, and due to this now revealed Ruby grammar
inconsistency introduced by Struct, exposes broken blundering like yours
as you try to botch your way out of it, when Ruby compounds double
execution of expressions used in class/Struct initialization process
(see my comment in your code: #=> 10 WRONG!!!).

Regards, igor

···

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

Robert Klemme wrote in post #1081589:

  s = S.new(0)
  p s.a #=> 10 WRONG!!!
  s.a = 100
  p s.a #=> 105

Igor, that was not my code. Your claim is wrong. I posted this
upthread:

I consider this red herring, and in continuation you actually repeat,
your half-way solution in which 'initialize' is using straight
assignment rather than expression 'n + 5'! You again ignore the
requirement that both 'initialize' method and the accessor (setter)
method initialize the variable in identical fashion! It is you who is
not sticking to the facts, and who is drawing attention away from the
real issue, which is the fact, that Struct does not allow you to define
custom accessor getters, and should not be used when these are needed.
All my examples very consistently show both sides of the story!

should be taken out of the language. You are free to use or not use
Struct based on your judgement, but please refrain from claiming that
Struct is useless, harmless or should be removed from the library on
such weak evidence.

These are your conclusions and inaccurate interpretations of what I
actually said. Struct indeed is ill suited for the case which I have
presented, which BTW is not at all weak evidence as you would like the
ignorant to believe!

We are spending way too much time on your interpretations of what and
even how things are discussed here. I clearly defined what is required
and where Struct is breaking away from Ruby language rules. I also
stated that we all can tolerate this glitch. However, it is precisely
because of such attitudes towards questionable practices, as you are
constantly demonstrating here, that it would be best if Struct was
pulled out of Ruby all together or fixed, because the way it is
currently implemented for limited use, I may add, only provides basis
for badmouthing this magnificent language. This is my opinion and I you
certainly have not convinced me that I am wrong.

Take it easy,
igor

···

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

Robert Klemme wrote in post #1081662:

Apparently I'm wrong - but where?

I have already told you, where:

""Ruby compounds double execution of expressions used in class/Struct
initialization process (see my comment in your code: #=> 10 WRONG!!!).""

Apparently the "#=> 10 WRONG!!!" line you never wrote, of course you did
not, you blocked it out, once you saw that it gives you the wrong
result, hence you preferred your half-way solutions. If you hadn't,
you'd understood what was happening.

I am glad you finally realized, that even this half-way version to
satisfy my original requirements, reveals how Struct breaks the basic
Ruby class initialization grammar! That is why in situations like this
you can not use it unless you are willing to accept botching into your
coding practices. And by the way, irb is not synchronized with the
interpreter, and does not realizes that Struct is not behaving in
accordance with Ruby class initialization grammar!

···

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

Oh god, are you still fighting over this crap? If this is the biggest
problem Ruby has, Matz must have done something right.

Igor, I tell you, every time you use the word "grammar", god kills a
kitten. You have no clue what this word means, and apparently you don't
want to know. You may have legitimate points, but both you and this
7stud guy come off as whiney amateurs who've found something they don't
understand and now try to play the great language critics, even though
they don't really know how this "grammar" and stuff works.

···

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

Finally a reasonable response, thank you very much, also for the link.

BTW:
  $ ri Struct
  Nothing known about Struct
  $

Is there a way to update ri library?

···

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

unknown wrote in post #1081806:

I think you can do that with `rvm docs generate'

Yes, this worked! {{ ri Struct }}, is there now!

Thank you,
regards

···

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

Okay, I see, thanks.

···

On Tue, Oct 23, 2012 at 12:44 PM, Igor Pirnovar <lists@ruby-forum.com> wrote:

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
    def change_num(n); self.num = n; end
  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

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 #=> s
  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.

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
=> #<struct S num=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

···

On Tue, Oct 23, 2012 at 7:44 PM, Igor Pirnovar <lists@ruby-forum.com> wrote:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Robert Klemme 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!

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.

···

On Thu, Oct 25, 2012 at 12:25 PM, Igor Pirnovar <lists@ruby-forum.com>wrote:

--
-yossef

Yossef Mendelssohn 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.

···

On Oct 25, 2012, at 13:22 , Igor Pirnovar <lists@ruby-forum.com> wrote:

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 Rex

···

2012/10/25 Igor Pirnovar <lists@ruby-forum.com>:

Robert Klemme 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

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.

Try to fix the following without redefining the redefining 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

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

···

On Thu, Oct 25, 2012 at 6:25 PM, Igor Pirnovar <lists@ruby-forum.com> wrote:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/