On Fri, Oct 26, 2012 at 5:44 PM, Igor P. [email protected]
wrote:
Robert K. 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”.
This has nothing to do with Ruby’s grammar. I think I know what
you’re up to but the term is simply wrong.
Every solution that you come up with introduces more complications and
weird or even unacceptable run-arounds, like your underscore aliasing.
These are basically introduced because you chose to have a property
setter which modifies the value on the fly - this is also a
questionable approach.
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).
I find that rather foggy. What code are you talking about exactly?
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.
I do not think the thing that you call “initialization grammar”
exists. Inherit from Hash and Array and you won’t see instance
variables either. Actually, all classes which are implemented in C
(which includes std lib as well as extensions) do have the liberty to
store the state in any way they like. Generally using accessors to
modify state is a safer method. It just happens that in most user
written classes the direct access to instance variables works and
gives expected results.
If
the idiom {{ class A < Struct.new(:num); end }} makes Struct a
superclass of A,
It doesn’t - at least not directly. Struct.new creates a new class
which sits between them.
irb(main):002:0> class A < Struct.new(:num); end
=> nil
irb(main):003:0> A.ancestors
=> [A, #Class:0x8b37678, Struct, Enumerable, Object, Kernel,
BasicObject]
then class A and indeed all its subclasses should
inherit @num instance method from Struct.
First of all @num is not a method. This is an instance variable read
access.
The method does not exist in Struct - and it can’t for obvious reasons.
irb(main):005:0> Struct.instance_method :num
NameError: undefined method num' for class
Struct’
from (irb):5:in instance_method' from (irb):5 from /usr/local/bin/irb19:12:in
’
Instead it’s defined in the anonymous superclass of A:
irb(main):006:0> A.instance_method :num
=> #<UnboundMethod: A(#Class:0x8b37678)#num>
irb(main):007:0> A.instance_method(:num).owner
=> #Class:0x8b37678
I am starting to wonder whether you understand how Struct works.
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
It’s obvious that you get wrong values if you create flawed logic.
This is also not what I had suggested.
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!
That is not true. If you only want to modify values during
initialization you can always do
S = Struct.new :a, :b do
def initialize(a, b)
self.a = a * 33
self.b = b / 10
end
end
irb(main):026:0> x = S.new 100, 200
=> #
irb(main):027:0> x.a
=> 3300
irb(main):028:0> x.b
=> 20
You even can omit the super invocation.
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!
That’s not a dirty trick - it’s just a slightly inconvenient way to
use Struct because one cannot use #initialize as predefined. Other
than that this is what you would for a regular class as well. But you
still benefit from properly defined #eql?, #hash, #[] etc. You’re
simply not using the default constructor but still gain useful
functionality. Heck, it doesn’t even involve meta programming. For
me this does not qualify as hack or trickery.
Cheers
robert