What's so special about operators, built-in classes and modules?

Then you will have complex network of classes instead of simple tree
structure, sharing code with modules.

How does a complex network of classes differ from a complex network of mixins? The main difference that I see is, as Ara pointed out, #is_a?. But I could be missing something. I usually am.

Hmm... sounds weird. I do tend to use Unicode characters,
specifically quotation marks. How does this look?

http://www.ruby-talk.org/cgi-bin/scat.rb/ruby/ruby-talk/149716

Devin

twifkak@comcast.net ha scritto:

Then you will have complex network of classes instead of simple tree
structure, sharing code with modules.

How does a complex network of classes differ from a complex network of mixins? The main difference that I see is, as Ara pointed out, #is_a?. But I could be missing something. I usually am.

you don't have a network of mixins, just a tree

twifkak@comcast.net schrieb:

Then you will have complex network of classes instead of simple
tree structure, sharing code with modules.

How does a complex network of classes differ from a complex network
of mixins? The main difference that I see is, as Ara pointed out,
#is_a?. But I could be missing something. I usually am.

For me, the main difference between modules and classes is that classes "define" state (instance variables), not only behaviour (methods). One classic problem with multiple inheritence is the so called diamond:

   class A
     def initialize(val)
       @var = val
     end
   end

   class B
     include A
     def initialize
       super(5)
     end
   end

   class C
     include A
     def initialize
       super(42)
     end
   end

   class D
     include B
     include C
   end

   d = D.new

Now, how many instances of A are in d? Two (one for B, one for C) or one? If one, what is the value of @var? You avoid such problems with mixins (modules).

Regards,
Pit

Pit Capitain wrote:

twifkak@comcast.net schrieb:

Then you will have complex network of classes instead of simple
tree structure, sharing code with modules.

How does a complex network of classes differ from a complex network
of mixins? The main difference that I see is, as Ara pointed out,
#is_a?. But I could be missing something. I usually am.

For me, the main difference between modules and classes is that classes
"define" state (instance variables), not only behaviour (methods). One
classic problem with multiple inheritence is the so called diamond:

Actually, wouldn't it make more sense to say that neither classes nor
modules define state, but rather that methods do?

classirb(main):001:0> class A; attr_accessor :x; end
=> nil
irb(main):002:0> a = A.new
=> #<A:0xb7d85bb0>
irb(main):003:0> a.x = 6
=> 6
irb(main):004:0> a
=> #<A:0xb7d85bb0 @x=6>
                  ^^^^
irb(main):005:0> module M; attr_accessor :y; end
=> nil
irb(main):006:0> class A; include M; end
=> A
irb(main):007:0> a
=> #<A:0xb7d85bb0 @x=6>
irb(main):008:0> a.y = 8
=> 8
irb(main):009:0> a
=> #<A:0xb7d85bb0 @y=8, @x=6>
                  ^^^^

gabriele renzi <surrender_it@remove-yahoo.it> writes:

you don't have a network of mixins, just a tree

This is just plain wrong. Mixin inheritance networks can
come in the shape of any directed acyclic graph.

···

--
Daniel Brockman <daniel@brockman.se>

Pit Capitain <pit@capitain.de> writes:

For me, the main difference between modules and classes is
that classes "define" state (instance variables), not only
behaviour (methods).

Yet there are many examples of modules whose modules set
instance variables, and there are many examples of classes
whose methods do not.

That alleged difference is not grounded in reality.

One classic problem with multiple inheritence is the so
called diamond:

   class A
     def initialize(val)
       @var = val
     end
   end

   class B
     include A
     def initialize
       super(5)
     end
   end

   class C
     include A
     def initialize
       super(42)
     end
   end

   class D
     include B
     include C
   end

   d = D.new

As has been pointed out countless times already, the diamond
inheritance problem already exists in Ruby:

   module A
     def initialize(val)
       @var = val
     end
   end

   module B
     include A
     def initialize
       super(5)
     end
   end

   module C
     include A
     def initialize
       super(42)
     end
   end

   class D
     include B
     include C
   end

Now, how many instances of A are in d?

