Comparison of prepended modules
B to class/module
A gives the following results (as expected):
module A; end module B; end A.include B A < B # => true B < A # => false A <=> B # => -1
And prepending module
A gives the following results:
module C; end A.prepend C A < C # => true C < A # => nil A <=> C # => -1
It looks like including and prepending almost do not make difference with respect to module comparison, i.e.,
A < B and
A < C are the same, and
A <=> B and
A <=> C are the same. However, then, the difference between
B < A and
C < A stands out unexplained. I suppose this is a bug. If
C < A were to return
false, then it would be at least consistent.
However, if that was what was intended, then at least to me, it is strange. In that case, I would like to make this a feature request. I would rather expect:
A < C # => false C < A # => true A <=> C # => 1
Updated by sawa (Tsuyoshi Sawada) over 4 years ago
I thought that the ordering relation among modules/classes represents the method call priority.
B means that method look-up first looks in
B; smaller module/class has higher priority. If so, since a module prepended to a class has higher priority than the class, the module should be smaller (
<) than the class. Is my interpretation wrong?
Updated by mame (Yusuke Endoh) 6 months ago
- Status changed from Assigned to Rejected
The current behavior is consistent. The rdoc of
call-seq: mod < other -> true, false, or nil Returns true if <i>mod</i> is a subclass of <i>other</i>.
Note that subclass is not directly related to the method lookup order. Consider the following example:
module M; end class C; prepend M; end C.new.is_a?(M) #=> true
C is a subclass of
C < M should return true. Note that, in terms of method lookup,
M has a higher priority than 'C'.
Updated by Eregon (Benoit Daloze) 6 months ago
- Status changed from Rejected to Open
At the very least it's inconsistent with the order of Module#ancestors:
module M; end class C; prepend M; end > C.ancestors => [M, C, Object, JSON::Ext::Generator::GeneratorMethods::Object, PP::ObjectMixin, Kernel, BasicObject] > C < Object => true > M < C => false > C < M => true
I think no user expects that the "subclass relation" is different than the order of ancestors, isn't it? (and why would it need to be?)
Also, the documentation says nothing about modules or this ad-hoc order which nothing else seems to use.
I think this is a bug and I'd like matz's ruling.
Updated by mame (Yusuke Endoh) 5 months ago
I think no user expects that the "subclass relation" is different than the order of ancestors, isn't it?
Regardless whether users know or not, they are actually different. Consider:
module M; end class C; prepend M; end class D; include M; end
M is a subclass of
D is a subclass of
A to be a subclass of
Note that the order of
Module#ancestors is not specified; the rdoc says nothing.
Module#prepend is a very bad thing that makes the object system complicated.)
Updated by matz (Yukihiro Matsumoto) 5 months ago
- Status changed from Open to Rejected
For the code like below:
module A; end module I include A end p A < I #=> false p A > I #=> true module P prepend A end # current: same as include p A < P #=> false p A > P #=> true
A > P does not mean
P is a subclass of
P includes the method sets defined in
A. So the current behavior should not be changed.