Bug #11878
closedComparison of prepended modules
Description
Including module 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 C
to 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 marcandre (Marc-Andre Lafortune) over 9 years ago
- Assignee set to nobu (Nobuyoshi Nakada)
Indeed.
Updated by nobu (Nobuyoshi Nakada) over 9 years ago
- Status changed from Open to Closed
Applied in changeset r53380.
object.c: fix prepend cmp
- object.c (rb_class_inherited_p): search the corresponding
ancestor to prepended module from prepending class itself.
[ruby-core:72493] [Bug #11878]
Updated by nobu (Nobuyoshi Nakada) over 9 years ago
- Description updated (diff)
- Backport changed from 2.0.0: UNKNOWN, 2.1: UNKNOWN, 2.2: UNKNOWN, 2.3: UNKNOWN to 2.0.0: REQUIRED, 2.1: REQUIRED, 2.2: REQUIRED, 2.3: REQUIRED
Updated by sawa (Tsuyoshi Sawada) over 9 years ago
As far as I see the revision r53380 by Nakada san, it looks like my feature proposal part was rejected. Is this the case?
Updated by nobu (Nobuyoshi Nakada) over 9 years ago
Making prepending class an ancestor of prepended module?
It feels strange a little, to me.
Updated by sawa (Tsuyoshi Sawada) over 9 years ago
I thought that the ordering relation among modules/classes represents the method call priority. A
< B
means that method look-up first looks in A
, then 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 naruse (Yui NARUSE) about 9 years ago
- Status changed from Closed to Assigned
- Assignee changed from nobu (Nobuyoshi Nakada) to matz (Yukihiro Matsumoto)
Updated by mame (Yusuke Endoh) over 5 years ago
- Status changed from Assigned to Rejected
The current behavior is consistent. The rdoc of Module#<
says:
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
This means C
is a subclass of M
. So, C < M
should return true. Note that, in terms of method lookup, M
has a higher priority than 'C'.
Updated by Eregon (Benoit Daloze) over 5 years 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 Eregon (Benoit Daloze) over 5 years ago
Re obj.is_a?(mod)
we can consider it as obj.singleton_class.ancestors.include?(mod)
or even obj.class.ancestors.include?(mod)
in this case.
That's consistent with ancestors
.
Updated by mame (Yusuke Endoh) over 5 years 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
If M
is a subclass of C
, D
is a subclass of C
. Both A.prepend(B)
and A.include(B)
make A
to be a subclass of B
.
Note that the order of Module#ancestors
is not specified; the rdoc says nothing.
(IMO, Module#prepend
is a very bad thing that makes the object system complicated.)
Updated by matz (Yukihiro Matsumoto) over 5 years 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 A
, but P
includes the method sets defined in A
. So the current behavior should not be changed.
Matz.