One (assuming you mean how many are in D's ancestor list).

···

--
Daniel Brockman <daniel@brockman.se>

Except that, in Ruby, it's all made into a tree.

-austin

···

On 7/27/05, Daniel Brockman <daniel@brockman.se> wrote:

gabriele renzi <surrender_it@remove-yahoo.it> writes:
> you don't have a network of mixins, just a tree
This is just plain wrong. Mixin inheritance networks can
come in the shape of any directed acyclic graph.

--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca

Hi,

···

In message "Re: What's so special about operators, built-in classes and modules?" on Thu, 28 Jul 2005 09:42:54 +0900, Daniel Brockman <daniel@brockman.se> writes:

you don't have a network of mixins, just a tree

This is just plain wrong. Mixin inheritance networks can
come in the shape of any directed acyclic graph.

If you treat classes and modules equally as graph nodes. But if you
focus on classes, they form a simple tree, with leaves (modules)
attached to them. Besides that, there's no diamond inheritance in
Mixin inheritance.

              matz.

did you run this? this is most defintely __not__ the diamond problem - it is
strictly a tree:

   harp:~ > cat a.rb
   module A
     def initialize(val)
       p 'in A'
       @var = val
     end
   end

   module B
     include A
     def initialize(*a)
       p 'in B'
       super(5)
     end
   end

   module C
     include A
     def initialize(*a)
       p 'in C'
       super(42)
     end
   end

   class D
     include B
   end

   p D::new

   class D
     include C
   end

   p D::new

   harp:~ > ruby a.rb
   "in B"
   "in A"
   #<D:0xb75ce160 @var=5>
   "in C"
   "in B"
   "in A"
   #<D:0xb75ce05c @var=5>

if this were the diamond problem ruby would have needed to choose between
__either__ the 'initialize' in B or C but, because we are walking a tree, it
simply does both - each 'include' statement is adding depth to the search but
it never becomes a complex graph that needs an algorithim which resolves
conflicts. see

   Multiple inheritance - Wikipedia

for more on this. again - there no ambiguity in which 'initialize' ruby calls
(both) and therefore no diamond problem.

hth.

-a

···

On Thu, 28 Jul 2005, Daniel Brockman wrote:

As has been pointed out countless times already, the diamond inheritance
problem already exists in Ruby:

  module A
    def initialize(val)
      @var = val
    end
  end

  module B
    include A
    def initialize
      super(5)
    end
  end

  module C
    include A
    def initialize
      super(42)
    end
  end

  class D
    include B
    include C
  end

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
My religion is very simple. My religion is kindness.
--Tenzin Gyatso

===============================================================================

Yukihiro Matsumoto wrote:

If you treat classes and modules equally as graph nodes. But if you
focus on classes, they form a simple tree, with leaves (modules)
attached to them. Besides that, there's no diamond inheritance in
Mixin inheritance.

What would be bad about allowing the include method to take Classes in addition to Modules? (And to keep everything else the same, including the distinction between subclassing (via "<") and mixin-ing (via "include").)

If nothing, then what would be the reason to keep Modules around?

Devin

Ara.T.Howard said:

As has been pointed out countless times already, the diamond inheritance
problem already exists in Ruby:

  module A
    def initialize(val)
      @var = val
    end
  end

  module B
    include A
    def initialize
      super(5)
    end
  end

  module C
    include A
    def initialize
      super(42)
    end
  end

  class D
    include B
    include C
  end

did you run this? this is most defintely __not__ the diamond problem - it
is strictly a tree:

     A
    / \
   B C
    \ /
     D

Ruby does linearizes the method resolution order to D,C,B,A.

if this were the diamond problem ruby would have needed to choose between
__either__ the 'initialize' in B or C but,

Correct, and it chooses C (which in turn calls B because of the super in C).

because we are walking a tree,
it simply does both - each 'include' statement is adding depth to
the search but it never becomes a complex graph that needs an
algorithim which resolves conflicts.

Actually, there *is* an algorithm that reduces the graph to a simple
linear search. As modules are included, the included module's search
order is appended to the beginning of the new search order, minus any
modules that are already included.

In fact, Ruby's MRO algorithm for modules is very similar to Python's
(old[1]) MRO for multiple inheritance (except Ruby favors the last module
added last and Python favors the first class added).

see

   Multiple inheritance - Wikipedia

I found this particular entry to be less helpful than other resources.
The problem is rarely which method to call (which actually is not a
diamond inheritance problem ... it can arise whenever two base classes
share a method with a common name). The big problem with diamond
inheritance is the question of instance data ... do you have one or two
copies?

(and given the way Ruby handles instance variables, I find it hard to
imagine the answer to this question would ever be two).

···

On Thu, 28 Jul 2005, Daniel Brockman wrote:

From my point of view, it looks like a diamond include structure, i.e.

for more on this. again - there no ambiguity in which 'initialize' ruby
calls (both) and therefore no diamond problem.

--
-- Jim Weirich jim@weirichhouse.org http://onestepback.org
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)

