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

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

I don’t know.

Aur S.

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

Aur S.

On 2/18/07, Trans [email protected] wrote:

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.

George O. 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

On 2/19/07, Pit C. [email protected] wrote:

}

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.

But yeah, interesting idea!

Regards,
George.

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?

Gary W.

On 2/18/07, SonOfLilit [email protected] wrote:

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

I don’t know.

Aur S.

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.

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/

On Feb 18, 12:26 pm, “George O.” [email protected] wrote:

X.ancestors[ 1 ].module_eval {

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.

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 17, 8:40 pm, “Andrew S.” [email protected] wrote:

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.

Hi,

In message “Re: More flexible inheritance”
on Sun, 18 Feb 2007 10:24:37 +0900, “Trans” [email protected]
writes:

|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.

On Feb 18, 2:49 pm, Gary W. [email protected] 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?

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 2/18/07, Trans [email protected] wrote:

words instead of writing:

end

end

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

T.

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 Feb 19, 3:04 am, Yukihiro M. [email protected] wrote:

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.

“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.

Hi,

In message “Re: More flexible inheritance”
on Thu, 22 Feb 2007 02:43:14 +0900, “Trans” [email protected]
writes:

|On Feb 19, 3:04 am, Yukihiro M. [email protected] wrote:
|
|> 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.

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

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs