Bug #7844

include/prepend satisfiable module dependencies are not satisfied

Added by Yusuke Endoh about 2 years ago. Updated 10 months ago.

[ruby-core:52208]
Status:Assigned
Priority:Normal
Assignee:Yukihiro Matsumoto
ruby -v:ruby 2.0.0dev (2013-02-13 trunk 39225) [x86_64-linux] Backport:

Description

Hello,

module P
  def m; puts "P"; super; end
end
module Q
  def m; puts "Q"; super; end
  include P
end
module R
  def m; puts "R"; super; end
  prepend Q
end
module S
  def m; puts "S"; super; end
  include R
end
class A
  def m; puts "A"; super; end
  prepend P
  include S
end
A.new.m #=> P, R, A, S, Q

This code has five module dependencies, but only two are satisfied.

  • Q includes P, which is not satisfied: P#m precedes Q#m.
  • R prepends Q, which is not satisfied: R#m precedes Q#m.
  • S includes R, which is not satisfied: R#m precedes S#m.
  • A prepends P, which is satisfied: P#m precedes A#m.
  • A includes S, which is satisfied: A#m precedes S#m.

Note that all the dependencies can be satisfied at all:

A.new.m #=> Q, P, A, S, R

Yusuke Endoh mame@tsg.ne.jp

History

#1 Updated by Yukihiro Matsumoto about 2 years ago

I believe the behavior is undefined (or implementation defined) when module dependency has contradiction.
And preferably error should be raised when contradiction detected.

Matz.

#2 Updated by Yusuke Endoh about 2 years ago

matz (Yukihiro Matsumoto) wrote:

I believe the behavior is undefined (or implementation defined) when module dependency has contradiction.
And preferably error should be raised when contradiction detected.

Thank you, I agree with the policy.

However, is there any 'contradiction' in this case? No pair of these dependencies conflicts. There are no cycles.
Actually, they are satisfiable. A solution can be found by using the topological sorting.

Yusuke Endoh mame@tsg.ne.jp

#3 Updated by Marc-Andre Lafortune about 2 years ago

As I stated before (#1586), I feel that the solution is easy:

A.ancestors = [*A.prepended_modules.flat_map(&:ancestors), A, *A.included_modules.flat_map(&:ancestors), *A.superclass.ancestors]

In the given example, this would be:

  P, A, S, Q, P, R, Object, Kernel, BasicObject

It makes absolutely no sense to me that R could come before A and believe it is clearly a major problem. R is never prepended, nor included in a prepended module!

Matz: how would you explain that R can be called before A in that example?

#4 Updated by Nobuyoshi Nakada 10 months ago

  • Description updated (diff)

Also available in: Atom PDF