[1] I understand that modern Python supports a different algorithm than
the one described on the wikipedia page. See
http://www.python.org/2.3/mro.html for details.

you don't have a network of mixins, just a tree

This is just plain wrong. Mixin inheritance networks
can come in the shape of any directed acyclic graph.

Except that, in Ruby, it's all made into a tree.

Excuse me? Using a shorthand notation for Module#include,

   module A ; end
   module B < A ; end
   module C < A ; end
   module D < B, C ; end

where is the graph "made into a tree"?

Are you talking about the linearization of superclasses and
included modules? In which case, why do you say "tree"?

···

--
Daniel Brockman <daniel@brockman.se>

Yukihiro Matsumoto <matz@ruby-lang.org> writes:

Mixin inheritance networks can come in the shape of any
directed acyclic graph.

If you treat classes and modules equally as graph nodes.

You don't even have to involve classes. Just modules alone
can form an inheritance network in the shape of any DAC.

But if you focus on classes, they form a simple tree,
with leaves (modules) attached to them.

Agreed. Classes can only form trees.

Besides that, there's no diamond inheritance in
Mixin inheritance.

This comes up again and again, and I never understand it.
Two years ago, you claimed the same thing:

This combination is indeed a restricted multiple
inheritance. [Thanks to single class inheritance],
we won't have diamond inheritance problem,

Mathieu Bouchard replied[1],

module A; end
module B; include A; end
module C; include A; end
class D; include C,B; end

This is a diamond inheritance pattern.

but you never replied back to him.

I'd appreciate it if someone could clarify once and for all
how the diamond inheritance problem doesn't exist in Ruby.

···

--
Daniel Brockman <daniel@brockman.se>

[1] <http://www.ruby-talk.org/cgi-bin/scat.rb/ruby/ruby-core/1472&gt;

Hi,

···

In message "Re: What's so special about operators, built-in classes and modules?" on Thu, 28 Jul 2005 11:57:32 +0900, Devin Mullins <twifkak@comcast.net> writes:

What would be bad about allowing the include method to take Classes in
addition to Modules? (And to keep everything else the same, including
the distinction between subclassing (via "<") and mixin-ing (via
"include").)

If we allow "include" to take classes, it's a plain multiple
inheritance. We should stand all the complexity of multiple
inheritance by that. I might have misunderstood you since I'm not
sure what you meant by "keeping everything else the same".

              matz.

My opinion is that it's good to have two different types there, if only for distinction. I can imagine the questions of, "Did I include that class or just inherit from it?"

That's just my opinion though.

James Edward Gray II

···

On Jul 27, 2005, at 9:57 PM, Devin Mullins wrote:

What would be bad about allowing the include method to take Classes in addition to Modules? (And to keep everything else the same, including the distinction between subclassing (via "<") and mixin-ing (via "include").)

If nothing, then what would be the reason to keep Modules around?

did you run this? this is most defintely __not__ the diamond problem - it
is strictly a tree:

From my point of view, it looks like a diamond include structure, i.e.

    A
   / \
  B C
   \ /
    D

Ruby does linearizes the method resolution order to D,C,B,A.

