What linearization algorithm does Ruby use for Mixins?

In Ruby, modules can include other modules, as a form of multiple inheritance. To test this, I wrote the following program based on an example from an article on linearizing C3:

module O
  def doIt()
    super if defined?(super)
    puts "O"
  end
end

module F
  include O
  def doIt()
    super if defined?(super)
    puts "F"
  end
end

module E
  include O
  def doIt()
    super if defined?(super)
    puts "E"
  end
end

module D
  include O
  def doIt()
    super if defined?(super)
    puts "D"
  end
end

module C
  include F
  include D
  def doIt()
    super if defined?(super)
    puts "C"
  end
end

module B
  include E
  include D
  def doIt()
    super if defined?(super)
    puts "B"
  end
end

class A
  include C
  include B
  def doIt()
    super if defined?(super)
    puts "A"
  end
end

A.new.doIt

What (on Ruby 1.9.3) outputs:

O
F
E
D
C
B
A

How does Ruby determine the resolution order of a method in this case?

+4
source share
2 answers

I wrote an article describing the algorithm here: http://norswap.com/ruby-module-linearization/

Insert the most important bits:

Include = Struct.new :mod
Prepend = Struct.new :mod

# Returns the ancestry chain of `mod`, given the environment `env`.
#
# No distinctions are made between classes and modules: where a class extends
# another class, that class is treated as the first included module.
#
# params:
# - mod: Symbol
#   Represents a module.
# - env: Map<Symbol, Array<Include|Prepend>>
#   Maps modules to inclusions, in order of apperance.

def ancestors (mod, env)
  chain = [mod]
  includes = env[mod]
  includes.each { |it| insert(mod, it, env, chain) }
  chain
end


def insert (mod, inclusion, env, chain)
  i = inclusion.is_a?(Prepend) ? 0 : 1
  ancestors(inclusion.mod, env).each do |it|
    raise ArgumentError('cyclic include detected') if it == mod
    j = chain.find_index it
    if not j.nil?
      i = j + 1 if j >= i
      next
    end
    chain.insert(i, it)
    i += 1
  end
end

Try to get some kind of intuition. Let's start with some observations:

  • If B is turned on after A, B takes precedence over A.

  • If B extends or includes A, B takes precedence over A.

  • X X.

  • , , .

: , , , , , !

, Ruby . Ruby , . .

. . , , , !

+1

, . Ruby, :

M A M A, , A.

, Scala MRO "" MRO, PEP 253 ( Python).

0

Source: https://habr.com/ru/post/1569697/


All Articles