[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Trickery in the ancestors chain

Paolo Nusco Perrotta

1/24/2007 2:39:00 PM

Try this:

module M
def hello; p 'M'; end
end

class C
def hello; p 'C'; end
end

class D < C; end

class C; include M; end
class D; include M; end
D.ancestors -> [D, C, M, Object, Kernel]
D.new.hello -> "C"


Now restart from a clean slate and change the last few lines:

class D; include M; end
class C; include M; end
D.ancestors -> [D, M, C, M, Object, Kernel]
D.new.hello -> "M"


Ruby silently prevents you from including a module that has been
included by an ancestor. Why?

9 Answers

Brian Mitchell

1/24/2007 3:37:00 PM

0

On 1/24/07, Robert Dober <robert.dober@gmail.com> wrote:
> Stop Press
> I can reproduce that behavior but only on my cygwin ruby 1.8.4 I cannot
> reproduce it in irb, irb seems to produce the correct behaviour (the first
> one for both versions).
>
> As soon as I'll have time I'll fire up tests on my Ubuntu with 1.8.5 and 1.9

Are you sure you entered the entire thing _in_order_? I had the same
sort of mistake when I was reading this post:

http://eigenclass.org/hiki.rb?cmd=view&p=class+hierarchy+introspecti...

It is a great read.

Brian.

Ara.T.Howard

1/24/2007 3:49:00 PM

0

Gavin Kistner

1/24/2007 4:54:00 PM

0

On Jan 24, 8:48 am, ara.t.how...@noaa.gov wrote:
> harp:~ > cat a.rb
> #
> # take one
> #
> module M
> def hello; 'M'; end
> end
> class C
> def hello; 'C'; end
> end
> class D < C; end
> class C; include M; end
> class D; include M; end
> p D.ancestors #=> [D, C, M, Object, Kernel]

I think the issue here is that essential no-op:

module M; end
class C; end
class D < C; end

class C; include M; end
x = D.ancestors
class D; include M; end
y = D.ancestors

p x==y
#=> true

The issue is that Ruby says "Hey, M is already in your ancestor
list...I'm going to prevent you from inserting it into the chain at a
lower level."

Seems like a bug, like it should only prevent the M inclusion if it's
already included directly in the class.

Pit Capitain

1/24/2007 5:04:00 PM

0

Phrogz schrieb:
> (...)
> Seems like a bug, like it should only prevent the M inclusion if it's
> already included directly in the class.

But it works as documented. Why do you want to change it?

Regards,
Pit

Luke Ivers

1/24/2007 5:20:00 PM

0

On Thu, 25 Jan 2007 01:55:07 +0900
"Phrogz" <gavin@refinery.com> wrote:

> On Jan 24, 8:48 am, ara.t.how...@noaa.gov wrote:
> > harp:~ > cat a.rb
> > #
> > # take one
> > #
> > module M
> > def hello; 'M'; end
> > end
> > class C
> > def hello; 'C'; end
> > end
> > class D < C; end
> > class C; include M; end
> > class D; include M; end
> > p D.ancestors #=> [D, C, M, Object, Kernel]
>
> I think the issue here is that essential no-op:
>
> module M; end
> class C; end
> class D < C; end
>
> class C; include M; end
> x = D.ancestors
> class D; include M; end
> y = D.ancestors
>
> p x==y
> #=> true
>
> The issue is that Ruby says "Hey, M is already in your ancestor
> list...I'm going to prevent you from inserting it into the chain at a
> lower level."
>
> Seems like a bug, like it should only prevent the M inclusion if it's
> already included directly in the class.
>
>
Yeah, but isn't this a dangerous road to go down? You will end up with people including a module to get some functionality and re-over-writing inherited methods that were over-writing instance methods in the original module. I guess we should be allowed to do that, but it's probably going to cause more headaches to do it that way than it's going to solve... if you really need to use the original definition of a module's instance method that's been over-written already by a superclass, you have to question why you're using that superclass at all... if you don't want its functionality, but the functionality of something that it's inheriting, maybe you should consider some other sort of object hierarchy?

--
Luke Ivers <technodolt@gmail.com>

Gavin Kistner

1/24/2007 6:04:00 PM

0

On Jan 24, 10:04 am, Pit Capitain <p...@capitain.de> wrote:
> Phrogz schrieb:
>
> > (...)
> > Seems like a bug, like it should only prevent the M inclusion if it's
> > already included directly in the class.
>
>But it works as documented. Why do you want to change it?

I have two, different responses:

1) OK, you're right: if it's documented that way, it's probably not a
bug. But just because it's designed, implemented, and
documented...doesn't mean that it's necessarily the correct behavior.
I'd be interested in hearing arguments for why it makes sense to
prevent a situation that can be achieved via a different call order?