right - we are saying the same thing - the diamond is reduced to a line - ergo
no 'diamond' problem - it's avoided by requiring the programmer to order the
line via 'include'.

because we are walking a tree,
it simply does both - each 'include' statement is adding depth to
the search but it never becomes a complex graph that needs an
algorithim which resolves conflicts.

Actually, there *is* an algorithm that reduces the graph to a simple linear
search. As modules are included, the included module's search order is
appended to the beginning of the new search order, minus any modules that
are already included.

In fact, Ruby's MRO algorithm for modules is very similar to Python's
(old[1]) MRO for multiple inheritance (except Ruby favors the last module
added last and Python favors the first class added).

this is definitely more correct...

   Multiple inheritance - Wikipedia

I found this particular entry to be less helpful than other resources. The
problem is rarely which method to call (which actually is not a diamond
inheritance problem ... it can arise whenever two base classes share a
method with a common name). The big problem with diamond inheritance is the
question of instance data ... do you have one or two copies?

sure... i just wast just trying to help the OP see that we currently never
actually end up with diamonds (tree does not degrade to graph) that require
ruby to resolve the search order. currently it is entirely up to programmer
who can chose based on the order of module inclusion. for classes it's even
simpler. in otherwords the search order is always totally explicit and shown
in the source rather than implicit as defined by some search algorithim used
by the interpreter... and, by extension, that making 'include' mean 'inherit'
would indeed setup a diamond issue that ruby would need to handle implicitly
(mi). of course there are possibly other solutions...

(and given the way Ruby handles instance variables, I find it hard to
imagine the answer to this question would ever be two).

that would certainly be confusing...

cheers.

-a

···

On Fri, 29 Jul 2005, Jim Weirich wrote:
--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
My religion is very simple. My religion is kindness.
--Tenzin Gyatso

===============================================================================

Jim Weirich wrote:

I found this particular entry to be less helpful than other resources.
The problem is rarely which method to call (which actually is not a
diamond inheritance problem ... it can arise whenever two base classes
share a method with a common name). The big problem with diamond
inheritance is the question of instance data ... do you have one or two
copies?

(and given the way Ruby handles instance variables, I find it hard to
imagine the answer to this question would ever be two).

Dim memory... wasn't there a proposal to have per-module (or class)
instance variables, so that different parts of the inheritance chain
don't step on each other?

Wouldn't this introduce the diamond problem?

Take a case like this:

  module Counts
    def inc
      @count ||= 0
      @count += 1
    end

    def cur
      @count
    end
  end

  module CountsDucks
    include Counts

    def saw_a_duck
      inc
    end

    def duck_count
      cur
    end
  end

  module CountsChickens
    include Counts

    def saw_a_chicken
      inc
    end

    def chicken_count
      cur
    end
  end

  class C
    include CountsDucks
    include CountsChickens
  end

  c = C.new

  c.saw_a_duck
  c.saw_a_duck
  c.saw_a_chicken

  p c.duck_count
  p c.chicken_count

Now, the output is

3
3

But using per-module instance variables, you would want this to be

2
1

This seems like a case where you would want there to be two copies of
the (ivars defined by the) Counts module.

Daniel Brockman wrote:

Mathieu Bouchard replied[1],

>>> module A; end
>>> module B; include A; end
>>> module C; include A; end
>>> class D; include C,B; end
>>>
>>> This is a diamond inheritance pattern.

but you never replied back to him.

I'd appreciate it if someone could clarify once and for all
how the diamond inheritance problem doesn't exist in Ruby.

B/c ruby _linearizes_ the inclusions.

irb(main):001:0> module A; end
=> nil
irb(main):002:0> module B; include A; end
=> B
irb(main):003:0> module C; include A; end
=> C
irb(main):004:0> class D; include C,B ; end
=> D
irb(main):005:0> D.ancestors
=> [D, C, B, A, Object, Kernel]

Method lookup will proceed in that order.

HTH,
T.

