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).
on 08.06.2006 08:23
on 08.06.2006 09:20
On 6/7/06, Alder Green <alder.green@gmail.com> 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"
on 08.06.2006 10:03
On 6/8/06, Phil Tomson <rubyfan@gmail.com> 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?
on 08.06.2006 12:57
Hi -- On Thu, 8 Jun 2006, Alder Green 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
on 08.06.2006 13:00
Hi,
In message "Re: Why the lack of mixing-in support for Class methods?"
on Thu, 8 Jun 2006 17:02:35 +0900, "Alder Green"
<alder.green@gmail.com> 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.
on 08.06.2006 13:43
Alder Green 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
on 08.06.2006 14:59
On Thu, 8 Jun 2006, Alder Green 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
on 08.06.2006 15:02
Jeff Rose 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.
on 08.06.2006 15:08
ara.t.how...@noaa.gov 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.
on 08.06.2006 15:27
On Thu, 8 Jun 2006 transfire@gmail.com 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 ;-) > 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
on 08.06.2006 16:50
ara.t.howard@noaa.gov 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 ;) 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.
on 08.06.2006 17:09
On Thu, 8 Jun 2006 transfire@gmail.com 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 ;) 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
on 08.06.2006 17:50
On 6/8/06, Yukihiro Matsumoto <matz@ruby-lang.org> 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 :) 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?
on 08.06.2006 19:38
Hi,
In message "Re: Why the lack of mixing-in support for Class methods?"
on Fri, 9 Jun 2006 00:49:19 +0900, "Alder Green"
<alder.green@gmail.com> 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.
on 08.06.2006 19:48
On Fri, 9 Jun 2006, Yukihiro Matsumoto 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
on 08.06.2006 19:52
On Jun 8, 2006, at 12:45 PM, ara.t.howard@noaa.gov wrote: >> good > class C > mixin M > end I like that. James Edward Gray II
on 08.06.2006 20:08
On 6/8/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote: > On Fri, 9 Jun 2006, Yukihiro Matsumoto 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
on 08.06.2006 20:27
ara.t.howard@noaa.gov 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* :) T.
on 08.06.2006 20:33
Yukihiro Matsumoto wrote:
> I don't like the name #inherit. Since it is not a inheritance.
I knew you were going to say that ;) 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.
on 08.06.2006 22:10
On 6/8/06, Austin Ziegler <halostatue@gmail.com> 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.
on 08.06.2006 22:25
Alder Green wrote: > On 6/8/06, Austin Ziegler <halostatue@gmail.com> 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. > A few others: derive (a little obscure) evolve (I quite like that) elaborate (too long?) develop gather (gets the dual nature quite nicely) invoke (seems too active to me) follow (like evolve, but less so) This could go on all night :-)
on 08.06.2006 22:50
On Jun 8, 2006, at 1:23 PM, Alex Young wrote: > derive (a little obscure) > evolve (I quite like that) evolve seems vague and makes people think of monkeys -- Elliot Temple http://www.curi.us/blog/
on 08.06.2006 23:28
Elliot Temple wrote: >>> > evolve seems vague and makes people think of monkeys Sure. Monkey patching. -- James Britt "Blanket statements are over-rated"
on 09.06.2006 00:49
On 6/8/06, Elliot Temple <curi@curi.us> wrote: > >> fuse, combine, integrate, compound. > > A few others: > > > > derive (a little obscure) > > evolve (I quite like that) > > evolve seems vague and makes people think of monkeys What's wrong with that? ;) Ok, now I have to toss some out... engulf (seems bigger than include or extend) completes (give me the complete list of methods) clobber (silly, but it's likely to do that to some methods, and has CLass/OBject mnemonic).
on 09.06.2006 02:11
From: "Bill Guindon" <agorilla@gmail.com> >> >> I like #blend. Goes well with #blend? etc. > > Ok, now I have to toss some out... > > engulf (seems bigger than include or extend) > completes (give me the complete list of methods) > clobber (silly, but it's likely to do that to some methods, and has > CLass/OBject mnemonic). enchant ? enliven ? enfold ? incorporate ? induct ? ;) What about import ? Regards, Bill
on 09.06.2006 02:23
transfire@gmail.com wrote: > end > end > > class Beanbag > extend (class << Beanable; self; end) > end Considering the above (which by the way should also have an 'include Beanable'), a notation that has some similarity and is available for use: class Beanbag self << Beanable end T.
on 09.06.2006 02:48
On Fri, 9 Jun 2006, Bill Kelly wrote:
> What about import ?
i like it. i think i may have come up with that one a while back too.
that,
along with 'mixin' - are my favourites so far.
cheers.
-a
on 09.06.2006 03:22
On 6/8/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote: > On Fri, 9 Jun 2006, Bill Kelly wrote: > > What about import ? > i like it. i think i may have come up with that one a while back too. that, > along with 'mixin' - are my favourites so far. Sensible. You include functionality from a module, you extend an object with a module, and you import a module wholesale. Or something nonsensical like that. ;) -austin
on 09.06.2006 04:26
Hi,
In message "Re: Why the lack of mixing-in support for Class methods?"
on Fri, 9 Jun 2006 09:22:55 +0900, transfire@gmail.com writes:
|Considering the above (which by the way should also have an 'include
|Beanable'), a notation that has some similarity and is available for
|use:
|
| 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.
matz.
on 09.06.2006 04:38
Yukihiro Matsumoto wrote: > | 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. class Beanbag include! Beanable end ;) Hal
on 09.06.2006 04:41
On Jun 8, 2006, at 10:25 PM, Yukihiro Matsumoto wrote: > | 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. > > matz. > well we have class << self, which involves singleton methods why not class Beanbag include << Beanable end which would also involve singleton methods? (Of course then include has to be a keyword (or "include <<" at least))
on 09.06.2006 08:53
On 6/8/06, Austin Ziegler <halostatue@gmail.com> wrote: > > end > 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. > What about a this approach: module A def A.foo "this method wouldn't get mixed in" end def self.bar "this method would get mixed in" end end class B include A end B.bar #=> "this method would get mixed in" B.foo #=> no method error So the idea would be that if you use 'def self.<method_name>' the method would be mixed in, but if you use 'def <module name>.<method name>' it would not be mixed in. ...I'm not sure how easy/hard/possible this would be to implement, though, or if it's even practical. Another approach would be to so something like: module A def self.not_mixable "not mixed in" end mixable def self.mixed "mixed in" end end class B include A end B.mixed #=> "mixed in" B.not_mixable #=> no method error Phil
on 09.06.2006 12:45
Hi -- On Fri, 9 Jun 2006, Phil Tomson wrote: > > > > ...I'm not sure how easy/hard/possible this would be to implement, > though, or if it's even practical. I think that's a dangerous path to go down. self is really just an alias for a particular object at a particular time, and having it be more "magic" than that could get messy. David
on 09.06.2006 13:00
Hi -- On Fri, 9 Jun 2006, Logan Capaldo wrote: >> |use: >> matz. >> > > well we have class << self, which involves singleton methods > > why not > class Beanbag > include << Beanable > end > > which would also involve singleton methods? I'm not sure << really evokes "connected in some way with singleton methods", though. Then again, I'm not sure what's wrong with "extend", so I'm probably a bit out of the loop on this discussion. > (Of course then include has to be a keyword (or "include <<" at > least)) Not necessarily: class MyModule def initialize @list = [] end def include(*args) if args.size.zero? @list else @list.concat(args) end end end m = MyModule.new m.instance_eval { include "a", "b" include << "c" p @list # ["a", "b", "c"] } David
on 09.06.2006 14:44
On Jun 9, 2006, at 5:58 AM, dblack@wobblini.net wrote: > Then again, I'm not sure what's wrong with > "extend", so I'm probably a bit out of the loop on this discussion. Well, extend takes the instance methods of a module and makes them class methods on the receiver. People are asking for a natural way to mix in the class methods of a module, preferably while mixing in the instance methods as well (as include currently does). In short, they want to turn this idiom into a one liner: module MixinForInstanceAndClassMethods module ClassMethods # ... class methods ... end extend ClassMethods def self.included(receiver) receiver.extend(ClassMethods) end # ... instance methods ... end class Receiver include MixinForInstanceAndClassMethods end Hope that helps. James Edward Gray II
on 09.06.2006 15:08
Hal Fulton wrote: > > > class Beanbag > include! Beanable > end I like this and it fits into the current scheme.
on 09.06.2006 16:18
Yukihiro Matsumoto wrote: > 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. Unfortuantly, as it is, we cannot know. I have seen some bad practices in the overriding of #include which do not ever inject the module. It's weird becuase Ruby reports the module as an ancestor though the module is totaly devoid of any content, i.e. The #include call was just used as a "trick" to do something else (usually using class_eval to add methods directly to the base class/module). Short of turning include and extend into keywords, I'm not sure there's any way to ensure proper behavior. And if that were done, it would likely create other limitations that may have been useful to more experienced Rubyists. T.
on 09.06.2006 16:22
Hi -- On Fri, 9 Jun 2006, James Edward Gray II wrote: > On Jun 9, 2006, at 5:58 AM, dblack@wobblini.net wrote: > >> Then again, I'm not sure what's wrong with >> "extend", so I'm probably a bit out of the loop on this discussion. > > Well, extend takes the instance methods of a module and makes them class > methods on the receiver. People are asking for a natural way to mix in the > class methods of a module, preferably while mixing in the instance methods as > well (as include currently does). In short, they want to turn this idiom > into a one liner: I know (believe me, I've been through many iterations of this discussion :-) What I really mean is: I don't see what's wrong with: module SomeMethodsIWantSomeClassesToHave end module SomeInstanceMethods end class C extend SomeMethodsIWantSomeClassesToHave include InstanceMethods end In other words, I prefer a module design that doesn't fuse different modularities together in the first place. David
on 09.06.2006 16:22
On Fri, 9 Jun 2006, dblack@wobblini.net wrote: > module SomeMethodsIWantSomeClassesToHave > end > > module SomeInstanceMethods > end > > class C > extend SomeMethodsIWantSomeClassesToHave > include InstanceMethods s/Instance/SomeInstance/ :-) David
on 09.06.2006 16:57
On Fri, 9 Jun 2006 dblack@wobblini.net wrote: > extend SomeMethodsIWantSomeClassesToHave > include InstanceMethods > end > > In other words, I prefer a module design that doesn't fuse different > modularities together in the first place. sometimes they belong together logically: module Sync require 'sync' def self.sync m, which = :EX module_eval <<-code alias_method "__#{ m }__", "#{ m }" def #{ m }(*a, &b) sync_init synchronize(:#{ which }){ __#{ m }__(*a, &b) } end code end def sync_init extend Sync_m unless Sync_m === self end end class C import Sync sync 'method_that_needs_to_be_thread_safe' sync 'another_method_that_needs_to_be_thread_safe', :SH end obviously one can break them apart, but it's un-natural. in general anytime the imported module introduced instance methods which rely on class state or class methods or, as this case does, introduces class methods which rely in instance state/methods, it makes logical sense to keep it all in one bundle. for me logical orthogonality is by far ruby's greatest asset and i hate doing illogical things in my ruby code - i save that for fortran. in any case i don't think anyone thinks anything is 'wrong' with splitting it up, but it clearly violates DRY to do this module M module ClassMethods end module InstanceMethods end end class A include M::InstanceMethods extend M::ClassMethods end class B include M::InstanceMethods extend M::ClassMethods end class C include M::InstanceMethods extend M::ClassMethods end vs this module M module ClassMethods end module InstanceMethods end def self.included other other.extend ClassMethods other.module_eval{ include InstanceMethods } end end class A include M end class B include M end class C include M end 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. kind regards. -a
on 09.06.2006 20:00
On 6/9/06, transfire@gmail.com <transfire@gmail.com> 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/...
on 09.06.2006 20:07
On Sat, 10 Jun 2006, Alder Green 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? ;-) -a
on 09.06.2006 20:17
On 6/9/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote: > > assimilated? borged? incorporated! _then_ Ruby will be Enterprise-ready ;)
on 09.06.2006 20:38
ara.t.howard@noaa.gov 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"]
on 09.06.2006 21:45
On 6/9/06, Joel VanderWerf <vjoel@path.berkeley.edu> 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.
on 09.06.2006 22:02
Alder Green wrote: > On 6/9/06, Joel VanderWerf <vjoel@path.berkeley.edu> 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.
on 09.06.2006 23:19
On 6/9/06, Joel VanderWerf <vjoel@path.berkeley.edu> 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.
on 10.06.2006 04:16
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.
on 10.06.2006 14:25
>> 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.
on 10.06.2006 18:28
Hi,
In message "Re: Why the lack of mixing-in support for Class methods?"
on Fri, 9 Jun 2006 11:25:53 +0900, Yukihiro Matsumoto
<matz@ruby-lang.org> 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.
on 11.06.2006 05:34
----- Original Message ----- From: "Yukihiro Matsumoto" <matz@ruby-lang.org> To: "ruby-talk ML" <ruby-talk@ruby-lang.org> 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
on 11.06.2006 08:13
Yukihiro Matsumoto 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.
on 11.06.2006 14:24
Hi,
In message "Re: Why the lack of mixing-in support for Class methods?"
on Sun, 11 Jun 2006 15:09:51 +0900, transfire@gmail.com 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.
on 11.06.2006 14:42
Yukihiro Matsumoto 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
on 11.06.2006 15:44
Yukihiro Matsumoto 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 #=> #<Annotation type=String> 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.
on 11.06.2006 15:54
Yukihiro Matsumoto 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
on 11.06.2006 15:54
Hi,
In message "Re: Why the lack of mixing-in support for Class methods?"
on Sun, 11 Jun 2006 22:43:11 +0900, transfire@gmail.com 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.
on 11.06.2006 16:04
Daniel Schierbeck 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.
on 11.06.2006 16:51
Hi -- On Sun, 11 Jun 2006, Yukihiro Matsumoto 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 (dblack@wobblini.net) Ruby Power and Light, LLC (http://www.rubypowerandlight.com) See what the readers are saying about "Ruby for Rails"! http://www.rubypowerandlight.com/quotes
on 11.06.2006 16:57
On 6/11/06, Yukihiro Matsumoto <matz@ruby-lang.org> 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]. > > The point is I want to make sure which approach is the way to go. > > matz. > > IMHO, the best approach in this thread is the access-control style notation suggested by transfire: > > importing" and it is up to the module/class to decide what it provides > --where the responsibility really belongs. While this certainly needs to be thought about and refined, I think a good implementation will not only answer the need at hand, but also contribute to the cohesiveness of the mixin concept, while planting the locus of control firmly within the mixin module. I.e. it's a very elegant way for the mixin module to determine exactly what functionality gets included in the reciever, whether it is the default instance methods, a combination of instance methods and class methods, maybe even just the class methods, or some subset of instance methods and class methods. Very nice in and of itself, if provided with a sensible default which would implicitly do the right thing for common uses. It also avoids what I see as the major problem with adding #import: the degradation of the cohesiveness of the inclusion concept. We understand what it means when a class #includes Enumerable right now. But what meaning would we ascribe to it #importing Enumerable? How is #import different from #include? Would we now need to check both where we before checked only for inclusion? And what if we allow the reciever to ask only for the class methods? Thus the neat mixin concept risks sprouting too many special cases so as to become meaningless. Transfire's suggestion keeps it cohesive. Another nice thing: it resembles the access control syntax (public/protected/private) which is conceptually similar to inclusion control.
on 11.06.2006 17:01
Yukihiro Matsumoto 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.
on 11.06.2006 17:08
On Jun 11, 2006, at 7:42 AM, Daniel Schierbeck wrote: > Yukihiro Matsumoto wrote: >> Redundancy can be removed by some kind of meta programming. > > This could do the job: Here's another idea: >> class Module >> def included(receiver) >> receiver.extend(const_get(:ClassMethods)) if const_defined? :ClassMethods >> end >> end => nil >> module Test >> module ClassMethods >> def test >> "I'm a class method." >> end >> end >> def test >> "I'm an instance method." >> end >> end => nil >> class MyTest >> include Test >> end => MyTest >> MyTest.test => "I'm a class method." >> MyTest.new.test => "I'm an instance method." James Edward Gray II
on 11.06.2006 17:42
James Edward Gray II wrote: > >> module ClassMethods > >> include Test > >> end I'm not too fond of the nested module approach -- I think it's cleaner to use a method. But that's a matter of opinion, of course. Daniel
on 11.06.2006 17:42
transfire@gmail.com wrote: > 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. Actually, that solution seems overly complicated -- that may be for performance reasons, but if we were to disregard those (as we have a tendency to do,) I like my solution better. Daniel
on 11.06.2006 17:46
dblack@wobblini.net wrote: > 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. I thought the same when I wrote it, but I couldn't come up with a better name. It's actually hard to pinpoint a reasonable one... Daniel
on 11.06.2006 18:11
On Jun 11, 2006, at 10:40 AM, Daniel Schierbeck wrote: >> >> module ClassMethods >> >> include Test >> >> end > > I'm not too fond of the nested module approach -- I think it's > cleaner to use a method. But that's a matter of opinion, of course. Oh well, to each their own. I prefer the nested module approach because: 1. I can envision the threads here where we try to explain when to define class methods with and without the method. 2. It has a been a common Ruby idiom for literally years, so history works in our favor. James Edward Gray II
on 11.06.2006 18:26
Daniel Schierbeck wrote: > transfire@gmail.com wrote: > > 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. > > Actually, that solution seems overly complicated -- that may be for > performance reasons, but if we were to disregard those (as we have a > tendency to do,) I like my solution better. I'm not sure if you understand the important difference though --it's not just a differnce in coding, but in actuall functionality. Your approach has a side effect you might not have considered. You see you're evaluating the code directly into the base class/module. In effect you copy the methods rather than include/extend. What happens is that if you were to define the same method in your class, you would over-write it completely. You could not use #super to "call-up" to the module version. In other words your approach might seem less complicate and thus more elegant, but it actually thwarts the use of "inheritance". T.
on 11.06.2006 18:51
On Mon, 12 Jun 2006, James Edward Gray II wrote: > Oh well, to each their own. > > I prefer the nested module approach because: > > 1. I can envision the threads here where we try to explain when to define > class methods with and without the method. > 2. It has a been a common Ruby idiom for literally years, so history works > in our favor. > > James Edward Gray II +1 i use this already, it's very flexible and does 'the right thing': harp:~ > cat inheritable.rb module Inheritable Inherit = lambda do |this, other| cm = this.const_get 'ClassMethods' rescue nil im = this.const_get 'InstanceMethods' rescue nil other.extend cm if cm other.module_eval{ include im if im extend RecursiveInherit } end module RecursiveInherit def included other Inherit[self, other] super end end extend RecursiveInherit end if $0 == __FILE__ module M include Inheritable module ClassMethods def foo() 42 end end end class C include M end p C.foo module A include Inheritable module ClassMethods def bar() 'forty-two' end end module InstanceMethods def foobar() 42.0 end end end module B include A end class K include B end p K.bar p K.new.foobar end harp:~ > ruby inheritable.rb 42 "forty-two" 42.0 perhaps a simple RCR for inheritable.rb? -a
on 11.06.2006 19:07
Hi -- On Mon, 12 Jun 2006, Daniel Schierbeck wrote: > name. It's actually hard to pinpoint a reasonable one... Which might be telling us something.... :-) David -- David A. Black (dblack@wobblini.net) Ruby Power and Light, LLC (http://www.rubypowerandlight.com) See what the readers are saying about "Ruby for Rails"! http://www.rubypowerandlight.com/quotes
on 11.06.2006 20:49
transfire@gmail.com wrote: > I'm not sure if you understand the important difference though --it's > not just a differnce in coding, but in actuall functionality. Your > approach has a side effect you might not have considered. You see > you're evaluating the code directly into the base class/module. In > effect you copy the methods rather than include/extend. What happens is > that if you were to define the same method in your class, you would > over-write it completely. You could not use #super to "call-up" to the > module version. In other words your approach might seem less complicate > and thus more elegant, but it actually thwarts the use of > "inheritance". What I like about my solution is the interface -- the implementation was something I just jotted down. This would of course be better (feel free to point out any errors): class Module def class_methods(&block) @class_methods ||= Module.new @class_methods.module_eval(&block) end def included(mod) mod.extend(@class_methods) end end That way you don't need to have a constant (which clutters the namespace.) Cheers, Daniel
on 11.06.2006 21:35
On Mon, Jun 12, 2006 at 03:48:14AM +0900, Daniel Schierbeck wrote: [...] } What I like about my solution is the interface -- the implementation was } something I just jotted down. This would of course be better (feel free } to point out any errors): } } class Module } def class_methods(&block) } @class_methods ||= Module.new } @class_methods.module_eval(&block) } end } } def included(mod) } mod.extend(@class_methods) } end } end } } That way you don't need to have a constant (which clutters the namespace.) That has two problems. First off, if you don't call class_methods before you first include the module (e.g. you want to reopen the module later to add class methods), the included method will fail since @class_methods is nil. Also, including the module in another module will not behave as expected. See http://redcorundum.blogspot.com/2006/06/mixing-in-class-methods.html for code and an explanation. It's similar to what you have, except: - a little more bulletproof - manages inclusion in modules gracefully - requires an explicit directive to provide the class method mixin functionality - provides a hook to put code in included() } Cheers, } Daniel --Greg
on 11.06.2006 22:15
Gregory Seidman wrote: > ... if you don't call class_methods before > you first include the module (e.g. you want to reopen the module later to > add class methods), the included method will fail since @class_methods is > nil. Wouldn't this fix it? class Module def class_methods(&block) @class_methods ||= Module.new @class_methods.module_eval(&block) end def included(mod) mod.extend(@class_methods ||= Module.new) end end I'm not saying this implementation is perfect; I only wrote it for this thread. Daniel
on 11.06.2006 22:31
On Mon, Jun 12, 2006 at 05:14:20AM +0900, Daniel Schierbeck wrote: } Gregory Seidman wrote: } >... if you don't call class_methods before } >you first include the module (e.g. you want to reopen the module later to } >add class methods), the included method will fail since @class_methods is } >nil. } } Wouldn't this fix it? } } class Module } def class_methods(&block) } @class_methods ||= Module.new } @class_methods.module_eval(&block) } end } } def included(mod) } mod.extend(@class_methods ||= Module.new) } end } end } } I'm not saying this implementation is perfect; I only wrote it for this } thread. Sure, I understand. And yes, that would fix it. Take a look at the complete solution I posted at http://redcorundum.blogspot.com/2006/06/mixing-in-class-methods.html } Daniel --Greg
on 12.06.2006 03:07
In message "Re: Why the lack of mixing-in support for Class methods?"
on Sun, 11 Jun 2006 23:59:17 +0900, transfire@gmail.com writes:
|> 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).
Which is your "last post"? Is it [ruby-talk:196751]?
If so, I agree with Daniel's reply in [ruby-talk:196770].
I have to mention I am not a big fan of the name "class_methods"
though.
|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:
It may make sense from some aspect, but not from others. Language
design is the very complicated task. ;-) In this case, it introduces
full multiple inheritance, which I avoided for years.
matz.
on 12.06.2006 06:40
Yukihiro Matsumoto wrote: > It may make sense from some aspect, but not from others. Language > design is the very complicated task. ;-) I concur with that. It is certainly not a simple thing, and I can only admire the work you have done in the field. That's for sure. > In this case, it introduces full multiple inheritance, which I avoided for years. Does it really introduce multiple inheritance? It's still a linear heirarchy. I always thought of MI as a means to route calls to ancestors in arbitrarily defined ways, more like a tree graph. So I'm not sure how the modularization of a module's "class methods" is multiple inheritance other than the fact that a class is gaining new behaviors from the module, making it something more than it was before. But we can do that already with #extend. I guess what I don't understand is this: If I can manully separate the "class-level" into it's own module and resuse that, why can't I just reusue the class-level to begin with? T.
on 12.06.2006 11:10
Yukihiro Matsumoto wrote: > It may make sense from some aspect, but not from others. Language > ... In this case, it introduces > full multiple inheritance, which I avoided for years. I'm wondering why it's not possible to #include a class into a module/class -- making the only difference between classes and modules the ability to instantiate (#allocate). That way, you could do something like this: class Module def included(mod) mod.extend(class << self; self; end) end end i.e. extend the receiver with the methods and constants of the singleton class. It sure would make this whole problem a lot easier to solve, and avoid those pesky hacks we're all but fond of. Cheers, Daniel
on 12.06.2006 12:20
Gregory Seidman wrote: > Sure, I understand. And yes, that would fix it. Take a look at the complete > solution I posted at > http://redcorundum.blogspot.com/2006/06/mixing-in-class-methods.html Still think it's rather complicated. Here's what I've got so far, using a slightly different approach than you: class Module def meta_module(&block) @meta_module ||= Module.new @meta_module.module_eval(&block) extend(@meta_module) end def included(mod) mod.extend(@meta_module ||= Module.new) if mod.kind_of? Module if mod.instance_variables.include? "@meta_module" other_meta_module = mod.instance_variable_get(:@meta_module) other_meta_module.send(:include, @meta_module) else mod.instance_variable_set(:@meta_module, @meta_module) end end end end There may still be flaws, but I don't think it's too bad. Notice the use of `meta_module' -- thought it was better than `class_methods', though I'm not sure it's perfect, either. Cheers, Daniel
on 12.06.2006 14:54
On Mon, Jun 12, 2006 at 07:19:10PM +0900, Daniel Schierbeck wrote: } Gregory Seidman wrote: } >Sure, I understand. And yes, that would fix it. Take a look at the complete } >solution I posted at } >http://redcorundum.blogspot.com/2006/06/mixing-in-class-methods.html } } Still think it's rather complicated. Here's what I've got so far, using } a slightly different approach than you: } } class Module } def meta_module(&block) } @meta_module ||= Module.new } @meta_module.module_eval(&block) } extend(@meta_module) Why should the module extend the meta_module? } end } } def included(mod) } mod.extend(@meta_module ||= Module.new) Again, if it's a module why should it extend the meta_module? } if mod.kind_of? Module This definitely won't work. You need to check for Class, not Module. Check it out: % irb irb(main):001:0> Object.kind_of? Module => true } if mod.instance_variables.include? "@meta_module" } other_meta_module = mod.instance_variable_get(:@meta_module) } other_meta_module.send(:include, @meta_module) Pretty much what I have. } else } mod.instance_variable_set(:@meta_module, @meta_module) } end } end } end } end That last line is dangerous. Consider the following usage: module M meta_module { def bar "class method bar() was defined in M" end } end class Foo include M end module N include M meta_module { def bar "pwn3d!" end } end puts Foo.bar By setting the including module's instance variable to be the same as the included module's instance variable you allow the including module to overwrite methods unexpectedly. That's why I give the including module its own new module and have it include the included module's module. It preserves the inclusion semantics. } There may still be flaws, but I don't think it's too bad. Notice the use } of `meta_module' -- thought it was better than `class_methods', though } I'm not sure it's perfect, either. I don't see anything particularly meta about it, but that may just be me. } Cheers, } Daniel --Greg
on 12.06.2006 16:51
Hi,
In message "Re: Why the lack of mixing-in support for Class methods?"
on Mon, 12 Jun 2006 13:37:25 +0900, transfire@gmail.com writes:
|I guess what I don't understand is this: If I can manully separate the
|"class-level" into it's own module and resuse that, why can't I just
|reusue the class-level to begin with?
Because it makes thing more complex, I guess. Mere #include should
not inherit class/module-level methods, since there are cases like
Math module. So we need to prepare two different method for module
inclusion instead of one, if we reuse class-level.
matz.
on 12.06.2006 17:04
On Mon, 12 Jun 2006, Yukihiro Matsumoto wrote: > not inherit class/module-level methods, since there are cases like > Math module. So we need to prepare two different method for module > inclusion instead of one, if we reuse class-level. what did you think about my solution matz? it's solves the issue cleanly and is leverages existing practices. here it is again: harp:~ > cat inheritable.rb module Inheritable Inherit = lambda do |this, other| cm = this.const_get 'ClassMethods' rescue nil im = this.const_get 'InstanceMethods' rescue nil other.extend cm if cm other.module_eval{ include im if im extend RecursiveInherit } end module RecursiveInherit def included other Inherit[self, other] super end end extend RecursiveInherit end # # example # if $0 == __FILE__ module M include Inheritable module ClassMethods def foo() 42 end end end class C include M end p C.foo module A include Inheritable module ClassMethods def bar() 'forty-two' end end module InstanceMethods def foobar() 42.0 end end end module B include A end class K include B end p K.bar p K.new.foobar end harp:~ > ruby inheritable.rb 42 "forty-two" 42.0 this way the module itself exports specific class methods in a deliberate fashion and no new keyword need be introduced to support it. regards. -a
on 12.06.2006 17:42
On Jun 12, 2006, at 5:09 AM, Daniel Schierbeck wrote: > > i.e. extend the receiver with the methods and constants of the > singleton class. It sure would make this whole problem a lot easier > to solve, and avoid those pesky hacks we're all but fond of. Then you have multiple inheritance.
on 12.06.2006 17:55
On 6/12/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote: > what did you think about my solution matz? it's solves the issue cleanly and > is leverages existing practices. here it is again: I for one like it - pragmatic and straightforward. However, I'm not sure why you want the InstanceMethods module (unless it's to be explicit). Regards, Sean
on 12.06.2006 18:05
Logan Capaldo wrote: >> mod.extend(class << self; self; end) >> end >> end >> >> i.e. extend the receiver with the methods and constants of the >> singleton class. It sure would make this whole problem a lot easier to >> solve, and avoid those pesky hacks we're all but fond of. > > Then you have multiple inheritance. Yes.
on 12.06.2006 18:09
On Tue, 13 Jun 2006, Sean O'Halpin wrote:
> Sean
module M
include Inheritable
module ClassMethods
def foo() 'foo' end
end
module InstanceMethods
def bar() 'bar' end
end
end
class A
include M # here we get everything due to overridden self.include
end
class B
include M::InstanceMethods # here we cherry pick
end
class C
extend M::ClassMethods # also cherry picking
end
if, instead we were to do
module M
include Inheritable
module ClassMethods
def foo() 'foo' end
end
def bar() 'bar' end
end
then this also works with Inheritable
class A
include M # here we get everything due to overridden self.include
end
since the overridden self.include calls super, but you lose the ability
to
cherry pick the instance methods. Note that the InstanceMethods modules
is
totally optional and only makes sense if you plan on cherry picking
later -
which i've actually done three for four times every. in pratice my own
inheritable.rb looks like this:
module Inheritable
Inherit = lambda do |this, other|
cm = this.const_get 'ClassMethods' rescue nil
im = this.const_get 'InstanceMethods' rescue nil
m = this.const_get 'Methods' rescue nil
other.extend cm if cm
other.extend m if m
other.module_eval{
include im if im
include m if m
extend RecursiveInherit
}
end
module RecursiveInherit
def included other
Inherit[self, other]
super
end
end
extend RecursiveInherit
end
so the rules are
ClassMethods => class methods only
InstanceMethods => instance methods only
Methods => methods that can be called at class or instance
level
(none) => 'normal' instance methods, no cherry picking
this covers all the bases and make sense even to newbies reading the
code.
90% of all cases will only use ClassMethods, however.
cheers.
-a
on 12.06.2006 18:28
On Jun 12, 2006, at 12:04 PM, Daniel Schierbeck wrote: >>> end >>> end >>> >>> i.e. extend the receiver with the methods and constants of the >>> singleton class. It sure would make this whole problem a lot >>> easier to solve, and avoid those pesky hacks we're all but fond of. >> Then you have multiple inheritance. > > Yes. > That's why it's not possible. matz. has strong feelings about MI.
on 12.06.2006 18:38
Gregory Seidman wrote: > On Mon, Jun 12, 2006 at 07:19:10PM +0900, Daniel Schierbeck wrote: > } class Module > } def meta_module(&block) > } @meta_module ||= Module.new > } @meta_module.module_eval(&block) > } extend(@meta_module) > > Why should the module extend the meta_module? Because I'd like the meta-module methods to act as module methods -- if you don't like that, remove that line. > it out: > > % irb > irb(main):001:0> Object.kind_of? Module > => true Or `instance_of? Module' Again, I haven't had time to fully test this implementation -- please focus on the interface. Cheers, Daniel
on 12.06.2006 18:44
Logan Capaldo wrote: >>>> class Module >> Yes. >> > > That's why it's not possible. matz. has strong feelings about MI. I'm not saying we should add it to core -- but I don't understand why we shouldn't allow people to do it themselves. If it was possible to include classes into modules/classes, it would still just be the instance methods and constants of the included class -- so it isn't full inheritance. If the coder overwrites Module#included, full inheritance can be achieved. I don't think it should be up to us whether or not we will allow people to do that. I'm sorry if I don't make much sense right now, I'm a bit drunk (sorry). Cheers, Daniel
on 12.06.2006 19:17
Daniel Schierbeck wrote: > Logan Capaldo wrote: > > Then you have multiple inheritance. > > Yes. Not really. Mulitple Inheritance provides orthogonal heirarchies. Ruby's is strictly linear. If what you say were true then Ruby would already be MI since modules can be used to add behavior to both class and instance levels, and readily associated together as well, as Ara's example clearly demonstrates. To think this consititutes MI and thus making specialized restrictions of code separation to repress it, only serves to complixify the situation, not the other way around. It's like trying to prevent someone from getting to their destinatin by not allowing them to turn right --they'll still get there, they'll just turn left three times. T.
on 12.06.2006 19:31
On Tue, 13 Jun 2006 transfire@gmail.com wrote: > and instance levels, and readily associated together as well, as Ara's > example clearly demonstrates. To think this consititutes MI and thus > making specialized restrictions of code separation to repress it, only > serves to complixify the situation, not the other way around. It's like > trying to prevent someone from getting to their destinatin by not > allowing them to turn right --they'll still get there, they'll just > turn left three times. > > T. in my mind it's this that makes it __not__ mi harp:~ > cat a.rb module A def foo() 'foo' end end module B def bar() 'bar' end end module C def bar() foo() end end class D include A include B include C end p D.new.foo harp:~ > ruby a.rb "foo" now, if ruby was truely mi then i could write something (imagined) like class D < (A, B, C) end p D.new.foo and ruby would be free to print either 'foo' or 'bar'. clearly it would be one or the other and a ruleset would exist to make it un-ambiguous, but the difference between ruby deciding and having built-in rules vs me deciding and have explicit call hierachies aranged manually is a rather large one. so, to me, this is the crucial thing which prevents me from considering ruby to be a true mi lang. the cool thing, of course, is that it's only a few lines of code to make is behave as if it was. cheers. -a
on 12.06.2006 19:35
This thread led me to write this:
# = capsule.rb
#
# == Copyright (c) 2006 Thomas Sawyer
#
# Ruby License
#
# This module is free software. You may use, modify, and/or
redistribute this
# software under the same terms as Ruby.
#
# This program is distributed in the hope that it will be useful,
but WITHOUT
# ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.
#
# == Authors & Contributors
#
# * Thomas Sawyer
# Author:: Thomas Sawyer
# Copyright:: Copyright (c) 2005 Thomas Sawyer
# License:: Ruby License
# = Capsule
#
# A Capsule encapsulates reusable code and is an analog to the Module
class,
# but rather than storing it's own methods it simply stores modules.
These
# modules are divided into two groups: extensions and inclusions. A
capsule
# is reused via the Kernel#inherit method.
#
# == Usage
#
# module Foo
# def foo ; "foo" ; end
# end
#
# module Bar
# def bar ; "bar" ; end
# end
#
# C = Capsule.new do
# extend Foo
# include Bar
# end
#
# class X
# inherit X
# end
#
# X.foo #=> "foo"
# X.new.bar #=> "bar"
#
# Use #[] as a shortcut for grouping one inclusion and one extension
together into
# a new capsule.
#
# C = Capsule[ Foo, Bar ]
#
class Capsule
attr_reader :extensions, :inclusions
def initialize( &template )
@extensions = []
@inclusions = []
instance_eval &template if template
end
# Shortcut for creating an inclusion-extension pair capsule.
def self.[]( e, i )
new << e < i
end
# Add modules to the inclusions group.
def include( *inclusions )
@inclusions.concat inclusions
end
# Add modules to the extensions group.
def extend( *extensions )
@extensions.concat extensions
end
# Add a module to the inclusions group.
def <( inclusion )
@inclusions << inclusion
self
end
# Add a module to the extensions group.
def <<( extension )
@extensions << extension
self
end
# Append this capsules features to a given base module or class.
def append_features( base )
# use #instance_exec in Ruby 1.9+
base.send( :extend, *@extensions )
base.send( :include, *@inclusions )
end
end
class Module
# Inherit behavior from other modules or capsules.
def inherit( *capsules )
capsules.each do |c|
c.append_features( self )
end
end
end
No doubt it can be improved on, but it clearly ties together what Ruby
presently does and the behvior some of us would like to achieve.
T.
on 12.06.2006 20:23
I've read most of this thread and still am a little confused. I'm no ruby guru by any stretch of the imagination, but I use modules to house code that can be utilized in various classes. If I need class methods then I use inheritance. IMO, this is simple and clean. So far, there doesn't seem to be a clean/simple way to have mixin support for class methods. Until that epiphany is reached, I would think it would be better to leave things as is. I will give a novice suggestion: Maybe instead of trying to work in this feature to modules, why not introduce "ClassModule"? Or has this been said? This way it is clear what they use is and they can, in turn, have features specific to class method inheritance (if any are needed). Therefore you could mix and match ClassModules with regular Modules. Seems pretty flexible to me. I have no idea how difficult this would be to implement.
on 12.06.2006 23:47
On 6/12/06, transfire@gmail.com <transfire@gmail.com> wrote: > and instance levels, and readily associated together as well, as Ara's > Exactly. 1. Multiple module inclusion is not multiple inheritance. Even if you think it is - Ruby already has it. So unless you're arguing for its removal - and nobody seems to want that - it's a moot point. 2. You may debate whether mixing-in class methods is a "bad" practice in some sense, and should therefore be discouraged - or at least not encouraged by adding elegant, clear and standard language support for it. But: 2a. It can't be that bad, since nobody is arguing that it should be disallowed completely. 2b. It doesn't seem to be widely considered "bad", at least not by several skilled Ruby hackers. Rails is highly regarded codebase, and the ClassMethods hack is all over it. Rails is the only codebase I'm (vaguely) familiar with, but posts in this thread indicate that other good codebases use similar hacks to accomplish the same programming strategy. 2c. Of course, one may argue that even if mixing-in of class methods is done by many skilled Ruby hackers, and is a vital programming strategy in many high quality Ruby programs, it is still "bad" in some theoretical sense. This is a subjective argument that cannot be resolved. But one of the major attractions of Ruby for me is the TIMTOWDI philosophy. I did not think Ruby would try to discipline its coders by inconveniencing them. Not that it would matter much - mixins would continue to be used for classs method inclusion. So in short, since it seems many people are mixin class methods, it would be nice if it were handled by a standard - hopefully convenient, elegant - language feature.
on 13.06.2006 00:09
On Jun 12, 2006, at 5:43 PM, Alder Green wrote: >> already be MI since modules can be used to add behavior to both class >> > > 2b. It doesn't seem to be widely considered "bad", at least not by > resolved. But one of the major attractions of Ruby for me is the > I think a little context has been lost in this conversation, if I may be permitted to go back and quote Mr. Schierbeck again: > I'm wondering why it's not possible to #include a class into a > module/class -- making the only difference between classes and > modules the ability to instantiate (#allocate). That way, you could > do something like this: > I do not see how straight up #include-ing a _class_ would _not_ be multiple inheritance. I'm was not talking including class methods, I'm talking about writing class A end class B end class C end class D < A include B include C end IOW, erasing the distinction between classes and modules, which is what Mr. Scheirbeck suggested, and what I took exception to.
on 13.06.2006 04:41
Logan Capaldo wrote: > I think a little context has been lost in this conversation, if I may > be permitted to go back and quote Mr. Schierbeck again: > > I'm wondering why it's not possible to #include a class into a > > module/class -- making the only difference between classes and > > modules the ability to instantiate (#allocate). That way, you could > > do something like this: > > > > I do not see how straight up #include-ing a _class_ would _not_ be > multiple inheritance. Techincally it isn't. Multiple inheritance can have iheritance structures like so: M N \ / C Single inheritance (like Ruby) is strictly like this: N | M | C With Ruby, it doesn't matter how many modules you include, it's still just a line of single inheritance. What's unique about mixins is that they allow us to slip "ancesotors" in between a class and it's superclass, thus augmenting behavior in a way similar to MI, but wiithout many of the additional complexities of MI. > > class D < A > include B > include C > end > > IOW, erasing the distinction between classes and modules, which is > what Mr. Scheirbeck suggested, and what I took exception to. Fair enough. But let me make clear that MI is not a wholly satisfactory answer to Mr. Scheirbeck query. The answer is more subtle, a distinction between module and class help promote two different forms of behavioral inheritance, one primary (class) and the other secondary (module). This is actually quite realistic. Think of a Car (class) with optional feature like FullyLoaded (module). I don't think anyone is suggesting we remove that disticition, even if it is *technically* debatable. What _was_ suggested though, is that the "class-level" of a module might be more appropriately a "module-level". Classes would still have class-levels, but by giving modules their "own kind" in this role, the difficulty of extending a class via a module's "module-level" is easily solved --a solution which is the hope of this thread. And such a solution, I reitererate, does not in any way constitute MI. T.
on 13.06.2006 10:23
transfire@gmail.com wrote: > What _was_ suggested though, is that the "class-level" of a module > might be more appropriately a "module-level". Classes would still have > class-levels, but by giving modules their "own kind" in this role, the > difficulty of extending a class via a module's "module-level" is easily > solved --a solution which is the hope of this thread. And such a > solution, I reitererate, does not in any way constitute MI. I agree with you throughout the post, and I do enjoy being called Mr. Schierbeck (we seldom use such formalities where I am from,) but I'm still not sure if we have the same idea about the "module-level". Are you suggesting we replace module objects' singleton classes with such a "module-level", or would the singleton methods and constants be defined in both? What I thought would be great about allowing #include and #extend to accept Class objects is that it would make it much easier for a module (or, well, class) author to decide for himself whether or not he wishes to have the module methods included as well. I agree with you that multiple inheritance would mean a non-linear line of inheritance, and that allowing Class objects to be included would not have such a consequence. Basically, I think module authors should be able to write this: module MyModule def self.included(mod) mod.extend(class << self; self; end) end def self.foo "a class method" end def bar "an instance method" end end class Test include MyModule end Test.foo #=> "a class method" Test.new.bar #=> "an instance method" I think module authors should be able to make that decision (as they do now, with the hacks and all.) This would also allow module "users" to opt-in on the module methods: class Module def inherit(*mods) include *mods mods.each{|mod| extend(class << mod; self; end)} end end (geez, it sure would be nice with a shorthand for singleton classes...) Cheers, Daniel
on 13.06.2006 15:37
Daniel Schierbeck wrote: > you suggesting we replace module objects' singleton classes with such a > "module-level", or would the singleton methods and constants be defined > in both? You have to understand a little about how modules are tied into the inhertiance chain. Modules are linked via virtual classes. (There are some good diagrams of this in the Pickaxe, I believe it's under "Classes and Objects"). The idea is then to replace a module's current "metaclass" with a virtual class linking in a "metamodule". Anything in a module's "class << self" then is defined in this metamodule instead. > What I thought would be great about allowing #include and #extend to > accept Class objects is that it would make it much easier for a module > (or, well, class) author to decide for himself whether or not he wishes > to have the module methods included as well. Well, one can argue the merits for or against that. But it goes beyond the need at hand. The division between module and class I think suits Ruby. Allowing classes to be included outright, while not MI underthehood, nonetheless appears as such on the surface b/c a class than appears to be a child of a more than one class. But having modules distinct, it can only be the child of one class and instead agumented with extra modular behaviors. > def self.foo > end > > Test.foo #=> "a class method" > Test.new.bar #=> "an instance method" > > I think module authors should be able to make that decision (as they do > now, with the hacks and all.) While I'm for 'mod.extend(class << AModule; self; end)'. I think the use of #included is done nowadays b/c there is no other obvious way to achieve the same thing. I think it's unfortuate to have to depend on a callback --and as things stand it goes beyond what a module user generally expects from #include. So in that respect it's not really a good practice. What we really need is a good practice to replace this callback pattern. > This would also allow module "users" to > opt-in on the module methods: > > class Module > def inherit(*mods) > include *mods > mods.each{|mod| extend(class << mod; self; end)} > end > end Yes, I think that's a fair option. And IMHO would say that it's the 2nd best choice, behind the ability to specify 'extensible' and 'uninheritable' sections in ones modules. I still tend to think that the the control is best left to a module itself. But short of that, then this would be a good way. Of course, matz, doesn't like the term "inherit" bc/ it invokes ideas of MI, but any name will do. > (geez, it sure would be nice with a shorthand for singleton classes...) I agree. I've been toying with "own" and "owner" lately myself. T.
on 13.06.2006 16:04
transfire@gmail.com wrote: > ... Modules are linked via virtual classes. (There are > some good diagrams of this in the Pickaxe, I believe it's under > "Classes and Objects"). The idea is then to replace a module's current > "metaclass" with a virtual class linking in a "metamodule". Anything in > a module's "class << self" then is defined in this metamodule instead. All I wanted to know. > distinct, it can only be the child of one class and instead agumented > with extra modular behaviors. So the functionality of modules repressed in order to preserved their secondary nature? Why should we limit the power of modules just so that we can say Ruby doesn't allow MI? I think it's a plus if Ruby had the ability to be *like* an MI language, just like it can appear to be functional. > Yes, I think that's a fair option. And IMHO would say that it's the 2nd > best choice, behind the ability to specify 'extensible' and > 'uninheritable' sections in ones modules. I still tend to think that > the the control is best left to a module itself. I think the use of sections is annoying -- I'd much rather just define in the module whether or not the module methods and constants should be included, too. If you only want certain methods to be included, I think the nested module approach is better. >> (geez, it sure would be nice with a shorthand for singleton classes...) > I agree. I've been toying with "own" and "owner" lately myself. I still like "metaclass" the best -- it may not be the most precise, but it flips off the tongue and keyboard. In the end, it's up to matz; I don't don't really care what name it'll get, as long as it's there. `class << self; self; end' is not the prettiest piece of code... Daniel aka Mr. Schierbeck :)
on 13.06.2006 16:39
Hi,
In message "Re: Why the lack of mixing-in support for Class methods?"
on Tue, 13 Jun 2006 22:35:14 +0900, transfire@gmail.com writes:
|> you suggesting we replace module objects' singleton classes with such a
|> "module-level", or would the singleton methods and constants be defined
|> in both?
|
|You have to understand a little about how modules are tied into the
|inhertiance chain. Modules are linked via virtual classes. (There are
|some good diagrams of this in the Pickaxe, I believe it's under
|"Classes and Objects"). The idea is then to replace a module's current
|"metaclass" with a virtual class linking in a "metamodule". Anything in
|a module's "class << self" then is defined in this metamodule instead.
I don't think I understand you. Do you want to allow #include to
include classes? Without making it MI? Hmm.
matz.
on 13.06.2006 18:45
On Jun 13, 2006, at 10:38 AM, Yukihiro Matsumoto wrote: > I don't think I understand you. Do you want to allow #include to > include classes? Without making it MI? Hmm. It isn't altogether obvious to me why a class passed to #include couldn't be interpreted as a module (since instances of Class are instances of Module via inheritance). That is to say the superclass ancestors a class should not be considered by #include but any included modules should be as well as any directly defined instance methods. To illustrate: module M; end class A; end class B < A; end class C < A; include M; end class D include B include C end M.ancestors => [M] A.ancestors => [A, Object, Kernel] B.ancestors => [B, A, Object, Kernel] C.ancestors => [C, M, A, Object, Kernel] D.ancestors => [D, C, M, B, Object, Kernel] In the last example, A isn't an ancestor because the superclass relationship is ignored by my fictitious 'include'. While I think this behavior could be well-defined, I'm not sure the subtlety is worth it nor do I think it suggests any particular answer to the question of how singleton methods should be handled by #include or some companion method that has been discussed. On that point I'm in Matz camp. I don't see an inherent relationship between the instance methods of a module and the singleton methods of the module object. In particular situations I can see the relationship but it isn't always guaranteed to be there. Instance methods are the raison d'etre of modules but singleton methods may exist for any object whatsoever. I'm not sure why they (singleton methods) should be considered by #include at all. I'm not sure I understand why singleton methods are tucked away in a pseudo-class object since none of the Class instance methods (new, superclass, allocate) seem to be applicable. The container for singleton methods seems much more like a module than a class to me. There is a relationship between the singleton classes of related parent/child classes but couldn't that be modeled as module inclusion? I'm probably missing some subtlety. I am often reminded of the maze in Adventure ("You are in a maze of twisty little passages, all alike") when I think about this stuff. Gary Wright
on 13.06.2006 18:51
On Wed, 14 Jun 2006 gwtmp01@mac.com wrote: > > On Jun 13, 2006, at 10:38 AM, Yukihiro Matsumoto wrote: >> I don't think I understand you. Do you want to allow #include to >> include classes? Without making it MI? Hmm. > > It isn't altogether obvious to me why a class passed to #include couldn't be > interpreted as a module (since instances of Class are instances of Module > via inheritance). module M def initialize 'ha' end end module N def initialize 'ha ha' end end class C include M include N def initialize 'hmmm' end end with classes, this is __always__ the situation. obviously it can be dealt with - but it's a little less straight forward than the 'normal' mixin effect. not for or against here - but i do believe being able to mixin classes, without some really slick handling of super/initialize, state, and class_init on ruby's part would be confusing for most. in otherwords, ruby would need to become mi to do so. regards. -a
on 13.06.2006 19:33
Yukihiro Matsumoto wrote: > I don't think I understand you. Do you want to allow #include to > include classes? Without making it MI? Hmm. No no. You'd still only be able to include modules. I'm thinking a module's "singleton class" could be a "singleton module", so to speak, instead. It's a bit difficult to communicate b/c the terminology is caught up in how it curently works. But it's a lot less fancy than I think it sounds. I'll use a diagram to make it clearer. Lets say we have this code. class Foo include Moo end foo = Foo.new A basic diagram of that currently looks like this: object foo || \/ class foo' -> class Foo -> (vclass Moo) || // \/ module Moo class Foo' || \/ class Moo' I'm thinking that instead it could be like this. object foo || \/ class foo' -> class Foo -> (vclass Moo) || // \/ module Moo class Foo' || \/ (vclass Moo') // module Moo' In this way the so-called "class-level" of a _module_ is instead a "module-level". Essentially: module Moo class << self self.class #=> Module end end Which is why I earlier used "module << self". Of course, in the implmentation you might be able omit the vclass Moo' altogether. I just conceived of it that way becuase it readly "plugs-in" to the current design. T.
on 13.06.2006 19:46
On Wed, 14 Jun 2006 transfire@gmail.com wrote: <snip ascii art> tom - you'll love this http://www.jave.de/ cheers. -a
on 13.06.2006 21:17
On Jun 13, 2006, at 12:50 PM, ara.t.howard@noaa.gov wrote: > end > be dealt > with - but it's a little less straight forward than the 'normal' > mixin effect. I don't see how your example is any different than the problem presented by nested includes. The ordering of ancestors and thus the resolution of method lookup is well defined in either case, no? > not for or against here - but i do believe being able to mixin > classes, > without some really slick handling of super/initialize, state, and > class_init > on ruby's part would be confusing for most. in otherwords, ruby > would need to > become mi to do so. Isn't this already an issue with module-only includes? There was some discussion on this recently I think. We got to this point in the discussion because of the desire to auto- mixin module methods, which happen to reside inside a singleton *class*. So this raised the question of what does it mean to include a class? I still don't like the idea of auto-mixin of module methods but to make sense of that idea you must somehow grapple with the notion that instance methods can be implicitly extracted from a singleton class object (i.e. an instance of Class). Once you go down that road, it just begs the question of why you can't do that with any class object. Gary Wright
on 13.06.2006 21:18
Hi,
In message "Re: Why the lack of mixing-in support for Class methods?"
on Wed, 14 Jun 2006 02:31:00 +0900, transfire@gmail.com writes:
|> I don't think I understand you. Do you want to allow #include to
|> include classes? Without making it MI? Hmm.
|
|No no. You'd still only be able to include modules. I'm thinking a
|module's "singleton class" could be a "singleton module", so to speak,
|instead. It's a bit difficult to communicate b/c the terminology is
|caught up in how it curently works. But it's a lot less fancy than I
|think it sounds.
That makes modules too "special". Every object has its own "singleton
class" (aka eigenclass) no matter what class it belongs to, under the
current implementation. After your proposal, modules should be
treated specially not to have ordinary singleton classes. I know its
purpose (to allow injecting a module's singleton methods using
#include), but I am not sure if it's worth making object model
complex.
It seems Daniel's _API_ is good enough. What's wrong with the idea?
Except for implementation issues, of course.
matz.
on 13.06.2006 21:57
On Wed, 14 Jun 2006 gwtmp01@mac.com wrote: > I don't see how your example is any different than the problem presented by > nested includes. The ordering of ancestors and thus the resolution of > method lookup is well defined in either case, no? yes. the problem is that mixin modules, by design, seldom have name collisions with important instance methods, while mixin classes, if they existed, always would. it's a matter of degrees. regards. -a
on 13.06.2006 23:38
are you looking for that here?
module MixInClassMethods
def test
puts "test"
end
end
try using 'extend'
class MyClass
extend MixInClassMethods
end
> MyClass.test
"test"
Andrew Stone schrieb:
on 14.06.2006 04:55
Yukihiro Matsumoto wrote: > That makes modules too "special". Every object has its own "singleton > class" (aka eigenclass) no matter what class it belongs to, under the > current implementation. After your proposal, modules should be > treated specially not to have ordinary singleton classes. I know its > purpose (to allow injecting a module's singleton methods using > #include), but I am not sure if it's worth making object model > complex. I see your point. This solution is kind of drastic in that respect. Although I sort of liked the symmetry of a module being a module from "head-to-toe" ;) And it's knid of too bad too, b/c this solution limited the capability just to module's class-level, where it's needed and no where else. Other solutions tend to be much broader and/or hackish. > It seems Daniel's _API_ is good enough. What's wrong with the idea? > Except for implementation issues, of course. Well, that's what I've been using already. In fact, as far as I can tell the Facets' implementation is the most complete around (not to say it can't be improved). But even so, it is hackish, inefficient, and fragile, and that really can;t be hellped because of the naturr of implementing it. And its' made worse in that the techinque is without standard, so nayone can run roughshod right over it. These are the reasons why I've asked you directly about the issue. I'm essentially at my wits end with it. I've spent more hours on this one problem than I care to admit. This evening is sadly no exception. I attempted another implementation, which I think has a better overall interface (not as good as just being able to include the singleton, but...) module M def meta.x self end end class X meta_include M end X.x #=> X Alas, an implementation of this appears to be impossible. Again those singleton methods are just out of reach and it is frustrating to no end. T.
on 14.06.2006 05:10
Hi,
In message "Re: Why the lack of mixing-in support for Class methods?"
on Wed, 14 Jun 2006 11:53:32 +0900, transfire@gmail.com writes:
|> It seems Daniel's _API_ is good enough. What's wrong with the idea?
|> Except for implementation issues, of course.
|
|Well, that's what I've been using already. In fact, as far as I can
|tell the Facets' implementation is the most complete around (not to say
|it can't be improved). But even so, it is hackish, inefficient, and
|fragile, and that really can;t be hellped because of the naturr of
|implementing it. And its' made worse in that the techinque is without
|standard, so nayone can run roughshod right over it. These are the
|reasons why I've asked you directly about the issue. I'm essentially at
|my wits end with it. I've spent more hours on this one problem than I
|care to admit.
You got me wrong again. I asked you what if I'd add Daniel's API to
the core.
matz.
on 14.06.2006 06:38
Yukihiro Matsumoto wrote: > |it can't be improved). But even so, it is hackish, inefficient, and > |fragile, and that really can;t be hellped because of the naturr of > |implementing it. And its' made worse in that the techinque is without > |standard, so nayone can run roughshod right over it. These are the > |reasons why I've asked you directly about the issue. I'm essentially at > |my wits end with it. I've spent more hours on this one problem than I > |care to admit. > > You got me wrong again. I asked you what if I'd add Daniel's API to > the core. Ah yea, sorry for rambling. As I said, I already use that API, so, yea, that works. I just don't know the best solution and have been throwing out ideas. I was hoping maybe you could tell us. So if you did this, would #include handle the extending? Or would another method need to be used? T.
on 14.06.2006 07:25
Well, I hate to throw a wrench in the works at this point, but (by
God's grace) I may have managed a better solution. It can be
implemented in either of two ways. Either by extending module with some
special methods like so:
class Module
def propagating_extensions
@propagating_extensions ||= []
end
def propagate( *mods )
propagating_extensions.concat mods
end
def extend_and_propagate( *mods )
propagate *mods
extend *mods
end
alias_method :append_features_without_propagation, :append_features
def append_features( base )
unless propagating_extensions.empty?
base.extend_and_propagate *propagating_extensions
end
append_features_without_propagation( base )
end
end
Or alternatively sepearating the capability into a subclass of module:
class Capsule < Module
def extensions
@extensions ||= []
end
def propagate( *mods )
extensions.concat mods
end
def extend( *mods )
propagate *mods
super
end
def append_features( base )
unless extensions.empty?
base.extend *extensions
end
super
end
end
The later is a little more elegant in implementation but requires the
use of the specialized class to continue the propogating behavior. The
former doesn't need the special class, but does require an additional
special method.
Here's an example of using the first implementation.
module Foo
def foo ; "foo" ; end
end
module Bar
extend_and_propagate Foo
end
class X
include Bar
end
p X.foo #=> "foo"
And here's an example of the second:
module Foo
def foo ; "foo" ; end
end
Bar = Capsule.new do
extend Foo
end
class X
include Bar
end
p X.foo #=> "foo"
T.
on 14.06.2006 08:28
Hi,
In message "Re: Why the lack of mixing-in support for Class methods?"
on Wed, 14 Jun 2006 13:36:34 +0900, transfire@gmail.com writes:
|So if you did this, would #include handle the extending? Or would
|another method need to be used?
I thinking of something like the following:
module Moo
def im
end
class_extension do
def cm
end
end
end
class Foo
include Foo
end
foo = Foo.new
foo.im
Foo.im
The name 'class_extension' may be changed in the real implementation,
or maybe it would be a new visibility.
matz.
on 14.06.2006 08:37
On 6/13/06, Yukihiro Matsumoto <matz@ruby-lang.org> wrote: > module Moo > def im > end > > class_extension do Maybe it could be called class_methods ? > Foo.im Do you mean Foo.cm ? > The name 'class_extension' may be changed in the real implementation, > or maybe it would be a new visibility. > Is this just a more automatic/nicer way to do: module Moo def self.included mod mod.extend ClassMethods end module ClassMethods def cm end end def im end end class Foo include Moo end ? It does seem a bit more convenient. Phil
on 14.06.2006 08:50
Hi,
In message "Re: Why the lack of mixing-in support for Class methods?"
on Wed, 14 Jun 2006 15:34:43 +0900, "Phil Tomson"
<rubyfan@gmail.com> writes:
|> class_extension do
|
|Maybe it could be called class_methods ?
I thought of the name first, but I changed my mind because it is too
similar to methods with different behavior e.g. private_methods.
Besides that class_extension can be used for something other than
methods, for example, constant definition.
|> Foo.im
|
|Do you mean Foo.cm ?
Yes. What a shame.
|Is this just a more automatic/nicer way to do:
|
|module Moo
| def self.included mod
| mod.extend ClassMethods
| end
| module ClassMethods
| def cm
| end
| end
| def im
| end
|end
|
|class Foo
| include Moo
|end
|
|?
Maybe. I am not opposing to this style.
|It does seem a bit more convenient.
How is it more convenient?
matz.
on 14.06.2006 11:06
transfire@gmail.com wrote:
> X.x #=> X
I've been down that road myself, trying something like this:
module A
def this.foo
"foo"
end
end
But sadly, I found it impossible. Maybe I'll give it another try today.
Cheers,
Daniel
on 14.06.2006 13:57
On Jun 14, 2006, at 1:34 AM, Phil Tomson wrote: >> >> module Moo >> def im >> end >> >> class_extension do > > Maybe it could be called class_methods ? I think that name misrepresents what the method does. James Edward Gray II
on 14.06.2006 14:26
On Jun 14, 2006, at 1:48 AM, Yukihiro Matsumoto wrote: > | mod.extend ClassMethods > | include Moo > |end > | > |? > > Maybe. I am not opposing to this style. I think I still prefer this approach, though the method thing is OK with me too (as long as we don't call it class_methods()). James Edward Gray II
on 14.06.2006 15:39
James Edward Gray II wrote: > > |module Moo > > | > > |class Foo > > | include Moo > > |end > > | > > |? > > > > Maybe. I am not opposing to this style. > > I think I still prefer this approach, though the method thing is OK > with me too (as long as we don't call it class_methods()). If you allow #class_extension (or whatever you want to call it) to also take a list of modules in addition to the block, then you'd effectively have it both ways. Ex- module Moo module ClassMethods def cm end end class_extension ClassMethods end T.
on 14.06.2006 16:18
Hi,
In message "Re: Why the lack of mixing-in support for Class methods?"
on Wed, 14 Jun 2006 22:37:24 +0900, transfire@gmail.com writes:
|If you allow #class_extension (or whatever you want to call it) to also
|take a list of modules in addition to the block, then you'd effectively
|have it both ways. Ex-
|
| module Moo
| module ClassMethods
| def cm
| end
| end
| class_extension ClassMethods
| end
I think class_extension (or whatever) plus and include will do.
module Moo
module ClassMethods
def cm
end
end
class_extension do
include ClassMethods
end
end
|T.
|
|
on 14.06.2006 16:31
Yukihiro Matsumoto wrote:
> I think class_extension (or whatever) plus and include will do.
Nice point.
Okay, so I'm wondering, will the annonymous module this creates get a
name of some sort instead of the ususal "#<Module:0xb7ca7e1c>" and if
the class_extension method is used more than once will it create an
additional new module or just reuse the first?
Thanks,
T.
on 14.06.2006 16:41
Hi,
In message "Re: Why the lack of mixing-in support for Class methods?"
on Wed, 14 Jun 2006 23:30:25 +0900, transfire@gmail.com writes:
|Okay, so I'm wondering, will the annonymous module this creates get a
|name of some sort instead of the ususal "#<Module:0xb7ca7e1c>" and if
|the class_extension method is used more than once will it create an
|additional new module or just reuse the first?
I am not thinking about implementation detail yet. What do you think
how it should behave?
matz.
on 14.06.2006 16:54
On Jun 14, 2006, at 10:30 AM, transfire@gmail.com wrote: > Okay, so I'm wondering, will the annonymous module this creates get a > name of some sort instead of the ususal "#<Module:0xb7ca7e1c>" I was thinking that the block would just be evaluated in the context of the targeted singleton class as vs. the context of a new anonymous module which is *then* included into the targeted singleton class. Gary Wright
on 14.06.2006 18:08
On 6/13/06, Yukihiro Matsumoto <matz@ruby-lang.org> wrote: > similar to methods with different behavior e.g. private_methods. > | > |end > > How is it more convenient? > I mean your proposal seems more convenient than the common idiom I showed. Phil
on 14.06.2006 19:26
Yukihiro Matsumoto wrote: > I am not thinking about implementation detail yet. What do you think > how it should behave? It's not as simple as it seems. After manuevering past the potential for infinite recursion on #include, I've see another issue: the old Dynamic Module Inclusion problem. This is what I'm playing with: class Module def class_extension( *mods, &blk ) unless @class_extension @class_extension = Module.new def @class_extension.append_features( base ) append_features_without_extension( base ) end extend @class_extension end @class_extension.module_eval(&blk) if blk @class_extension end alias_method :append_features_without_extension, :append_features def append_features( base ) ce = class_extension base.class_extension { include ce } append_features_without_extension( base ) end end Problem is that 'extend @class_extension' doesn't work unless it occurs _after_ the module_eval(&blk). Which means later changes to the extensions module will have no effect on modules that have already included it. Not good. So unless I've misanalyzed this, I think keeping a running list of extension modules, more like my previous implementation post, is going to be neccessary. T.
on 14.06.2006 19:46
gwtmp01@mac.com wrote: > > On Jun 14, 2006, at 10:30 AM, transfire@gmail.com wrote: >> Okay, so I'm wondering, will the annonymous module this creates get a >> name of some sort instead of the ususal "#<Module:0xb7ca7e1c>" > > I was thinking that the block would just be evaluated in the context of > the targeted singleton class as vs. the context of a new anonymous > module which is *then* included into the targeted singleton class. Then inheritance problems will arise -- I think we need to use modules. Daniel
on 14.06.2006 20:11
On Thu, 15 Jun 2006, Daniel Schierbeck wrote:
> Then inheritance problems will arise -- I think we need to use modules.
can someone point out to me what's wrong with this impl? it's simple,
works
in a recursive way, is self documenting, is already used, and is 34
lines
long. as far as i can tell it already does every required and it very
simple
and robust.
module Mixable
Mixin = lambda do |this, other|
cm = this.const_get 'ClassMethods' rescue nil
im = this.const_get 'InstanceMethods' rescue nil
other.extend cm if cm
other.module_eval{
include im if im
extend RecursiveMixin
}
end
module RecursiveMixin
def included other
Mixin[self, other]
super
end
end
extend RecursiveMixin
end
class Object
def mixin other
sc =
class << self
self
end
sc.module_eval{ mixin other }
end
end
class Module
def mixin other
other.module_eval{ include Mixable } unless
Mixable > other
include other
end
end
if __FILE__ == $0
#
# example 1 - simple usage
#
module M
include Mixable
module ClassMethods
def foo() 42 end
end
end
class C
include M
end
p C.foo #=> 42
#
# example 2 - note the how it works recursively
#
module A
include Mixable
module ClassMethods
def bar() 'forty-two' end
end
module InstanceMethods
def foobar() 42.0 end
end
end
module B
include A
end
class K
include B # and recurively add ClassMethods from A!
end
p K.bar #=> 'forty=two'
p K.new.foobar #=> 42.0
#
# example 3 - we needn't both with Mixable if the 'includee' specifies
that it
# wants to to a mixin
#
module M
module ClassMethods
def bar() 42.0 end
end
def foo() 42 end
end
class C
mixin M
end
p C.bar #=> 42.0
p C.new.foo #=> 42
#
# and, of course, it works with objects
#
mixin M
p foo #=> 42
class << self
p bar #=> 42.0
end
end
-a
on 14.06.2006 21:19
transfire@gmail.com wrote: > Problem is that 'extend @class_extension' doesn't work unless it occurs > _after_ the module_eval(&blk). Which means later changes to the > extensions module will have no effect on modules that have already > included it. Not good. So unless I've misanalyzed this, I think keeping > a running list of extension modules, more like my previous > implementation post, is going to be neccessary. Why should the order of calling extend and adding methods matter? It doesn't seem to in this example, but this is much simpler than class_extension: module M def foo; p "FOO"; end end class C extend M end module M def bar; p "BAR"; end end C.foo C.bar __END__ "FOO" "BAR"
on 14.06.2006 21:28
Joel VanderWerf wrote: > Why should the order of calling extend and adding methods matter? It > doesn't seem to in this example, but this is much simpler than > class_extension: It's a little more subtle than that, because it has to do with modules included in modules. irb(main):001:0> module M irb(main):002:1> def foo; p "FOO"; end irb(main):003:1> end => nil irb(main):004:0> module N irb(main):005:1> def bar; p "BAR"; end irb(main):006:1> end => nil irb(main):007:0> module O irb(main):008:1> def baz; p "BAZ"; end irb(main):009:1> end => nil irb(main):010:0> module M irb(main):011:1> include N irb(main):012:1> end => M irb(main):013:0> class C irb(main):014:1> extend M irb(main):015:1> end => C irb(main):016:0> C.foo "FOO" => nil irb(main):017:0> C.bar "BAR" => nil irb(main):018:0> module M irb(main):019:1> include O irb(main):020:1> end => M irb(main):021:0> C.baz NoMethodError: undefined method `baz' for C:Class from (irb):21 from :0 T.
on 14.06.2006 21:49
transfire@gmail.com wrote: > Joel VanderWerf wrote: > >> Why should the order of calling extend and adding methods matter? It >> doesn't seem to in this example, but this is much simpler than >> class_extension: > > It's a little more subtle than that, because it has to do with modules > included in modules. Yes, I see your point. Two questions: 1. Are chained inclusions of modules a good way to propagate class methods? (Do we really need this feature, for good design?) 2. Do we really want the modules to respond to the class methods that they provide to classes? (I think that's the way your class_extension works, but in my own test implementation I avoided that.)
on 14.06.2006 21:59
Joel VanderWerf wrote: > Yes, I see your point. Two questions: > > 1. Are chained inclusions of modules a good way to propagate class > methods? (Do we really need this feature, for good design?) Yes, b/c if you don't you won't be able to propogate data/control through the hierarcy. i.e. calling #super. > 2. Do we really want the modules to respond to the class methods that > they provide to classes? (I think that's the way your class_extension > works, but in my own test implementation I avoided that.) In some cases one will, so it important to have the ability. In contrast, I could not think of any benefit of not having the modules respond, nor any adverse effect from having them always respond. So I'd say yes here as well. Make sense? T.
on 14.06.2006 23:37
ara.t.howard@noaa.gov wrote: > can someone point out to me what's wrong with this impl? it's simple, > works > in a recursive way, is self documenting, is already used, and is 34 lines > long. as far as i can tell it already does every required and it very > simple > and robust. I can of course only speak for myself, but I'm not fond of the idea of reserving constant names when it's really not needed -- "class extension", as it seemingly has come to be named, should be handled internally, within the module that defines such an extension. Even if the ClassMethods and InstanceMethods modules are private, they would still clutter the namespace. By using anonymous modules in instance variables you avoid collisions (not completely of course.) Furthermore, I feel that it's redundant to have an entire "child" module for the instance methods. Not that the implementation is bad at all; I just don't think it's streamlined enough to make it to the core, although that obviously isn't my call to make. Cheers, Daniel
on 15.06.2006 02:19
On Thu, 15 Jun 2006, Daniel Schierbeck wrote: hi daniel- > I can of course only speak for myself, but I'm not fond of the idea of > reserving constant names when it's really not needed -- "class extension", > as it seemingly has come to be named, should be handled internally, within > the module that defines such an extension. Even if the ClassMethods and > InstanceMethods modules are private, they would still clutter the namespace. > By using anonymous modules in instance variables you avoid collisions (not > completely of course.) > > Furthermore, I feel that it's redundant to have an entire "child" module for > the instance methods. indeed. read the code though, it's not needed - it's just for symmtry. > Not that the implementation is bad at all; I just don't think it's > streamlined enough to make it to the core, although that obviously isn't my > call to make. here's my complaint against any non-module based solution: it wildly violates POLS due to the change in scoping. i'm not saying it can't be done, but read over these tests/demos carefully and you'll see it's not quite as straightforward as you're suggesting - espcially if you want 'normal' class method definition semantics. i think you may be able to work around some of these issues, but some are part of ruby. harp:~ > ruby a.rb _______________________________________________________________________________ recursive inclusion : meta_module _______________________________________________________________________________ a.rb:140:in `included': stack level too deep (SystemStackError) from a.rb:140:in `included' from a.rb:9 from a.rb:197:in `show' from a.rb:194:in `show' from a.rb:4 _______________________________________________________________________________ recursive inclusion : mixable _______________________________________________________________________________ success _______________________________________________________________________________ double inclusion : meta_module _______________________________________________________________________________ a.rb:140:in `append_features': cyclic include detected (ArgumentError) from a.rb:140:in `included' from a.rb:37 from a.rb:197:in `show' from a.rb:194:in `show' from a.rb:23 _______________________________________________________________________________ double inclusion : mixable _______________________________________________________________________________ success _______________________________________________________________________________ namespace pollution : meta_module _______________________________________________________________________________ a.rb:73: N polluted! (RuntimeError) from a.rb:197:in `show' from a.rb:194:in `show' from a.rb:60 _______________________________________________________________________________ namespace pollution : mixable _______________________________________________________________________________ success _______________________________________________________________________________ constant scoping : meta_module _______________________________________________________________________________ a.rb:104:in `const_get': uninitialized constant #<Module:0xb75ccc50>::C (NameError) from a.rb:104 from a.rb:131:in `meta_module' from a.rb:102 from a.rb:197:in `show' from a.rb:194:in `show' from a.rb:100 _______________________________________________________________________________ constant scoping : mixable _______________________________________________________________________________ success harp:~ > cat a.rb # # recursive inclusion # show('recursive inclusion', :meta_module){ module M meta_module{ def foo() :foo end } include M end } show('recursive inclusion', :mixable){ module M module ClassMethods def foo() :foo end end mixin M end } # # double inclusion # show('double inclusion', :meta_module){ module M meta_module{ def foo() :foo end } end module N meta_module{ def bar() :bar end } include M end class C include M include N end } show('double inclusion', :mixable){ module M module ClassMethods def foo() :foo end end end module N module ClassMethods def bar() :bar end end mixin M end class C mixin M mixin N end } # # namespace pollution # show('namespace pollution', :meta_module){ module N meta_module{ def foo() :foo end } end module M include N meta_module{ def bar() :foo end # defined in N! } end if N.respond_to? 'bar' raise 'N polluted!' else true end } show('namespace pollution', :mixable){ module N module ClassMethods def foo() :foo end end end module M mixin N module ClassMethods def bar() :foo end end end if N.respond_to? 'bar' raise 'N polluted!' else true end } # # constant scoping # show('constant scoping', :meta_module){ module N meta_module{ C = true const_get :C } end true } show('constant scoping', :mixable){ module N module ClassMethods C = true const_get :C end end true } BEGIN { # # define two impls of class method mixin # META_MODULE_IMPL = lambda { class Module def meta_module(&block) @meta_module ||= Module.new @meta_module.module_eval(&block) extend(@meta_module) @meta_module end def included(mod) mod.extend(@meta_module ||= Module.new) if mod.kind_of? Module if mod.instance_variables.include? "@meta_module" other_meta_module = mod.instance_variable_get(:@meta_module) other_meta_module.send(:include, @meta_module) else mod.instance_variable_set(:@meta_module, @meta_module) end end end end } MIXABLE_IMPL = lambda { module Mixable Mixin = lambda do |this, other| cm = this.const_get 'ClassMethods' rescue nil im = this.const_get 'InstanceMethods' rescue nil other.extend cm if cm other.module_eval{ include im if im extend RecursiveMixin } end module RecursiveMixin def included other Mixin[self, other] super end end extend RecursiveMixin end class Object def mixin other sc = class << self self end sc.module_eval{ mixin other } end end class Module def mixin other other.module_eval{ include Mixable } unless Mixable > other include other end end } # # demonstrate implications of two implimentations of class method mixin # def show label, which, &code div = '_' * 79 puts div puts "#{ label } : #{ which }" puts div impl = Object.const_get(which.to_s.upcase << '_IMPL') fork { STDOUT.sync = STDERR.sync = true impl.call ret = code.call puts(ret ? 'success' : 'failed') } Process.wait ensure 2.times{ puts } end } regards. -a
on 15.06.2006 05:14
On 6/15/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote: > > here's my complaint against any non-module based solution: it wildly violates > POLS due to the change in scoping. i'm not saying it can't be done, but read > over these tests/demos carefully and you'll see it's not quite as > straightforward as you're suggesting - espcially if you want 'normal' class > method definition semantics. > > i think you may be able to work around some of these issues, but some are part > of ruby. > [snip persuasive code examples] You ~could~ get round the constant scoping problem with: module N meta_module{ self::C = true const_get :C } end which is similar to the way we deal with locals shadowing attributes (and just as likely to catch us out). Also, using: mod.instance_variable_set(:@meta_module, @meta_module.clone) seems to avoid the double inclusion and namespace pollution problems. I can't see a way to avoid the 'stack level too deep' in recursive inclusion though. However, as I said earlier, I think your implementation is the most straightforward (though I also think that having the InstanceMethods module in there is obscuring your argument in the present discussion). Regards, Sean
on 15.06.2006 09:57
ara.t.howard@noaa.gov wrote: > here's my complaint against any non-module based solution: it wildly > violates > POLS due to the change in scoping. i'm not saying it can't be done, but > read > over these tests/demos carefully and you'll see it's not quite as > straightforward as you're suggesting - espcially if you want 'normal' class > method definition semantics. I'm not saying it'll be easy, but I do think it's possible to use anonymous modules. I'll see if I can find time today to further develop the existing implementation. There'll be flaws at first, of course, but if we could avoid the whole "in your face" mentality I think has sprung up a bit in this thread, I'm sure we'll prevail, as George junior would've said :) Daniel
on 15.06.2006 09:58
Hi,
In message "Re: Why the lack of mixing-in support for Class methods?"
on Thu, 15 Jun 2006 04:59:18 +0900, transfire@gmail.com writes:
|> 2. Do we really want the modules to respond to the class methods that
|> they provide to classes? (I think that's the way your class_extension
|> works, but in my own test implementation I avoided that.)
|
|In some cases one will, so it important to have the ability. In
|contrast, I could not think of any benefit of not having the modules
|respond, nor any adverse effect from having them always respond. So I'd
|say yes here as well.
I consider a module as a place holder of methods to be injected into
classes. From that view, a module does not have to have same methods
that classes will have; it is not a class. If you consider a module
as a ripped off class, you need to go for multiple inheritance.
matz.
on 15.06.2006 09:58
Hi,
In message "Re: Why the lack of mixing-in support for Class methods?"
on Thu, 15 Jun 2006 02:25:14 +0900, transfire@gmail.com writes:
|Problem is that 'extend @class_extension' doesn't work unless it occurs
|_after_ the module_eval(&blk). Which means later changes to the
|extensions module will have no effect on modules that have already
|included it. Not good. So unless I've misanalyzed this, I think keeping
|a running list of extension modules, more like my previous
|implementation post, is going to be neccessary.
It's how modules work, at least under the current implementation.
If you want/have to fix it, you should fix it all.
| class Module
|
| def class_extension( *mods, &blk )
| unless @class_extension
| @class_extension = Module.new
| def @class_extension.append_features( base )
| append_features_without_extension( base )
| end
| extend @class_extension
| end
| @class_extension.module_eval(&blk) if blk
| @class_extension
| end
I am not going to call "extend @class_extension" for the target
module. That's not necessary, even bad from my eyes. I think this is
where our opinions differ.
matz.
on 15.06.2006 10:13
Yukihiro Matsumoto wrote: > |implementation post, is going to be neccessary. > | append_features_without_extension( base ) > | end > | extend @class_extension > | end > | @class_extension.module_eval(&blk) if blk > | @class_extension > | end > > I am not going to call "extend @class_extension" for the target > module. That's not necessary, even bad from my eyes. I think this is > where our opinions differ. You mean to say you are going to eval the code directly into the target module instead? T.
on 15.06.2006 10:47
Okay, I've sorted out a few of the problems, and I hope I haven't created new ones. 1 class Module 2 def class_extension(&block) 3 @class_extension ||= Module.new 4 @class_extension.module_eval(&block) 5 end 6 7 def included(mod) 8 if not @class_extension.nil? 9 case mod 10 when Class 11 mod.extend(@class_extension) 12 when Module 13 unless mod.instance_variables.include? "@class_extension" 14 mod.instance_variable_set(:@class_extension, Module.new) 15 end 16 other = mod.instance_variable_get(:@class_extension) 17 other.send(:include, @class_extension) 18 end 19 end 20 end 21 end Please report any errors here, sans bashing. Daniel
on 15.06.2006 11:15
Update:
class Module
def class_extension(&block)
@class_extension ||= Module.new
@class_extension.module_eval(&block)
end
def included(mod)
if not @class_extension.nil?
mod.extend(@class_extension)
if mod.instance_of? Module # better way?
unless mod.instance_variables.include? "@class_extension"
mod.instance_variable_set(:@class_extension, Module.new)
end
other = mod.instance_variable_get(:@class_extension)
other.send(:include, @class_extension)
end
end
end
end
Cheers,
Daniel
on 15.06.2006 13:22
This should fix the recursion issue:
class Module
def class_extension(&block)
@class_extension ||= Module.new do
def self.included(mod); end
end
@class_extension.module_eval(&block) if block_given?
@class_extension
end
def included(mod)
mod.extend(class_extension)
if mod.instance_of? Module
unless mod.instance_variables.include? "@class_extension"
mod.send(:class_extension)
end
other = mod.instance_variable_get(:@class_extension)
other.send(:include, class_extension)
end
end
end
I'm sure there are more issues, but I'll fix them as they come.
Cheers,
Daniel
on 15.06.2006 14:11
Simplification. Tell me if I should stop spamming.
class Module
def class_extension(&block)
@class_extension ||= Module.new do
def self.included(mod); end
end
@class_extension.module_eval(&block) if block_given?
@class_extension
end
def included(mod)
mod.extend(class_extension)
if mod.instance_of? Module and mod.respond_to? :class_extension
mod.send(:class_extension).send(:include, class_extension)
end
end
end
The code might need a change if we decide to make #class_extension
private.
Daniel
on 15.06.2006 14:48
Daniel Schierbeck wrote: > > def included(mod) > mod.extend(class_extension) > if mod.instance_of? Module and mod.respond_to? :class_extension > mod.send(:class_extension).send(:include, class_extension) > end > end > end > > The code might need a change if we decide to make #class_extension private. Daniel, I assume you are doing this for the challenge / learning expience.... #included is not the best method to use since it is a callback. While you could override #include itself, probably the most appropriate method to use is #append_features. This is the method include calls to do it's "magic". Also, after defining the class_extension method for Module, you'll want to undefine it for Class, since it has no relevance there: class Class undef_method :class_extension end HTH, T.
on 15.06.2006 16:39
transfire@gmail.com wrote: > I assume you are doing this for the challenge / learning expience.... That, and it's nice to have an implementation that fits with the interface we have pretty much agreed on (well, matz has). > end Thanks for the feedback, here's the updated version. class Module def class_extension(&block) @class_extension ||= Module.new do def self.append_features(mod); end end @class_extension.module_eval(&block) if block_given? @class_extension end def append_features(mod) mod.extend(class_extension) if mod.instance_of? Module and mod.respond_to? :class_extension mod.send(:class_extension).send(:include, class_extension) end end end class Class undef_method :class_extension end #class_extension should probably be private, right? Daniel
on 15.06.2006 17:13
Hi,
In message "Re: Why the lack of mixing-in support for Class methods?"
on Thu, 15 Jun 2006 23:38:24 +0900, Daniel Schierbeck
<daniel.schierbeck@gmail.com> writes:
|That, and it's nice to have an implementation that fits with the
|interface we have pretty much agreed on (well, matz has).
Yes, and there's something that only working code can prove.
|#class_extension should probably be private, right?
I think so. Besides, I am thinking of class/module separation in the
future (both of them will become subclass of Object), so that users
won't confuse modules with classes.
matz.
on 15.06.2006 17:52
On Fri, 16 Jun 2006, Yukihiro Matsumoto wrote: > Hi, > > In message "Re: Why the lack of mixing-in support for Class methods?" > on Thu, 15 Jun 2006 23:38:24 +0900, Daniel Schierbeck <daniel.schierbeck@gmail.com> writes: > > |That, and it's nice to have an implementation that fits with the > |interface we have pretty much agreed on (well, matz has). > > Yes, and there's something that only working code can prove. do you think this is important? harp:~ > cat b.rb require 'class_extension' module M class_extension do p const_get(:File) # File p File # File class File def self.inspect() 'my_file' end end p const_get(:File) # File p File # my_file end end p M::File # my_file p M::const_get(:File) # my_file p M::class_extension::const_get(:File) # File p M::class_extension::File # exception! harp:~ > ruby b.rb File File File my_file my_file my_file File b.rb:18: uninitialized constant #<Module:0xb75cdf08>::File (NameError) i am in the minority, but it bothers me. don't get me wrong, i like the interface - but the semantics seem odd. regards. -a
on 15.06.2006 18:02
#class_extension is now private, the magic has been moved to
#append_features, and #class_extension has been undefined in Class, per
T's request.
class Module
alias_method :__append_features__, :append_features
def class_extension(&block)
@class_extension ||= Module.new do
def self.append_features(mod)
__append_features__(mod)
end
end
@class_extension.module_eval(&block) if block_given?
@class_extension
end
private :class_extension
def append_features(mod)
__append_features__(mod)
mod.extend(class_extension)
if mod.instance_of? Module
mod.send(:class_extension).send(:include, class_extension)
end
end
end
class Class
undef_method :class_extension
end
#send must of course be replaced with #funcall in 1.9 -- has there been
any progress with the naming of that? I checked the archives, but
couldn't find anything never than August '05... it sounds like a "fun
call", if you know what I mean... hopefully you don't.
Daniel
on 15.06.2006 18:12
This seems to have been resolved in the latest version. Daniel
on 15.06.2006 18:12
On Fri, 16 Jun 2006, Daniel Schierbeck wrote: > __append_features__(mod) > mod.extend(class_extension) > #send must of course be replaced with #funcall in 1.9 -- has there been any > progress with the naming of that? I checked the archives, but couldn't find > anything never than August '05... it sounds like a "fun call", if you know > what I mean... hopefully you don't. > > > Daniel hi daniel- is this by design? harp:~ > cat b.rb require 'class_extension' module M class_extension{ attr_accessor 'a' } end M.a # exception! harp:~ > ruby b.rb b.rb:6: undefined method `a' for M:Module (NoMethodError) i lost track of whether the module should respond to this or not... (the thread is looooong). regards. -a
on 15.06.2006 18:19
Daniel Schierbeck wrote: > __append_features__(mod) > mod.extend(class_extension) > #send must of course be replaced with #funcall in 1.9 -- has there been > any progress with the naming of that? I checked the archives, but > couldn't find anything never than August '05... it sounds like a "fun > call", if you know what I mean... hopefully you don't. Isn't #funcall now called #instance_exec? T.
on 15.06.2006 18:20
ara.t.howard@noaa.gov wrote: > harp:~ > ruby b.rb > b.rb:6: undefined method `a' for M:Module (NoMethodError) > > i lost track of whether the module should respond to this or not... (the > thread > is looooong). Hi, Yes, it's by design. Originally I extended the class that defined the class extension, but per the request of some, I removed it again. This should sort it out module M class_extension{ attr_accessor :a, :b } extend class_extension end M.a = 42 Cheers, Daniel
on 15.06.2006 18:23
Hi,
In message "Re: Why the lack of mixing-in support for Class methods?"
on Fri, 16 Jun 2006 01:10:53 +0900, ara.t.howard@noaa.gov writes:
|is this by design?
|
| harp:~ > cat b.rb
| require 'class_extension'
| module M
| class_extension{ attr_accessor 'a' }
| end
| M.a # exception!
Yes. class_extension add features to the target class but not to the
module itself.
class C
include M
end
M.a = 42
p M.a # => 42
matz.
on 15.06.2006 18:33
Hi,
In message "Re: Why the lack of mixing-in support for Class methods?"
on Fri, 16 Jun 2006 00:50:43 +0900, ara.t.howard@noaa.gov writes:
| p M::class_extension::const_get(:File) # File
| p M::class_extension::File # exception!
The difference here is caused because a class statement with in
module_eval defines a constant in the innermost surrounding class /
module (M, this case) in 1.8, and const_get looks ancestors for
constants by default, where double colons do not.
In 1.9, a class statement in module_eval defines a constant in the
target module (anonymous class_extension module in this case). So
that it gives:
File
File
my_file
my_file
/tmp/c.rb:42: warning: toplevel constant File referenced by M::File
File
File
my_file
my_file
for 1.9 that means the new File class is defined under anonymous
class_extension module. It seems more consistent and simple.
matz.
on 15.06.2006 18:33
transfire@gmail.com wrote:
> Isn't #funcall now called #instance_exec?
I think #instance_exec is an eval method.
I liked the `#send!' and `#instance_send' names.
Cheers,
Daniel
on 15.06.2006 18:34
Hi,
In message "Re: Why the lack of mixing-in support for Class methods?"
on Fri, 16 Jun 2006 01:16:08 +0900, transfire@gmail.com writes:
|> #send must of course be replaced with #funcall in 1.9 -- has there been
|> any progress with the naming of that? I checked the archives, but
|> couldn't find anything never than August '05... it sounds like a "fun
|> call", if you know what I mean... hopefully you don't.
I don't. Luckily or not. For your information, 'funcall' is a lisp
function name that has its own history.
|Isn't #funcall now called #instance_exec?
No, they are different.
matz.
on 15.06.2006 18:50
Yukihiro Matsumoto wrote: > ... 'funcall' is a lisp > function name that has its own history. Ahh, the Ruby Collage of Inspiration. Cool. Cheers, Daniel
on 15.06.2006 18:50
On Fri, 16 Jun 2006, Yukihiro Matsumoto wrote: > module (M, this case) in 1.8, and const_get looks ancestors for > /tmp/c.rb:42: warning: toplevel constant File referenced by M::File > File > File > my_file > my_file > > for 1.9 that means the new File class is defined under anonymous > class_extension module. It seems more consistent and simple. yes it does. that makes me feel better. thanks for the explanation. -a
on 15.06.2006 18:53
On Fri, 16 Jun 2006, Yukihiro Matsumoto wrote: > Yes. class_extension add features to the target class but not to the > module itself. > > class C > include M > end > M.a = 42 > p M.a # => 42 code.gsub! /M/, 'C' ?? -a
on 15.06.2006 19:03
Yukihiro Matsumoto wrote: > I don't. Luckily or not. For your information, 'funcall' is a lisp > function name that has its own history. > > |Isn't #funcall now called #instance_exec? > > No, they are different. I see. I wouldn't otherwise have a problem with the name 'funcall' but it's a meta-programming method and as such it's something important to always have available. I have BasicObject class (basically the same as BlankSlate) which clears out the Kernel methods. But some methods are too important to remove. Thankfully all the important ones (except #dup and #clone) start with either '__' or 'instance_' (eg. __send__). But #funcall will add another exception. It would be nicer to see a consistant pattern in the naming of these meta-methods. T.
on 16.06.2006 02:05
Hi,
In message "Re: Why the lack of mixing-in support for Class methods?"
on Fri, 16 Jun 2006 01:51:32 +0900, ara.t.howard@noaa.gov writes:
|> class C
|> include M
|> end
|> M.a = 42
|> p M.a # => 42
| code.gsub! /M/, 'C'
Yes, thank you.
matz.
on 16.06.2006 03:18
On 6/15/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote: > > i am in the minority, but it bothers me. don't get me wrong, i like the > interface - but the semantics seem odd. > Well, I agree with you on both counts - I like your semantics and Daniel's interface. I certainly think you're right to raise the scoping issues of this implementation in 1.8. Luckily, it seems we can have our cake and eat it with ruby 1.9! :) And if people use Daniel's implementation as a shim for 1.8, we now have a clear explanation of the differences between this and what will eventually be the official behaviour. Gotta love this list. Regards, Sean
on 16.06.2006 04:04
Sean O'Halpin wrote: > > And if people use Daniel's implementation as a shim for 1.8, we now > have a clear explanation of the differences between this and what will > eventually be the official behaviour. Right on. I and the Nitro project have been despretely needing a standardized approach to this. I'll be providing a distribution of Daniel's implementation in Facets until it makes its way into Ruby. Thanks Matz, Daniel, Ara, Sean and everyone that participated in this thread! Oh and Alder Green for starting it! (Did you get your answer? ;) BTW Is this possibly the longest ruby-talk thread in history? Sheesh! ;) > Gotta love this list. Indeed. T.
on 16.06.2006 04:38
Hi,
In message "Re: Why the lack of mixing-in support for Class methods?"
on Fri, 16 Jun 2006 02:01:53 +0900, transfire@gmail.com writes:
|I see. I wouldn't otherwise have a problem with the name 'funcall' but
|it's a meta-programming method and as such it's something important to
|always have available. I have BasicObject class (basically the same as
|BlankSlate) which clears out the Kernel methods. But some methods are
|too important to remove. Thankfully all the important ones (except #dup
|and #clone) start with either '__' or 'instance_' (eg. __send__). But
|#funcall will add another exception. It would be nicer to see a
|consistant pattern in the naming of these meta-methods.
Good point. We will have __funcall__ as well. Thank you.
matz.
on 16.06.2006 05:41
On 6/15/06, Yukihiro Matsumoto <matz@ruby-lang.org> wrote: > |Thankfully all the important ones (except #dup > |and #clone) start with either '__' or 'instance_' (eg. __send__). But > |#funcall will add another exception. It would be nicer to see a > |consistant pattern in the naming of these meta-methods. > Good point. We will have __funcall__ as well. Thank you. Should we have __dup__ and __clone__ and __class__ and __object_id__? I am already using #__send__ in preference to #send when I need the functionality, but it would be nice to know that for those methods we literally cannot get along without ... we have access to without rebind hacks. -austin
on 16.06.2006 06:36
On Jun 15, 2006, at 11:40 PM, Austin Ziegler wrote: > Should we have __dup__ and __clone__ and __class__ and __object_id__? > > I am already using #__send__ in preference to #send when I need the > functionality, but it would be nice to know that for those methods we > literally cannot get along without ... we have access to without > rebind hacks. I can definitely see __class__ and __object_id__. #dup and #clone though it seems to me that there would be plently of situations that someone would want to override those, and you would want the overridden versions. Failing that maybe add a #copy method that would default to an alias of #dup but would be for the express purpose of overriding for "deeper" copies. I guess it's a judgement call. (One I thankfully don't have to make).
on 16.06.2006 09:14
Austin Ziegler wrote: > functionality, but it would be nice to know that for those methods we > literally cannot get along without ... we have access to without > rebind hacks. I agree. And I did add __class__ in basic object, though I find it's not so often needed b/c of 'X === x'. Nonetheless it certainly seems like one of the fundamentals. BTW, we have __id__ already, so I don't think __object_id__ is needed. I've also expiremented with two other methods, #__self__ (alias #object) which is a Functor that binds subsequent calls to Object. Eg. x.__self__.class will return the class of x even if x.class won't. I took it a step further with #as / #__as__, in which one can specify which ancestor to bind to instead of just Object. x.__as__(Enumerble).each, for instance. Food for thought. T.
on 16.06.2006 09:21
Logan Capaldo wrote: > I can definitely see __class__ and __object_id__. #dup and #clone > though it seems to me that there would be plently of situations that > someone would want to override those, and you would want the > overridden versions. Failing that maybe add a #copy method that would > default to an alias of #dup but would be for the express purpose of > overriding for "deeper" copies. I guess it's a judgement call. (One I > thankfully don't have to make). Of course __dup__ and __clone__ can be overridden too. But I imagine that would create quite a bit of additional work, so maybe these two are just better left as exceptions ...not sure. T.
on 16.06.2006 11:58
Hi -- On Fri, 16 Jun 2006, Yukihiro Matsumoto wrote: > | class_extension{ attr_accessor 'a' } > p M.a # => 42 Will this happen if the module is included in another module? If so, I'm wondering whether a more generic name, without "class" in it, could be used instead of "class_extension". David -- David A. Black (dblack@wobblini.net) Ruby Power and Light, LLC (http://www.rubypowerandlight.com) See what the readers are saying about "Ruby for Rails"! http://www.rubypowerandlight.com/quotes
on 16.06.2006 12:14
Hi,
In message "Re: Why the lack of mixing-in support for Class methods?"
on Fri, 16 Jun 2006 18:56:23 +0900, dblack@wobblini.net writes:
|> class C
|> include M
|> end
|> M.a = 42
|> p M.a # => 42
|
|Will this happen if the module is included in another module? If so,
|I'm wondering whether a more generic name, without "class" in it,
|could be used instead of "class_extension".
That's where I am wondering. Using the Daniel's code:
module M
class_extension do
def foo
end
end
end
module M2
include M
end
class C
include M2
end
M.foo # => NoMethodError
M2.foo # => Error? (should it affect modules?)
C.foo # => Error? (should class_extension be propagated?)
If 'class_extension' affects modules as well, do you think of any
more generic name, in place of "class" extension?
matz.
on 16.06.2006 12:48
Yukihiro Matsumoto wrote: > include M2 > end > > M.foo # => NoMethodError > M2.foo # => Error? (should it affect modules?) No, the class extension should be included into both modules and classes. I think this'll get too complex otherwise, with users wondering why #include works differently in classes and modules. > C.foo # => Error? (should class_extension be propagated?) I'm not sure on that one, though I'd default to say yes, it should be propagated. > If 'class_extension' affects modules as well, do you think of any > more generic name, in place of "class" extension? I temporarily used #metamodule or something similar, as I saw the class extension as a "module above the module", that got included into the "class above the class". That may be rubbish, but it's how I think of it. Cheers, Daniel
on 16.06.2006 13:03
Yukihiro Matsumoto wrote: > | > end > end > module M2 > include M > end > class C > include M2 > end > > M.foo # => NoMethodError > M2.foo # => Error? (should it affect modules?) I'd say yes. > C.foo # => Error? (should class_extension be propagated?) I'd say no, but maybe there should be another function to import it. module M class_extension do def foo end end end module M2 include_class_extension M end module M3 include M2 end M.foo #=> error M2.foo #=> error M3.foo #=> no error > If 'class_extension' affects modules as well, do you think of any > more generic name, in place of "class" extension? module_extension, included_module_features, define_module_features, ...?
on 16.06.2006 14:53
Yukihiro Matsumoto wrote: > M2.foo # => Error? (should it affect modules?) > > If 'class_extension' affects modules as well, do you think of any > more generic name, in place of "class" extension? The name is not really incorrect because class << Module.new ; self.class ; end #=> Class > C.foo # => Error? (should class_extension be propagated?) It needs to propagate. Otherwise it defeats much of the purpose. Without propapation we could not create module adapters, or use #super, which is absolutely vital when using it to track meta information, like a method annotation for example. T.
on 16.06.2006 14:57
Heh, soon we'll end up adding other __*__ methods for the sake of it. I don't see this as a benefit, and I've never really got down and said, "Gee, I wish this method wasn't overridable!", and if I have, it would have been for `send' which was easily remedied with `__send__'. Also, for #object_id, there's already a #__id__.
on 16.06.2006 16:18
On Fri, 16 Jun 2006, Yukihiro Matsumoto wrote: > end > class C > include M2 > end > > M.foo # => NoMethodError > M2.foo # => Error? (should it affect modules?) > C.foo # => Error? (should class_extension be propagated?) > > If 'class_extension' affects modules as well, do you think of any more > generic name, in place of "class" extension? singleton_methods &block -a
on 16.06.2006 16:40
ara.t.howard@noaa.gov wrote: >> If 'class_extension' affects modules as well, do you think of any more >> generic name, in place of "class" extension? > > singleton_methods &block Except it's not restricted to methods. Here's a couple of spinoffs: * singleton_module * singleton_extension Cheers, Daniel
on 16.06.2006 17:07
Daniel Schierbeck wrote:
>
Eeek! Please, no "singleton". Anything but "singleton".
T.
on 16.06.2006 17:26
transfire@gmail.com wrote: > > Eeek! Please, no "singleton". Anything but "singleton". `beer_module'?
on 16.06.2006 17:45
On Sat, 17 Jun 2006 transfire@gmail.com wrote: > > Eeek! Please, no "singleton". Anything but "singleton". > what's wrong with singleton? alternatives? -a
on 16.06.2006 18:04
On Jun 16, 2006, at 11:43 AM, ara.t.howard@noaa.gov wrote: > On Sat, 17 Jun 2006 transfire@gmail.com wrote: >> >> Eeek! Please, no "singleton". Anything but "singleton". >> > > what's wrong with singleton? alternatives? Uh oh. Here we go again... Gary Wright
on 16.06.2006 18:29
"Austin Ziegler" <halostatue@gmail.com> writes: > On 6/15/06, Yukihiro Matsumoto <matz@ruby-lang.org> wrote: >> |Thankfully all the important ones (except #dup >> |and #clone) start with either '__' or 'instance_' (eg. __send__). But >> |#funcall will add another exception. It would be nicer to see a >> |consistant pattern in the naming of these meta-methods. >> Good point. We will have __funcall__ as well. Thank you. > > Should we have __dup__ and __clone__ and __class__ and __object_id__? Instead of further pythonizing the language (sorry, couldn't resist), I'd rather prefer a non-OO way for the tricky cases: Object.object_id_of myobj Object.class_of myobj Object.send_to myobj, :foo Maybe it's not optimal to make them singleton methods of Object, a module would probably be better.
on 16.06.2006 20:00
On 6/16/06, Christian Neukirchen <chneukirchen@gmail.com> wrote:
> module would probably be better.
I could get behind that. Probably worth an RCR, I think.
-austin
on 16.06.2006 21:02
On 6/16/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote: > On Sat, 17 Jun 2006 transfire@gmail.com wrote: > > > > > Eeek! Please, no "singleton". Anything but "singleton". > > > > what's wrong with singleton? alternatives? > > -a > -- Off the top of my head: extension &block inheritance &block effects &block (in both senses - i.e. this is what it makes happen and this is what it 'bequeathes' to its inheritors) Regards, Sean
on 16.06.2006 23:31
Hi-- On Fri, 16 Jun 2006, Yukihiro Matsumoto wrote: > | > end > end > module M2 > include M > end > class C > include M2 > end > > M.foo # => NoMethodError > M2.foo # => Error? (should it affect modules?) Yes, I think so. > C.foo # => Error? (should class_extension be propagated?) I think not. The call to class_extension in M registers a policy on the part of M. The policy affects M2, but it should not be shared by M2. > If 'class_extension' affects modules as well, do you think of any > more generic name, in place of "class" extension? Sean O'H. mentioned "extension", which I think is as good as it will get. (I'm still not a fan of this whole thing.) David -- David A. Black (dblack@wobblini.net) Ruby Power and Light, LLC (http://www.rubypowerandlight.com) See what the readers are saying about "Ruby for Rails"! http://www.rubypowerandlight.com/quotes
on 17.06.2006 15:02
Austin Ziegler wrote: > > Maybe it's not optimal to make them singleton methods of Object, a > > module would probably be better. > > I could get behind that. Probably worth an RCR, I think. I expiremented with that idea a while back when I was doing some work on the BasicObject class. I expirementd with offering a variety of "extenal" meta-programming methods as module methods of ObjectSpace. And I also tried using global variable lambas, eg. $object_id[myobj]. While the former was more POLS, the globals actually proved nicer to use. Yet I'm not sure its the right approach. Is it that big a deal to have a handful of __ methods? If so then maybe the milder appraoch of a single non-overwriteable "self knowledge" method would be a good way, 'myobj.self(:id)' or even 'myobj.self.id' for instance. T.
on 17.06.2006 17:22
On Sat, Jun 17, 2006 at 01:28:12AM +0900, Christian Neukirchen wrote: > Instead of further pythonizing the language (sorry, couldn't resist), > I'd rather prefer a non-OO way for the tricky cases: > > Object.object_id_of myobj > Object.class_of myobj > Object.send_to myobj, :foo So, it quacks like a dog, but if we really press it, we can get a proper fowlsome noise? >> duck.quack >> #<CryOfAlarm("GAA! GAA!")> # .. uh.. let's try this again, that was a bit noisey >> Animal.come_on_gimme_the_real_duck_quack duck => #<BasicQuack> I'm not saying it's a totally bad idea. I just don't think I'd be able to spot when Object.object_id_of is necessary. I mean when in advance do you know you're getting nasty mangled objects that don't behave? I generally try to use __send__ right now. I figure if __send__ is overridden, then someone has figured out a better way to dispatch methods. Hey, let's let them try. _why
on 17.06.2006 20:31
why the lucky stiff <ruby-talk@whytheluckystiff.net> writes: > > figured out a better way to dispatch methods. Hey, let's let them try. And what if I still feel more meta than you? ;-) Note that e.g. Ruby itself won't call the updated definition of __send__...
on 18.06.2006 03:40
why the lucky stiff wrote: > > figured out a better way to dispatch methods. Hey, let's let them try. I was looking over the Kernel methods, thinking about which one's are vital and and/or meta in nature. And really it seems like almost all of them are, which probably accounts for why they are in Kernel to begin with. They are (v1.8.4): ["==", "===", "=~", "__id__", "__send__", "class", "clone", "display", "dup", "eql?", "equal?", "extend", "freeze", "frozen?", "hash", "id", "inspect", "instance_eval", "instance_of?", "instance_variable_get", "instance_variable_set", "instance_variables", "is_a?", "kind_of?", "method", "methods", "nil?", "object_id", "private_methods", "protected_methods", "public_methods", "respond_to?", "send", "singleton_methods", "taint", "tainted?", "to_a", "to_s", "type", "untaint"] Looking through these, it really becomes hard to say what should stay and what should go in forming a near empty class. And given their nature one might be inclined to think that most of these should be external to the object anyway. I mean, should the class really have the "right" to override #object_id? After thinking about this further. I started to wonder if maybe the __foo__ notation could be a special syntax Ruby would always take as a method call bound to Kernel, analogous to: def method_missing( meth, *args, &blk ) if md = /^__(.*)__$/.match( meth.to_s ) Kernel.instance_method( md[1] ).bind(self).call(*args, &blk) end super end This example implmentation still allows them to be overridden. Whether that should be so is debatible, though _why makes a good point. If you think you can make a better quack than go for it. Also interesting, If you look as Ruby 1.9 specs you'll see there is now a BasicObject that lies behind Object and Kernel. These are the methods it isolates: [ "__send__", "funcall", "__id__", "==", "send", "respond_to?", "equal?", "object_id"] I'm all for the idea of a BasicObject b/c it will make OpenStruct and classes of that ilk more standardized and robust. But I was wondering what if you wanted to make a subclass of BasicObject but there were one or perhaps a few other Kernel methods you needed to include. Say for instance you needed #instance_variables. How would you be able to include that since Kernel is not even in the class hiearchy? Maybe it isn't that important, but it doesn't seem like it should be out the question. Of course, we take another step back we see that all of this crops up b/c the OpenStruct pattern is just too damn nice not use, even though Hash will usually work just as well. But who wants to do foo[:a][:b][:c] when they can do foo.a.b.c? Moreover an OpenStruct can quack like any duck; a hash can't. But then the little edge problems arise, b/c an OpenStruct pattern is generally used for something that can have completely arbitarty keys. And that's when the Kernel methods can get in the way, not to mention any other internal methods you might would like the class to have. So you clear out most of then methods with BasicObject or BlankSlate or what have you and then some one elses lib croaks b/c it can't #dup you object or whatever. Yeah, it can get nasty. Okay, I'm starting to rattle on, but one last thought occurs to me: might the whole issue go away if private and public methods could have their own separate namespaces? The public method would still be available privately, but if a specifcally private method of the same name were defined it would be used when called without a receiver, rather than the public one. The even for an OpenStruct one could generally ensure the proper response from vital methods by using #__funcall__. T.
on 03.08.2006 14:23
Hi,
In message "Re: Why the lack of mixing-in support for Class methods?"
on Thu, 15 Jun 2006 16:35:58 +0900, transfire@gmail.com writes:
|> I am not going to call "extend @class_extension" for the target
|> module. That's not necessary, even bad from my eyes. I think this is
|> where our opinions differ.
|
|You mean to say you are going to eval the code directly into the target
|module instead?
No. I just said `I am not going to call "extend @class_extension" for
the target module'.
matz.
on 03.08.2006 14:23
Yukihiro Matsumoto wrote: > ... I just said `I am not going to call "extend @class_extension" for > the target module'. Let me get this straight: module A class_extension do def foo :foo end end end module B include A end B.foo #=> NoMethodError Correct? It would make the implementation a whole lot easier... Daniel
on 03.08.2006 14:23
Hi,
In message "Re: Why the lack of mixing-in support for Class methods?"
on Thu, 15 Jun 2006 17:16:01 +0900, Daniel Schierbeck
<daniel.schierbeck@gmail.com> writes:
|Let me get this straight:
|
| module A
| class_extension do
| def foo
| :foo
| end
| end
| end
|
| module B
| include A
| end
|
| B.foo #=> NoMethodError
|
|Correct? It would make the implementation a whole lot easier...
I meant A.foo would raise NoMethodError. Since class_extension
defines a part to inject into the inclusion target, I think B.foo
works as natural consequence of inclusion. If B.foo raises an error,
it makes class/module separation a lot wider than it currently is.
It is an interesting idea though.
matz.
on 03.08.2006 14:23
Yukihiro Matsumoto wrote: > No. I just said `I am not going to call "extend @class_extension" for > the target module'. Okay. Whew. Scared me for a moment. Your last comment about me going for "multiple inheritance" threw me --that's not what I meant, I just said I saw no adverse effect. But I see your point about the intent is class injection. So that makes sense. T.
on 03.08.2006 14:23
Yukihiro Matsumoto wrote: > I meant A.foo would raise NoMethodError. Since class_extension > defines a part to inject into the inclusion target, I think B.foo > works as natural consequence of inclusion. Splendid! That was also my first hunch. I'll just fix my implementation then... Cheers, Daniel