2) I personally don't want to change it; I've never run into this
situation, and it seems very unlikely to me that I would ever have a
situation where this would be needed. When would you find a class that
includes a module, that has a method named the same as that class, and
need to shadow the class you're inheriting from? I was just trying to
explain to Ara the 'bug' that the OP was describing.

Paolo Nusco Perrotta

1/24/2007 7:18:00 PM

0

Catch-all reply:

On Jan 24, 7:03 pm, "Phrogz" <g...@refinery.com> wrote:
> >But it works as documented. Why do you want to change it?I have two, different responses:
>
> 1) OK, you're right

Yes, it's a feature - I found the C implementation in file class.c,
lines 396 and following (for Ruby 1.8.5). Ruby skips the inclusion of M
in D when D already includes M by way of an ancestor (or itself). This
normally prevents me from adding the same module to the ancestors chain
twice. But I can work around this by including the module in D first,
and later in the ancestor.

I see that most people think this is how it should work. I guess I'm
easily surprised, but I wasn't expecting the order of inclusions into a
hierarchy to make a difference. So, I wonder: why does Ruby go to the
trouble of preventing this in the first place? If it does, shouldn't it
do it all the time, not sometimes? If including a module twice is an
error, shouldn't Ruby raise an exception? I don't have a strong opinion
on this, but it feels strange.

Pit Capitain

1/25/2007 8:41:00 AM

0

Phrogz schrieb:
> On Jan 24, 10:04 am, Pit Capitain <p...@capitain.de> wrote:
>> But it works as documented. Why do you want to change it?
>
> I have two, different responses:
>
> 1) OK, you're right: if it's documented that way, it's probably not a
> bug. But just because it's designed, implemented, and
> documented...doesn't mean that it's necessarily the correct behavior.

Absolutely. That's why I asked for reasons to change it.

> I'd be interested in hearing arguments for why it makes sense to
> prevent a situation that can be achieved via a different call order?

Yes, maybe the bug is here:

>> D.ancestors # => [D, M, C, M, Object, Kernel]

But from what I know of the Ruby interpreter, this would be hard to fix.

Regards,
Pit


Pit Capitain

1/25/2007 8:53:00 AM

0

On 24 Jan., 20:17, "Paolo Nusco Perrotta"
<paolo.nusco.perro...@gmail.com> wrote:
> (...) So, I wonder: why does Ruby go to the
> trouble of preventing this in the first place?

Paolo, the Ruby interpreter does a lot to prevent including a module
twice. It's much more than the lines you've found. This is also the
source of a well known problem with Module inclusion, for which there's
no solution yet. All this could be prevented without this behaviour. So
it seems there must be a very good reason to have it, but I don't know
it right now. Perhaps someone else here does?

> If it does, shouldn't it
> do it all the time, not sometimes? If including a module twice is an
> error, shouldn't Ruby raise an exception? I don't have a strong opinion
> on this, but it feels strange.

Yes, I think so too. But as I've written in another post, I think this
would be hard to fix.

Regards,
Pit