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


#41

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

def self.pod

use:

class Beanbag
self << Beanable
end

T.

You can say in a comment or a discussion: “check if Fooable is
imported* into Bar”. But can you “check if Fooable is
double-lessed-than into Bar”?

Having both module and instance methods imported* is a concept. Just
like inclusion is right now. Concepts should have a name, imho.

  • integrated/combined/merged/…

#42

On Sat, 10 Jun 2006, Alder G. wrote:

You can say in a comment or a discussion: “check if Fooable is
imported* into Bar”. But can you “check if Fooable is
double-lessed-than into Bar”?

Having both module and instance methods imported* is a concept. Just
like inclusion is right now. Concepts should have a name, imho.

  • integrated/combined/merged/…

assimilated? borged?

:wink:

-a


#43

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

assimilated? borged?

incorporated! then Ruby will be Enterprise-ready :wink:


#44

removed_email_address@domain.invalid wrote:

plus, the first case is annoying. if it weren’t, people wouldn’t be
rolling
their own shortcuts left and right - which is kind of what this thread is
about.

Here’s another shortcut, dunno how original it is. I don’t like the
#import (or #include!) suggestions because they take control away from
the module. In most cases, the module will know best which (if any)
class methods to contribute.

class Module
def included_class_methods(&bl)
if bl
included_class_methods_module.module_eval(&bl)
else
included_class_methods_module.instance_methods
end
end

def included(cl)
cl.extend included_class_methods_module
end

private
def included_class_methods_module
@included_class_methods_module ||= Module.new
end
end

module M
included_class_methods do
def bar; “BAR”; end
end
def self.zap; “ZAP”; end
def foo; “FOO”; end
end

class C
include M

p bar

begin
zap
rescue
puts “Can’t call C.zap”
end
end

p C.new.foo

p M.included_class_methods

END

Output:

“BAR”
Can’t call C.zap
“FOO”
[“bar”]


#45

Alder G. wrote:

On 6/9/06, Joel VanderWerf removed_email_address@domain.invalid wrote:

Here’s another shortcut, dunno how original it is. I don’t like the
#import (or #include!) suggestions because they take control away from
the module. In most cases, the module will know best which (if any)
class methods to contribute.

Assuming #import would have a callback-hook like #included
(#imported?), how would the locus of control shift from where it is
right now?

The #imported callback might help (and it would inevitably be requested
if it were not provided), but then the module author has to decide how
to handle each of #included, #imported, and #extended.

I’m still leaning towards having just one method, include, that is
available to client classes to “use” the module in typical cases. The
module decides what gets included at the instance and class levels.


#46

On 6/9/06, Joel VanderWerf removed_email_address@domain.invalid wrote:

Here’s another shortcut, dunno how original it is. I don’t like the
#import (or #include!) suggestions because they take control away from
the module. In most cases, the module will know best which (if any)
class methods to contribute.

Assuming #import would have a callback-hook like #included
(#imported?), how would the locus of control shift from where it is
right now?

In a somewhat related note:

Perhaps #import should call #include. Or maybe they would be entirely
seperate: #import for module-level methods, #include for
instance-level methods. Of course, this means that in the most common
case you’d have the unDRYish idiom:

import Fooable
include Fooable

But it’s still much better than the mass of boilerplate code we need
to paste there right now. Another suggestion:

import Fooable, :include => false

To import only the module level methods. I.e. :include => true by
default.


#47

Joel VanderWerf wrote:

class methods to contribute.
Hey, Joel. Have a look at Facets’ class_inherit.rb. It is a complete
implementation of what you suggest.

T.


#48

On 6/9/06, Joel VanderWerf removed_email_address@domain.invalid wrote:

I’m still leaning towards having just one method, include, that is
available to client classes to “use” the module in typical cases. The
module decides what gets included at the instance and class levels.

The major lack right now is a way to “include” module methods. Even if
the module is to be the one deciding which methods get exported, it
does not have a straightforward way to export its own self-methods.
The lack of such a way right now forces us to various underhanded
hacks, e.g. to first ill-define those module-methods as instance
methods, and then use #extend in various unnatural ways, ending up
with a lot of boilerplate code and duplicated, redundant instance
methods no-one is ever going to use directly (the ugly ClassMethods
module with all its krufty instance-methods would remain in the
namespace).

The #imported callback might help (and it would inevitably be requested
if it were not provided), but then the module author has to decide how
to handle each of #included, #imported, and #extended.

