Why the lack of mixing-in support for Class methods?


#1

Hi

I’m confused as to why class methods aren’t include’d by default along
with instance methods when mixing-in modules.

It obstructs the important orthoganility between modules and classes,
and - among other problems - thus undermines the position of mixins as
an alternative to class-based multiple inheritance.

In short, I can see a lot of poor consequence to that lack of support,
while on the other hand no good reason for it. Maybe someone would
care to elighten me?

(I’m aware of the various hacks for fudging such support, and in fact
use some of them in my code. But there are readability and
maintainability issues with those hacks for something that’s supposed
to be a core feature).


#2

On 6/7/06, Alder G. removed_email_address@domain.invalid wrote:

while on the other hand no good reason for it. Maybe someone would
care to elighten me?

(I’m aware of the various hacks for fudging such support, and in fact
use some of them in my code. But there are readability and
maintainability issues with those hacks for something that’s supposed
to be a core feature).

The main reason is because a different ‘self’ is involved, I would
guess.
You can use extend to mixin class methods. Also, there’s a nice idiom
for getting include to mixin both class and instance methods of a
module (though the class methods are actually in another sub-module):

module A
def self.included receiver
receiver.extend ClassMethods
end

module ClassMethods
def foo
“foo”
end
end
def inst_method
“instance method”
end
end

class B
include A
end

B.foo #=> “foo”
B.new.inst_method #=> “instance method”


#3

On 6/8/06, Phil T. removed_email_address@domain.invalid wrote:

In short, I can see a lot of poor consequence to that lack of support,
You can use extend to mixin class methods. Also, there’s a nice idiom
“foo”

B.foo #=> “foo”
B.new.inst_method #=> “instance method”

Yes, I’m using this idiom in my code. Note, however, that if you want
to use A’d “class methods” from A itself, you have to add the extra
line:

self.extend(ClassMethods)

after A::ClassMethods’s definition.

Still, it’s unclear to me why this very natural functionality isn’t
built into the default append_features. It hinders the ease of going
from a module to a class or vice-versa, which Ruby seems to encourage.
In any case, every other aspect of switching between those two is
well-handled, so why not this one as well? Are there plans to add it
for Ruby 2?


#4

Hi,

In message “Re: Why the lack of mixing-in support for Class methods?”
on Thu, 8 Jun 2006 17:02:35 +0900, “Alder G.”
removed_email_address@domain.invalid writes:

|Still, it’s unclear to me why this very natural functionality isn’t
|built into the default append_features.

Mix-in is used for several purposes and some of them can be hindered
by inheriting class methods, for example, I don’t want to spill
internal methods when including the Math module. I am not against for
some kind of inclusion that inherits class methods as well, but it
should be separated from the current #include behavior.

						matz.

#5

Hi –

On Thu, 8 Jun 2006, Alder G. wrote:

while on the other hand no good reason for it. Maybe someone would
care to elighten me?

It’s actually a fairly specialized case. In the general case, if a
module has a method of its own, there’s no particular reason to think
that every class that mixes it in should also have that method.

For example:

module Edible
def self.definition
“able to be eaten without ill effect”
end
end

class Bread
include Edible
end

class Bagel < Bread
end

You don’t really want Bread or Bagel reporting back their definitions
as being the same as Edible’s definition. You can arrange for that to
happen quite easily (see Phil’s answer) if you want to, but you also
want to be able to have it not happen.

There’s a huge amount of discussion of this in the mailing list
archives and on RCRchive. People disagree, irreconcilably, and it’s
not worth rehashing the whole thing. But have a look at the archives
if you’re interested.

David


#6

On Thu, 8 Jun 2006, Alder G. wrote:

while on the other hand no good reason for it. Maybe someone would
care to elighten me?

(I’m aware of the various hacks for fudging such support, and in fact
use some of them in my code. But there are readability and
maintainability issues with those hacks for something that’s supposed
to be a core feature).


-Alder

module M
def self.new
‘yikes!’
end
def self.alloc
‘even worse’
end
def self.name
‘this is getting bad’
end
def self.is_a?
‘i hope you did not override this’
end
end

class C
include M
end

-a


#7

Alder G. wrote:

while on the other hand no good reason for it. Maybe someone would
care to elighten me?

(I’m aware of the various hacks for fudging such support, and in fact
use some of them in my code. But there are readability and
maintainability issues with those hacks for something that’s supposed
to be a core feature).

You can do this without too much trouble. This is what I use, which is
based off some code _why posted on his blog a while back.

---- meta.rb ----

A set of methods to help create meta-programming gizmos.

class Object

The metaclass is the singleton behind every object.

def metaclass
class << self
self
end
end

Evaluates the block in the context of the metaclass

def meta_eval &blk
metaclass.instance_eval &blk
end

Acts like an include except it adds the module’s methods

to the metaclass so they act like class methods.

def meta_include mod
meta_eval do
include mod
end
end

Adds methods to a metaclass

def meta_def name, &blk
meta_eval { define_method name, &blk }
end

Defines an instance method within a class

def class_def name, &blk
class_eval { define_method name, &blk }
end
end


require ‘meta’

module Foo
def test
puts “test method”
end
end

class Bar
meta_include Foo
end

Bar.test

>> “test method\n”

-Jeff


#8

Jeff R. wrote:

Not the same thing.

end

Bar.test

>> “test method\n”

Why? You can just do

class Bar
extend Foo
end

Bar.test

>> “test method\n”

T.


#9

removed_email_address@domain.invalid wrote:

def self.is_a?
‘i hope you did not override this’
end
end

class C
include M
end

Sigh. We’ve been through this Ara. It’s a silly example --don’t use
those methods if you don;t want to override them --and what if you do?

Nonethesless, I agree with Matz. I think a simple alternative call is
the prefect compromising solution. Perhaps:

class C
inherit M
end

Also, This post is kind of timely, I’be been preparing a post with
example’s from Nitro on the extensive need of this behavior --you
should see the many “hacks” being used to accomplish this there --I
think at least three different techinqes are being used throughout
dozens of components.

T.


#10

On Thu, 8 Jun 2006 removed_email_address@domain.invalid wrote:

Sigh. We’ve been through this Ara. It’s a silly example --don’t use those
methods if you don;t want to override them --and what if you do?

indeed. still - i see it being a bit anti POLS from someone’s
perspective,
that’s all.

Nonethesless, I agree with Matz. I think a simple alternative call is
the prefect compromising solution. Perhaps:

class C
inherit M
end

i have my own impl :wink:

Also, This post is kind of timely, I’be been preparing a post with example’s
from Nitro on the extensive need of this behavior --you should see the many
“hacks” being used to accomplish this there --I think at least three
different techinqes are being used throughout dozens of components.

the one issue i see is with stateful methods - we don’t currently have
‘inheritable state’. eg

module M
class << self
attr ‘foo’
end
@foo = 42
end

class A
inherit M
end

class B < A
end

p M.foo #=> 42
p A.foo #=> nil
p B.foo #=> nil

A.foo = ‘forty-two’

p M.foo #=> 42
p A.foo #=> ‘forty-two’
p B.foo #=> nil

traits addresses this. eg this works

require ‘traits’

class M
class_trait ‘foo’ => 42
end

class A < M
end

class B < A
end

p M.foo #=> 42
p A.foo #=> 42
p B.foo #=> 42

A.foo = ‘forty-two’

p M.foo #=> 42
p A.foo #=> ‘forty-two’
p B.foo #=> ‘forty-two’

not a deal breaker - but i’ve found in that in many designs relying on
class
inheritence it’s precisely this kind of information sharing one is
trying to
acheive via inheritence or mixing in class methods.

