More flexible inheritance

A notion came to me yesterday with regards to how we extend classes.
On the one hand we can create a module and include it into a class,
but this will not allow us to override a preexisting method -- for
that we must reopen the class and play the alias and/ or binding game.
Various ideas have been put forth for simplifying and improving the
robustness of this, from #wrap_method, alias_method_chain, as well
as :pre, :post methods and my own cuts (albeit they go a bit further
than just method wrapping). But occurs to me that these add
complexities onto something that is already straightforward if we are
willing to separate out all class concerns into modules. In other
words instead of writing:

  class X
    def a; "a"; end
  end

write:

  module Xa
    def a; "a"; end
  end

  class X
    include Xa
  end

  X.new.a #=> "a"

Then it's easy enough to overlay new behaviors:

  module Xb
    def a; "{"+super+"}"; end
  end

  class X
    include Xb
  end

  X.new.a #=> "{a}"

Since that works so well, it occurs to me, why not make this the
fundamental behavior of defining a class? In other words defining a
class:

  class X
    def a; "a"; end
  end

could be equivalent to writing:

  class X
    include( Module.new{
        def a; "a"; end
    } )
  end

So there would always be a module involved in the definition of a
class. And classes become simply containers of modules.

T.

Trans,

I'm actually in the middle of a week long argument concerning "fat models"
versus "thin models" and with my approach being fat libraries and thin
models. We have a few sites which will share a lot of functionality and
instead of sharing the models, I like sharing libraries that are included in
the models. The idea being that this is easy to do with svn externals on
the lib directory. Then fix a bug or add functionality in one place. Even
though this gives two places to look for methods, I think it's nice and
clean.

Even though my example is for a specific implementation, the idea of classes
becoming containers of modules is a great solution to sharing functionality.

I'm pleased to hear others are thinking this way.

-andy

···

--
Andrew Stone

Wouldn't this hurt performance a lot? For each and every message
passed, one more step in lookup.

I don't know.

Aur Saraf

···

On 2/18/07, Trans <transfire@gmail.com> wrote:

A notion came to me yesterday with regards to how we extend classes.
On the one hand we can create a module and include it into a class,
but this will not allow us to override a preexisting method -- for
that we must reopen the class and play the alias and/ or binding game.
Various ideas have been put forth for simplifying and improving the
robustness of this, from #wrap_method, alias_method_chain, as well
as :pre, :post methods and my own cuts (albeit they go a bit further
than just method wrapping). But occurs to me that these add
complexities onto something that is already straightforward if we are
willing to separate out all class concerns into modules. In other
words instead of writing:

  class X
    def a; "a"; end
  end

write:

  module Xa
    def a; "a"; end
  end

  class X
    include Xa
  end

  X.new.a #=> "a"

Then it's easy enough to overlay new behaviors:

  module Xb
    def a; "{"+super+"}"; end
  end

  class X
    include Xb
  end

  X.new.a #=> "{a}"

Since that works so well, it occurs to me, why not make this the
fundamental behavior of defining a class? In other words defining a
class:

  class X
    def a; "a"; end
  end

could be equivalent to writing:

  class X
    include( Module.new{
        def a; "a"; end
    } )
  end

So there would always be a module involved in the definition of a
class. And classes become simply containers of modules.

T.

Hi Tom,

The problem to my eyes is that they're not equivalent.

The former is still required when you want to redefine an existing
method in a class, often one you didn't write yourself. If you change
the former to mean the latter, does that mean we can't redefine
methods like that?

Regards,
George.

···

On 2/18/07, Trans <transfire@gmail.com> wrote:

Since that works so well, it occurs to me, why not make this the
fundamental behavior of defining a class? In other words defining a
class:

  class X
    def a; "a"; end
  end

could be equivalent to writing:

  class X
    include( Module.new{
        def a; "a"; end
    } )
  end

So there would always be a module involved in the definition of a
class. And classes become simply containers of modules.

T.

Why not just use a subclass? Isn't that one of the primary motivations
for inheritance in the first place, to override a method in an ancestor?

Gary Wright

···

On Feb 17, 2007, at 8:24 PM, Trans wrote:

A notion came to me yesterday with regards to how we extend classes.
On the one hand we can create a module and include it into a class,
but this will not allow us to override a preexisting method -- for
that we must reopen the class and play the alias and/ or binding game.

Trans wrote:

[...] But occurs to me that these add
complexities onto something that is already straightforward if we are
willing to separate out all class concerns into modules.

Okay, I must admit that I actually don't have the slightest idea about
Traits[1], but from what little I heard about them, this sounds a lot
like it. Doesn't it?

And I read some blog entry somewhere by somebody who wanted to
implement Traits in Ruby but AFAIR there was no code (yet).