I agree, the need to handle 3 cases is somewhat icky. It’s also icky
right now with both #included and #extended to block.

I’m not sure how prevalent is the need to limit the ways functionality
in a module can be exported. I never had it, and most of the responses
in the thread are about the opposite need (which I did share several
times) - to allow functionality in the module to propagate more
freely.

So I hope we’re not providing here for some hypothetical need absent
from reality. Also, arguably, if you’re doing something unnatural like
limiting access to your module’s functionality, you should be the one
bothering to secure all hatches (not that it would actually prevent
anyone from hacking it out quite easily… what you’re talking about
is more like a warning-system really, a sort of recommendation).

I suspect it should also be possible to easily define macro methods
for auto-generating #included, #imported, and #extended. Which is
better than the state for #import, whose lack cannot be filled by
macros alone, and requires copy-pasting code chunks to each module,
and/or unnatural changes to the way those modules are defined.


#49

Hi,

In message “Re: Why the lack of mixing-in support for Class methods?”
on Fri, 9 Jun 2006 11:25:53 +0900, Yukihiro M.
removed_email_address@domain.invalid writes:

|| class Beanbag
|| self << Beanable
|| end
|
|I like this more than others, but I worry about that this is less
|descriptive than other at the same time. For example, how one can
|know whether “include” does not injects module methods, where “<<”
|does.

But I am still not sure if we need to provide the way to inject module
methods to a class’s singleton class at the same time instance methods
are inherited to the class. Could somebody elaborate? It seems that
having two separate modules (one to include for instance methods and
another to extend for class methods) is more natural for me.

						matz.

#50

I’m still leaning towards having just one method, include, that is
available to client classes to “use” the module in typical cases. The
module decides what gets included at the instance and class levels.

The major lack right now is a way to “include” module methods. Even if
the module is to be the one deciding which methods get exported, it
does not have a straightforward way to export its own self-methods.

I am suddenly reminded of the idea non-inherited methods --a way to
sepcify that a method is not be inherited through sublcassing or
inclusion. The idea being that some methods might just exist to serve
other methods and should not be apart of the class/module’s interface
at all, not even for subclasses --sort of a “superprivate”. So if we
further extrapolate, this concept allows control of what a module will
provide upon inclusion at the instance level. Might we not do the same
at the class level, but in the the case of a module we need the
opposite control (because the default is not to “extend”) --a sort of
“superpublic”.

module M
class << self
def foo ; end
extensible
def bar ; end
end
def bar ; end
uninheritable
def foo ; end
end

Class X
include M
end

X.foo #=> error
X.bar #=> nil
X.new.foo #=> error
X.new.bar #=> nil

This way then we need only use #include for all forms of “bevahiorial
importing” and it is up to the module/class to decide what it provides
–where the responsibility really belongs.

T.


#51

----- Original Message -----
From: “Yukihiro M.” removed_email_address@domain.invalid
To: “ruby-talk ML” removed_email_address@domain.invalid
Sent: Saturday, June 10, 2006 12:28 PM
Subject: Re: Why the lack of mixing-in support for Class methods?

[skip]

But I am still not sure if we need to provide the way to inject module
methods to a class’s singleton class at the same time instance methods
are inherited to the class. Could somebody elaborate? It seems that
having two separate modules (one to include for instance methods and
another to extend for class methods) is more natural for me.

matz.

It’s natural and more “perfect”.
Perfect means “nothing can be removed”, not “everything was added”,
so why complicate the language by adding one more shortcut if required
functionality is already available and can be implemented in library?
Who likes Ruby to follow C++, CommonLisp, Java, Perl way?
May be it’s time to remove extra features to make Ruby flawless?

Sergey


#52

Hi,