just food for thought. if we start mixing in class methods then the
strange
behaviour of ruby’s ‘class variables’ and ‘class instance variables’
will
become a popular topic on this list.

cheers.

-a


#11

removed_email_address@domain.invalid wrote:

indeed. still - i see it being a bit anti POLS from someone’s perspective,
that’s all.

Fair enough. No doubt it will come with it’s own set of considerations
with which we progammers will have to become familiar.

from Nitro on the extensive need of this behavior --you should see the many
@foo = 42
p M.foo #=> 42
p A.foo #=> nil
p B.foo #=> nil

A.foo = ‘forty-two’

p M.foo #=> 42
p A.foo #=> ‘forty-two’
p B.foo #=> nil

I think one would need to set that up oneself using a class variable.
Rather I would expext this it to behave like it does, just as with a
class:

class X
def self.x
@x
end
@x = 10
end

class Z < X
end

X.x #=> 10
Z.x #=> nil

p M.foo #=> 42
p A.foo #=> ‘forty-two’
p B.foo #=> ‘forty-two’

You bring up your traits library all the time :wink: Actually all-in-all I
think it’s pretty good. Unfortuately I have this one nagging problem
with it. I think the term ‘traits’ is a terrible misnomer. That may
seem silly but having done some study of prototype-base OOPS,
especially Self, traits are just a totlatlly different concept to me.

T.


#12

On Thu, 8 Jun 2006 removed_email_address@domain.invalid wrote:

class Z < X
end

X.x #=> 10
Z.x #=> nil

right. it’s just that ‘inheritence’ with object generally follows this
pattern

class B
attr :b
def initialize() @b = ‘bar’ end
end

class C < B
end

p C.new.b #=> ‘bar’

which is to say there are mechanisms, namely initialize and super, for
propagating state. no such mechanisms exist for class based state. if
one is
proposing an ‘inherit’ method (which i think is a great idea) then i
think
it’s important to also toss around ideas for things like
Class.class_init
and/or Module.module_init - eg hooks that are provided to accomplish
this. my
preferred approach now is this

module M
module ClassMethods
attr ‘a’
attr ‘b’
end
module InstanceMethods
end
def self.included other
other.extend ClassMethods
other.module_eval{ include InstaneMethods }
init other
end
def self.init other
other.a = 42
other.b = ‘forty-two’
end
end

if we don’t also consider this then the situation you describe in Nitro,
where
there a muliple ways of implimenting class method mixins, will more or
less
remain if any of those methods require state - that is to say it’d be a
shame
to make every ruby developer roll his is own way of initializing the
required
state for the module methods he could mix into his classes so easily.

You bring up your traits library all the time :wink:

yeah, not on purpose though: it just fills a lot of meta-programming and
class
inheritence niches which seems to come up often on this list.

Actually all-in-all I think it’s pretty good. Unfortuately I have this one
nagging problem with it. I think the term ‘traits’ is a terrible misnomer.
That may seem silly but having done some study of prototype-base OOPS,
especially Self, traits are just a totlatlly different concept to me.

well - you can use the ‘has’ interface

class C
has ‘c’ => 42
class_has ‘b’
end

it’s just an alias - but if you hate the name…

cheers.

-a


#13

On 6/8/06, Yukihiro M. removed_email_address@domain.invalid wrote:

internal methods when including the Math module. I am not against for
some kind of inclusion that inherits class methods as well, but it
should be separated from the current #include behavior.

                                                    matz.

Hi Matz :slight_smile:

I see your and Dblack’s point about the need for an #include that
won’t inherit the class methods. And I agree that it’s probably a good
idea to keep #include behaving as it does right now. So adding a
separate #inherit seems like a very good idea.

I encountered the need for #inherit when I wrote Foo as a class, and
then later discovered I needed to have Foo features in class Bar that
already has parent Baz. It seems Ruby would be much more flexible if I
could simply:

module Fooable # instead of class Foo

end

class Foo
inherit Fooable
end