jwm

[1] <http://www.iam.unibe.ch/~scg/Research/Traits/&gt;

Hi,

Then it's easy enough to overlay new behaviors:

module Xb
   def a; "{"+super+"}"; end
end

class X
   include Xb
end

X.new.a #=> "{a}"

Since that works so well, it occurs to me, why not make this the
fundamental behavior of defining a class? In other words defining a
class:

class X
   def a; "a"; end
end

could be equivalent to writing:

class X
   include( Module.new{
       def a; "a"; end
   } )
end

So there would always be a module involved in the definition of a
class. And classes become simply containers of modules.

What is the benefit of by this change? Besides that this change would
make remove_method impossible.

I vaguely think of similar idea with combination of class box, so that
we can control side-effect from open classes, i.e.

  namespace foo # fake syntax

  # re-opening class that effective only in this namespace
  class String
    def a; "a"; end
  end

  "foo".a # "a" method available here

But I am still not confident of the benefit and effect of this
behavior.

              matz.

···

In message "Re: More flexible inheritance" on Sun, 18 Feb 2007 10:24:37 +0900, "Trans" <transfire@gmail.com> writes:

I am not sure if I am grasping the quintessence (I have not forgotten
:wink: of your post
but your ideas just reinforce an idea which is getting clearer and
clearer in my mind.
Use classes with care, actually we could do some Javascript in pure
ruby, kindly look at this
code e.g.

The variation to your idea is that I really forget Class and only use
objects and prototypes.
So #include becomes #extend, Class.new becomes a constructor in the
Javascript sense.

module M1
  attr_accessor :name
end

module M2
  attr_reader :value
  def inc; @value += 1; end
  def dec; @value -=1 ; end
  def init
    @value = 41
    self
  end
end

def newM
  m = Object.new
  m.extend M1
  m.extend M2

  m.init
end

m = newM
m.name = 'Tom'
puts m.name
m.inc
puts m.value

Cheers
Robert

···

On 2/18/07, Trans <transfire@gmail.com> wrote:

A notion came to me yesterday with regards to how we extend classes.
On the one hand we can create a module and include it into a class,
but this will not allow us to override a preexisting method -- for
that we must reopen the class and play the alias and/ or binding game.
Various ideas have been put forth for simplifying and improving the
robustness of this, from #wrap_method, alias_method_chain, as well
as :pre, :post methods and my own cuts (albeit they go a bit further
than just method wrapping). But occurs to me that these add
complexities onto something that is already straightforward if we are
willing to separate out all class concerns into modules. In other
words instead of writing:

  class X
    def a; "a"; end
  end

write:

  module Xa
    def a; "a"; end
  end

  class X
    include Xa
  end

  X.new.a #=> "a"

Then it's easy enough to overlay new behaviors:

  module Xb
    def a; "{"+super+"}"; end
  end

  class X
    include Xb
  end

  X.new.a #=> "{a}"

Since that works so well, it occurs to me, why not make this the
fundamental behavior of defining a class? In other words defining a
class:

  class X
    def a; "a"; end
  end

could be equivalent to writing:

  class X
    include( Module.new{
        def a; "a"; end
    } )
  end

So there would always be a module involved in the definition of a
class. And classes become simply containers of modules.

T.

--
We have not succeeded in answering all of our questions.
In fact, in some ways, we are more confused than ever.
But we feel we are confused on a higher level and about more important things.
-Anonymous

Hey, how about a new #include variant that does alias magic whenever a
method exists in both the class and the mixin module?

Aur Saraf

···

On 2/18/07, SonOfLilit <sonoflilit@gmail.com> wrote:

Wouldn't this hurt performance a lot? For each and every message
passed, one more step in lookup.

I don't know.

Aur Saraf

On 2/18/07, Trans <transfire@gmail.com> wrote:
> A notion came to me yesterday with regards to how we extend classes.
> On the one hand we can create a module and include it into a class,
> but this will not allow us to override a preexisting method -- for
> that we must reopen the class and play the alias and/ or binding game.
> Various ideas have been put forth for simplifying and improving the
> robustness of this, from #wrap_method, alias_method_chain, as well
> as :pre, :post methods and my own cuts (albeit they go a bit further
> than just method wrapping). But occurs to me that these add
> complexities onto something that is already straightforward if we are
> willing to separate out all class concerns into modules. In other
> words instead of writing:
>
> class X
> def a; "a"; end
> end
>
> write:
>
> module Xa
> def a; "a"; end
> end
>
> class X
> include Xa
> end
>
> X.new.a #=> "a"
>
> Then it's easy enough to overlay new behaviors:
>
> module Xb
> def a; "{"+super+"}"; end
> end
>
> class X
> include Xb
> end
>
> X.new.a #=> "{a}"
>
> Since that works so well, it occurs to me, why not make this the
> fundamental behavior of defining a class? In other words defining a
> class:
>
> class X
> def a; "a"; end
> end
>
> could be equivalent to writing:
>
> class X
> include( Module.new{
> def a; "a"; end
> } )
> end
>
> So there would always be a module involved in the definition of a
> class. And classes become simply containers of modules.
>
> T.
>

George Ogata schrieb:

The problem to my eyes is that they're not equivalent.

The former is still required when you want to redefine an existing
method in a class, often one you didn't write yourself. If you change
the former to mean the latter, does that mean we can't redefine
methods like that?

George, I'm not Tom, but I think that for redefining a method, instead of

   class X
     def a; "a2"; end
   end

internally you would do something like

   X.ancestors[ 1 ].module_eval {
     def a; "a2"; end
   }

Everything you would do with class X in normal Ruby you would do to the anonymous module being the first in the ancestors chain.

Regards,
Pit

Hi Aur,

I believe the result of method lookups are cached, so you mightn't get
burnt too bad.

Alternatively, if we're only trying to affect method lookup, you could
keep storing methods in the class, but store a list of method bodies
for each name rather than just one, and only change super to look
beyond the front of the list if necessary.

Maybe Trans had a grander vision using full Module objects, though.

Regards,
George.

···

On 2/18/07, SonOfLilit <sonoflilit@gmail.com> wrote:

Wouldn't this hurt performance a lot? For each and every message
passed, one more step in lookup.

I don't know.

Aur Saraf

Trans,

I'm actually in the middle of a week long argument concerning "fat models"
versus "thin models" and with my approach being fat libraries and thin
models.

That's a long argument :wink:

We have a few sites which will share a lot of functionality and
instead of sharing the models, I like sharing libraries that are included in
the models. The idea being that this is easy to do with svn externals on
the lib directory. Then fix a bug or add functionality in one place. Even
though this gives two places to look for methods, I think it's nice and
clean.

Even though my example is for a specific implementation, the idea of classes
becoming containers of modules is a great solution to sharing functionality.

I think yours is a perfect example. Modules provide SOC and
consequently allow one to more easily mix and match aspects to
different models. That a class would become exclusively a container of
modules (anonymous or explicit) at the very least helps promote a good
practice.

The only substantial argument against such an approach I've come up is
one in preference of delegation over inheritance. If available aspects
are modules rather than classes then we are sort-of pushed toward
using inheritance, even though in some cases delegation would be
preferable. However it's easy enough to maneuver around this if need
be, for example:

  class Module
    def new(*a,&b)
      base = self
      Class.new{ include base }.new(*a,&b)
    end
  end

I'm pleased to hear others are thinking this way.

Me too.

T.

···

On Feb 17, 8:40 pm, "Andrew Stone" <stoneli...@gmail.com> wrote:

You're right of course --if that's what one needs. But often one wants
to extend a prexisting class. There are numerous reasons one might
want to do that, and which arn't very suitable to subclassing.
Imagine if we didn't have open classes , then just to add a single
method to a class required a new subclass. (would Ruby start to look a
lot more like Java?)

T.

···

On Feb 18, 2:49 pm, Gary Wright <gwtm...@mac.com> wrote:

On Feb 17, 2007, at 8:24 PM, Trans wrote:

> A notion came to me yesterday with regards to how we extend classes.
> On the one hand we can create a module and include it into a class,
> but this will not allow us to override a preexisting method -- for
> that we must reopen the class and play the alias and/ or binding game.

Why not just use a subclass? Isn't that one of the primary motivations
for inheritance in the first place, to override a method in an ancestor?

What is the benefit of by this change?

The benifits are more robust means of extension b/c

  1) mixins can be used for extensions
  2) mixins are much more flexible and elegant than open/alias/