[Warning ... I'm afraid I got a bit long winded ... sorry]

I'd appreciate it if someone could clarify once and for all
how the diamond inheritance problem doesn't exist in Ruby.

First, some clarification: What is the problem with diamond inheritance?
Diamond inheritance is a pattern of MI that has a parent class (or module in
Ruby's case) that is reachable via different inheritance paths. The specific
term for this is Repeated Inheritance.

The basic problem with Repeated Inheritance is how instance variables defined
in the repeated class should be handled. Should there be one copy total, or
one copy for each repeated parent class.

(An aside: Another thread on this topic indicated that method resolution was
a problem in repeated inheritance. That is incorrect, it is an issue in any
form of multiple inheritance (even the module variety), and does not specific
to repeated inheritance).

Ok, now that we've clarified the question, we can answer it: Why isn't
repeated inheritance a problem with modules?

Some people will say its because modules don't define state. Although
correct, its misleading because it implies that classes do define state.
Classes (in Ruby) don't define state either.

Wait a minute, let me say that in a better way!

Module (and Classes) in Ruby don't contain any /instance/ state. The instance
variables in a ruby object are managed by the instance itself, not the
classes. This is different from most other OO languages.

Most other languages think of objects as little areas of memory. Consider a
class B that inherits from A. Its memory layout would look something like
this:

    ><--- A --->|
    ><------------- B ------>|

The memory for the A part of the object takes up the first section of memory
of a B object. In fact, someone looking at the beginning of the B object
would see a real, live A object in memory. That's how polymorphism works in
languages like C++.

When you have repeated inheritance, the whole memory layout scheme gets all
screwed up. Lets introduce two new classes, C which also inherits from A,
and D which multiply inherits from B and C. What does the memory layout of D
look like?

    ><--- A --->| |<--- A --->|
    ><------------- B ------>|<------------- C -------->|
    ><------------------------------------------------------ D --->|

Yep, two copies of A. One for B and one for C. And that's the problem. Most
times you only want one copy of A.

Different languages handle the repeated inheritance differently. C++
introduces "virtual" inheritance, where the base class is not located in the
same memory area, but is (possibly) displaced. Java only allows MI in
interfaces (and since interfaces have no data memory layout, the problem
doesn't arise).

Eiffel has a flexible approach to repeated inheritance. Any feature (Eiffel's
term for methods and instance variables) that ends up with the same name in
the final class, gets only one copy of that feature. You can rename features
as you inherit, so if a feature ends up with two names in the final class,
you get two copies of it, each with its own name (so there is no ambiguity
either ... very clever). Eiffel's solution is very clean for the developer,
but really hard to implement correctly.

Getting back to Ruby ... The whole memory layout problem is brought about by
thinking of variables as shoeboxes which hold values. Once you have
shoeboxes, you got to pay attention to how you arrange them.

Ruby objects don't have a memory layout for the instance variables. In fact,
it is quite possible that two objects of the same class don't even have the
same instance variables.

So, in summary, why is diamond (repeated) inheritance not a problem in Ruby
modules?

Because the memory layout issues that force multiple copies of a base class
just don't exist in Ruby.

···

On Thursday 28 July 2005 10:00 pm, Daniel Brockman wrote:

--
-- Jim Weirich jim@weirichhouse.org http://onestepback.org
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)

Daniel,

I haven't really followed this VERY long thread completely, but
I'd thought I'd jump in. What was the exact proposal to unify
class and module? Without much thought/analysis, it looks like
either of these could be done to unify class and module:

1. Allow an instance of a module to be created: "new" should be
a "class" method of a module and work just like it does with a
class. I think this would allow a module to do everything a
class can do.

2. Allow a class to be included just like a module can. I
think this would allow a class to do what a module can.

Right now, if you wanted to make an instance of a module you'd
have to make a proxy class to do the dirty work. Currently,
doing it like this gives you the most flexibility because you
can mixin/inherit this class/module wherever and make instances
of it:

module Mod
    # put your class/module code here
end

class ModClass
    include Mod
end

x = ModClass.new

I don't see why the language shouldn't allow one to make an
instance of the Mod above rather than having to make an
instance of the proxy class ModClass.

.. or why you can't include a class.

···

____________________________________________________
Start your day with Yahoo! - make it your home page