class Bar < Baz
inherit Fooable
end

This is not a rare need, at least not for me. I’m pretty new to Ruby,
and alredy needed to do this 5 times. Also I’d argue current lack of
support of this encourages programmers to encapuslate functionality in
classes even when they don’t need to instantiate it, and a module
would be more appropriate.

Since you don’t seem averse to the idea, and there are others (below,
in this thread, and earlier in other threads) who share the same need,
what do we need to do to have #inherit added as feature?


#14

On Fri, 9 Jun 2006, Yukihiro M. wrote:

I don’t like the name #inherit. Since it is not a inheritance.
i’ve suggested ‘mixin’ in the past

module M
end

class C
mixin M
end

??

-a


#15

Hi,

In message “Re: Why the lack of mixing-in support for Class methods?”
on Fri, 9 Jun 2006 00:49:19 +0900, “Alder G.”
removed_email_address@domain.invalid writes:

|I see your and Dblack’s point about the need for an #include that
|won’t inherit the class methods. And I agree that it’s probably a good
|idea to keep #include behaving as it does right now. So adding a
|separate #inherit seems like a very good idea.

I don’t like the name #inherit. Since it is not a inheritance.

						matz.

#16

On Jun 8, 2006, at 12:45 PM, removed_email_address@domain.invalid wrote:

good
class C
mixin M
end

I like that.

James Edward G. II


#17

On 6/8/06, removed_email_address@domain.invalid removed_email_address@domain.invalid wrote:

On Fri, 9 Jun 2006, Yukihiro M. wrote:

I don’t like the name #inherit. Since it is not a inheritance.
i’ve suggested ‘mixin’ in the past

module M
end

class C
mixin M
end

Um. I think that’s too confusing. The problem is that we want to be
clear that:

  • #include only deals with the inclusion of instance-level methods
    at the instance level.
  • #extend only deals with inclusion of instance-level methods at the
    class/object level.
  • #??? includes both instance- and class-level methods at the
    instance/class level.

In my mind, #mixin does not capture that, since the process of
#include or #extend is called mixing-in.

No, I don’t have a better name. I thought of #blend, but that’s just
too … ugly.

-austin


#18

Yukihiro M. wrote:

I don’t like the name #inherit. Since it is not a inheritance.
I knew you were going to say that :wink: I actually hesitated to suggest
it, but I haven’t thought of anything better. But I would like to point
out an interesting (albiet currently illegal) equivalency:

module Beanable
def self.pod
“oooooo”
end
end

class Beanbag
extend (class << Beanable; self; end)
end

T.


#19

removed_email_address@domain.invalid wrote:

 module ClassMethods
 def self.init other
   other.a = 42
   other.b = 'forty-two'
 end

end

if we don’t also consider this then the situation you describe in Nitro, where
there a muliple ways of implimenting class method mixins, will more or less
remain if any of those methods require state - that is to say it’d be a shame
to make every ruby developer roll his is own way of initializing the required
state for the module methods he could mix into his classes so easily.

Okay. I see what you’re saying. Thouhg, I guess I’m thinking class
variables might help as far as carrying inheritable class level state.
Class variables are going to be “fixed” and made local to the
class/module right? So they will be able to be used for a class state
state. Is that right? And I think we all agree some sort of
module-based initialization could be helpful (though I think it’s
hard to say how you do that for instance variables without thwarting
proper OOP).

well - you can use the ‘has’ interface

class C
has ‘c’ => 42
class_has ‘b’
end

it’s just an alias - but if you hate the name…

Better! nodes head approvingly :slight_smile:

T.


#20

On 6/8/06, Austin Z. removed_email_address@domain.invalid wrote:


No, I don’t have a better name. I thought of #blend, but that’s just
too … ugly.

I like #blend. Goes well with #blend? etc.

Some other nice alternatives from the thesaurus: meld, merge, mingle,
fuse, combine, integrate, compound.