redefine
  3) no longer need to fuss with alias names or method binding
  4) we would no longer require alias (which is a keyword)
  5) lends itself to future possiblities such as uninclude
  6) promotes better design and way of thinking whereby
     a class is a collection of "bahaviors" (sub your prefered term)

Besides that this change would make remove_method impossible.

remove_method would be possible. by default it could effect the
"lowest" module. and if need be, of course, one can select the
particular mixin to effect.

I vaguely think of similar idea with combination of class box, so that
we can control side-effect from open classes, i.e.

  namespace foo # fake syntax

  # re-opening class that effective only in this namespace
  class String
    def a; "a"; end
  end

  "foo".a # "a" method available here

But I am still not confident of the benefit and effect of this
behavior.

another topic worth consideration, but selector namespaces are a
different animal I think --well, unless we can dynamically activate
and deactive the mixin modules depedning on the namespace we're in,
then namespaces could be based on these. Hmm...that's a cool idea, but
I suspect it would require a major overhaul in how Ruby works under
the hood.

T.

···

On Feb 19, 3:04 am, Yukihiro Matsumoto <m...@ruby-lang.org> wrote:

Hi Pit,

Ah ok, I was overlooking the fact that *original* methods of X would
live in an anonymous module as well.

It's an interesting idea. It does still change the existing semantics
a little, though. Currently, if you do:

  class C
    def f; 'C'; end
  end

  module M
    def f; 'M'; end
  end

  C.send(:include, M)

