From: Robert Klemme Date: 2012-10-27T02:17:43+09:00 Subject: Re: Name/symbol/object type clash? What is happening here? On Fri, Oct 26, 2012 at 5:44 PM, Igor Pirnovar wrote: > 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". 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, #, 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 => #)#num> irb(main):007:0> A.instance_method(:num).owner => # 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 -- remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/