In message “Re: Why the lack of mixing-in support for Class methods?”
on Sun, 11 Jun 2006 15:09:51 +0900, removed_email_address@domain.invalid writes:

|I think the clearest use is with DSLs that also has useful instance
|methods. Currently DSLs of this sort can only be provided via
|subclasses.

I still have difficulties to imagine the concrete situation.

| module M
| class << self
| attr_accessor :foo
| end
| def foo
| self.class.foo
| end
| end

Do we have to allow M.foo? I fell like you need to a way to add class
methods and instance methods at once, but it doesn’t have to (or maybe
it shouldn’t) be module methods inclusion.

|It’s a class level setting, but with a way to easily access it from the
|instance level. To split this up into two modules is clearly a forced
|concession.
|
| module Mc
| attr_accessor :foo
| end
|
| module Mi
| def foo
| self.class.foo
| end
| end
|
| class X
| extend Mc
| include Mi
| end

If you don’t need M.foo and M.foo= in above example (while you
definitely need X.foo and X.foo=), Mc and Mi solution is the way to
go. Redundancy can be removed by some kind of meta programming.

						matz.

#53

Yukihiro M. wrote:

But I am still not sure if we need to provide the way to inject module
methods to a class’s singleton class at the same time instance methods
are inherited to the class. Could somebody elaborate? It seems that
having two separate modules (one to include for instance methods and
another to extend for class methods) is more natural for me.

I think the clearest use is with DSLs that also has useful instance
methods. Currently DSLs of this sort can only be provided via
subclasses. As you say, you can split it into two modules but if the
methods go together inextrobaly, then this is very inconvenient --and
unacceptably so when building frameworks and asking others to make use
of such tools. But sometimes it goes beyond mere inconvenience b/c an
instance method might call on a class method. The most basic example of
this kind of inter-relationship looks like this:

module M
class << self
attr_accessor :foo
end
def foo
self.class.foo
end
end

class X
self << M
end

It’s a class level setting, but with a way to easily access it from the
instance level. To split this up into two modules is clearly a forced
concession.

module Mc
attr_accessor :foo
end

module Mi
def foo
self.class.foo
end
end

class X
extend Mc
include Mi
end

T.


#54

Yukihiro M. wrote:

Redundancy can be removed by some kind of meta programming.

This could do the job:

class Module
def class_methods(&block)
@class_methods = block
end

 def included(mod)
   (class << mod; self; end).module_eval(&@class_methods)
 end

end

module Mod
class_methods do
def foo
“FOO”
end
end

 def bar
   "BAR"
 end

end

class Test
include Mod
end

Test.foo #=> “FOO”
Test.new.bar #=> “BAR”

Cheers,
Daniel


#55

Yukihiro M. wrote:

| end
| end

If you don’t need M.foo and M.foo= in above example (while you
definitely need X.foo and X.foo=), Mc and Mi solution is the way to
go. Redundancy can be removed by some kind of meta programming.

The problem runs deeper than this. Having to depend on “some kind of
meta programming” is not as simple as it might seem. The complexity can
grow. Take for example my implementation of Annotations. Ex-

module M
def x ; “foo” ; end
ann :x, :type => String
end

class X
include M
end

An annotation stores metadata about instance methods and hence accessed
at the class-level. They are also “inheritable”, ie. class X refers
back to M’s annotations:

X.a_x #=> #

This is a clear example of where the instance-level is tied to the
class-level in such a way that they must both go along with each other.

In my experience the main thing that drives us to use ClassMethods and
like “hacks” is not that it can’t be done using a two module
extend/include approach, but becuase we don’t want to trouble our users
with the differentiation – docs like:

NOTE: This module is designed to be used with #extend (not #include)
when “extending” classes (though you must use #include when
“extending” it to modules which subsequently must then also use
#extend).

This may seem excessive, but not everyone is a Ruby expert. And it only
becomes worse to tell someone thay have to both #include the module AND
#extend it’s extensible counter-part module to get full functionality.

As you say, meta-programming can take care of it, but the biggest
problem with meta-programming approaches is that everyone is using
there own variation. They may use #included, they may override
#append_features, they may override #include itself, or some other
trick. And like I’ve said I’ve seen code go so far as to paste methods
directly into a class via the included callback --all becuase this
distiinctin between extend and include and how to handle it (or hack it
as the case may be) was clear to the programmer.

T.


#56

Hi,

In message “Re: Why the lack of mixing-in support for Class methods?”
on Sun, 11 Jun 2006 22:43:11 +0900, removed_email_address@domain.invalid writes:

|As you say, meta-programming can take care of it, but the biggest
|problem with meta-programming approaches is that everyone is using
|there own variation.

Don’t get me wrong. I didn’t say that I wouldn’t do nothing, and let
you help yourself using meta-programming features. I’d happy to add
the standard feature to achieve your goal, if it really is a generic
requirement. I like the solution that Daniel has shown in
[ruby-talk:196730].

The point is I want to make sure which approach is the way to go.

						matz.

#57

Daniel S. wrote:

 def included(mod)

Test.new.bar #=> “BAR”
Daniel! I think you actually make my point! Matz, see what Daniel has
done here? --dumping the class methods directly into the class. This is
the kind of thing I see happening in other peoples code.

This really has nothing to do with the capability to meta-code a
solution (I have done so myself, of coure), it’s that people don’t
fully understand what really needs to happen, or understand the
distinctinctive use of extend vs. include. It may seem “intuitive” for
you since you have a complete understanding of Ruby underthehood. But
it’s far from intuitive for others.

BTW, Daniel, have a look as Facets’ classmethods.rb or class_inherit.rb
for a better idea of how to do what you are suggesting.

T.


#58

Yukihiro M. wrote:

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

Why change the name of include, why not just give it some options. The
behavior is still the same, it is just the scope (singleton or instance
level methods) that is changing.

include Foo # current behavior
# (more explicitly 'include Foo, :instance_methods
include Foo, :singleton_methods # include only singleton methods
include Foo, :all # include both instance and singleton methods

I think it makes more sense to reuse the name include for this rather
then a new name, since the behavior isn’t changing. Only the scope it
applies to.

One possible drawback of this is that it would always include singleton
methods at the class-level for the including class. And someone wouldn’t
be able to use this to include a singleton method as an instance method
on a class. I personally wouldn’t want to do this (seems way to
confusing), but perhaps someone else does.

Zach


#59

Hi –

On Sun, 11 Jun 2006, Yukihiro M. wrote:

you help yourself using meta-programming features. I’d happy to add
the standard feature to achieve your goal, if it really is a generic
requirement. I like the solution that Daniel has shown in
[ruby-talk:196730].

It looks reasonable though I would recommend not calling the method
“class_methods”. That has the effect of freezing, in the language,
the status and purpose of singleton methods on module and class
objects. I know that “class methods” is of course a conventional way
to refer to those methods, but I’d be sorry to see it introduced at
the language level at the expense of the generality of the current
design.

David


David A. Black (removed_email_address@domain.invalid)
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

See what the readers are saying about “Ruby for Rails”!
http://www.rubypowerandlight.com/quotes


#60

Yukihiro M. wrote:

you help yourself using meta-programming features. I’d happy to add
the standard feature to achieve your goal, if it really is a generic
requirement.

I like the solution that Daniel has shown in [ruby-talk:196730].

He he. That’s ironic. Yes, Daniels solution is the right direction for
the best meta-programming approach, but it still has some problems (as
I pointed out in my last post).

The point is I want to make sure which approach is the way to go.

Ah, I see. Cool. Well, Maybe that approach is the best way then. You
can look at Facets, classmethod.rb or classinherit.rb for a full
rendition of that techinque. The two are identical, BTW. They just
differ in terminology (though I’m now thinking ‘extensible’ might be a
much better term).

On the other hand, I can’t help but wonder if a module’s “class-level”
were instead a “module-level”, --a module instead of a class. Or at
least if those methods were delegated to an extending module but
transaprently, underthehood. Then this might actually make sense:

module M
module << self

end
end

And it would be easy enough to write:

def include_and_extend( mod )
include mod
extend (module << mod; self; end)
end

An interesting thought.

Thanks,
T.