...C still comes first in the lookup path. You'd need to ensure that
the anonymous modules (eigenmodules? :wink: come at the front of the
ancestor chain if you wanted to preserve this behavior.

But yeah, interesting idea!

Regards,
George.

···

On 2/19/07, Pit Capitain <pit@capitain.de> wrote:

George, I'm not Tom, but I think that for redefining a method, instead of

   class X
     def a; "a2"; end
   end

internally you would do something like

   X.ancestors[ 1 ].module_eval {
     def a; "a2"; end
   }

Everything you would do with class X in normal Ruby you would do to the
anonymous module being the first in the ancestors chain.

Hi,

What is the benefit of by this change?

The benifits are more robust means of extension b/c

1) mixins can be used for extensions
2) mixins are much more flexible and elegant than open/alias/redefine
3) no longer need to fuss with alias names or method binding
4) we would no longer require alias (which is a keyword)
5) lends itself to future possiblities such as uninclude
6) promotes better design and way of thinking whereby
    a class is a collection of "bahaviors" (sub your prefered term)

I think I get the picture .. gradually. Interesting.
Can you be more specific about the suggestion, for example, for the
code

  class Foo < Object
    def foo
    end
  end

  class Foo
    def bar
    end
  end

what would happen? Can we access appending anonymous mixin module?

another topic worth consideration, but selector namespaces are a
different animal I think --well, unless we can dynamically activate
and deactive the mixin modules depedning on the namespace we're in,
then namespaces could be based on these. Hmm...that's a cool idea, but
I suspect it would require a major overhaul in how Ruby works under
the hood.

Sure it is. But I suspect this proposal is nearly as huge as selector
namespace for language change, although implementation is far easier.

              matz.

···

In message "Re: More flexible inheritance" on Thu, 22 Feb 2007 02:43:14 +0900, "Trans" <transfire@gmail.com> writes:

On Feb 19, 3:04 am, Yukihiro Matsumoto <m...@ruby-lang.org> wrote:

Actually, one of the benefits is that they would not come first,
becuase then you can override methods without resorting to alias and
what have you. However there's no reason we could not have the ability
to do both (which as you point out would benefit backward
compatability.) In fact as Pit points out we could have quite a bit of
flexibility accessing the module stack.

T.

···

On Feb 18, 12:26 pm, "George Ogata" <george.og...@gmail.com> wrote:

On 2/19/07, Pit Capitain <p...@capitain.de> wrote:

> George, I'm not Tom, but I think that for redefining a method, instead of

> class X
> def a; "a2"; end
> end

> internally you would do something like

> X.ancestors[ 1 ].module_eval {
> def a; "a2"; end
> }

> Everything you would do with class X in normal Ruby you would do to the
> anonymous module being the first in the ancestors chain.

Hi Pit,

Ah ok, I was overlooking the fact that *original* methods of X would
live in an anonymous module as well.

It's an interesting idea. It does still change the existing semantics
a little, though. Currently, if you do:

  class C
    def f; 'C'; end
  end

  module M
    def f; 'M'; end
  end

  C.send(:include, M)

...C still comes first in the lookup path. You'd need to ensure that
the anonymous modules (eigenmodules? :wink: come at the front of the
ancestor chain if you wanted to preserve this behavior.

I tried to implement similar solution some time ago, in case with Foo -
it may looks like that:

  class Foo
    instance_methods do
      def foo

      end
    end
  end

  Foo.instance_methods do
    def bar
    end
  end

For working code and specs please go to:
specs:
http://github.com/alexeypetrushin/ruby-ext/blob/master/lib/ruby_ext/prototype_inheritance.rb
code:
http://github.com/alexeypetrushin/ruby-ext/blob/master/spec/ruby_ext/_prototype_inheritance_spec.rb

It seems to works, but I prefer (and using actually) a little lighter
solution, kind of mixins with sugar, you can find more details about it
here http://bos-tec.com/pages/ruby-multiple-inheritance

···

--
Posted via http://www.ruby-forum.com/.