From: Robert Klemme on 24 Jun 2010 06:48 2010/6/24 Caleb Clausen <vikkous(a)gmail.com>: > On 6/22/10, Jörg W Mittag <JoergWMittag+Ruby(a)googlemail.com> wrote: >> Gary Wright wrote: >>> On Jun 21, 2010, at 3:35 PM, Jörg W Mittag wrote: >>>> Eric MSP Veith wrote: >>>>> Modules are used for mixins. Is it possible -- and does it make sense at >>>>> all >>>>> - -- to mixin a class into another class? I guess not? >>>> Actually, to add a bit to Brian's answer: it *is* possible and makes >>>> perfect sense. It's just not allowed in Ruby, but it *is* allowed in >>>> other languages. >>> Allowing classes to mixin other classes would change the class >>> hierarchy from a tree to a directed acyclic graph.  As soon as you >>> do that you have to figure out how to manage the method lookup >>> process along multiple paths. I don't think there is any sort of >>> consensus on how to do that (i.e. different languages take >>> different approaches) and so I'm not sure 'makes perfect sense' is >>> an accurate characterization of the solution space for that >>> problem. >> >> You can take the algorithm Ruby uses for mixins and simply substitute >> classes for modules and it would work exactly the way it does today, >> including the linearization property. >> >> The way mixins work in Ruby is (roughly) as follows (assume that we >> want to mix â¦M⧠into a class â¦Câ§, ignore for a moment the case of >> mixing a mixin into another mixin): >> >> 1. create an anonymous class â¦I⧠>> 2. set â¦Iâ§'s method table pointer to â¦Mâ§'s method table >> 3. set â¦Iâ§'s superclass to the â¦Câ§'s singleton class's superclass >> 4. set â¦Câ§'s singleton class's superclass to â¦I⧠>> >> In other words: create an anonymous class with the same methods but a >> different identity as the mixin and insert it in the ancestor chain >> directly above the singleton class. >> >> There's really no need here for â¦M⧠to be a module, it could just as >> well be a class. The linearization property depends on the fact that >> mixin inclusion is done via a freshly minted anonymous class which has >> no ancestors or descendants of its own: the include class â¦I⧠*only* >> gets the methods from the mixin, it does not get its place in the >> inheritance hierarchy. Whether the method table that â¦I⧠points to >> used to belong to a module or a class is really irrelevant. It's just >> a bag of methods. >> >> The case of mixing in a mixin into another mixin is also handled >> similar to the way it is today: currently, when you mix a module into >> a module, it gets inserted above the module like it would above a >> class. And then, when you mix a module â¦M⧠into a *class*, Ruby not >> only constructs an include class for the â¦Mâ§'s method table, but >> recursively also for all modules mixed into â¦Mâ§. Note that this means >> that this mixin chain is only walked *once*, when you include â¦M⧠into >> the class. If you later mix other modules into â¦Mâ§, their methods will >> *not* be available to instances of the class. >> >> Again, it would be possible to do just the same thing with classes >> instead of modules: walk the chain of include classes and simply clone >> them, then inject them into the ancestors chain like above. >> >> That's what I meant by "makes perfect sense": you can take the exact >> same algorithm that Ruby currently uses for module inclusion and use >> it for class inclusion with the exact same results. > > What you describe might work well enough for pure-ruby classes, but if > you tried to mix a core class (Array, String) into another class, the > result would be segfaults. Maybe the same would be true of any class > implemented in C? Not sure. I believe the more important point is that - although classes and modules have only few differences (inheritance, instance creation) - they are deliberately kept separate because they are language artifacts intended to model different concepts (entities vs. behavior). Even if it is technically possible to mix in classes the same way as modules (and I believe Jörg's assessment is correct here) it probably does not make much sense to allow this from a conceptual point of view. Kind regards robert -- remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/
From: Intransition on 24 Jun 2010 09:21 On Jun 24, 6:48 am, Robert Klemme <shortcut...(a)googlemail.com> wrote: > I believe the more important point is that - although classes and > modules have only few differences (inheritance, instance creation) - > they are deliberately kept separate because they are language > artifacts intended to model different concepts (entities vs. > behavior). Even if it is technically possible to mix in classes the > same way as modules (and I believe Jörg's assessment is correct here) > it probably does not make much sense to allow this from a conceptual > point of view. I disagree. This sort of explanation has no material bases --how does the conceptual distinction help us? I think it hurts us for a few reasons. 1) It disrupts auto-creation of namespaces. E.g. class Y::X end What is Y, a class or module? Because of the distinction between class and module you have to pre-define it. This futher leads to "bottom requiring" --using require at the bottom of scripts instead of at the top. 2) The developer is forced to choose between two arbitrarily limited mechanisms for modeling behavior. Classes can only re-used once per subclass and not at all via modules. Modules can be reused extensively but do not naturally include their singleton methods. These limitations lead to a number of code smells from overly limited use of inheritance to included/ClassMethods hooks. 3) Using tools for conceptually different cases does not require that their be physically different tools for each case. I wrench is just as good for tightening a nut as it is for loosening one. If I had to use two separate tools it would be quite annoying. Following your conceptual distinction argument we could just as easily argue for separate entities for mixins and namespaces since those are also different uses of modules. Clearly no one wants that. I have never heard of a solid reason for the division between classes and modules --my guess is that there is no real reason other than that it how it was done originally, and changing it now means 1) a major version bump and 2) an overhaul of the implementation.
From: Robert Klemme on 24 Jun 2010 11:58 2010/6/24 Intransition <transfire(a)gmail.com>: > > > On Jun 24, 6:48 am, Robert Klemme <shortcut...(a)googlemail.com> wrote: > >> I believe the more important point is that - although classes and >> modules have only few differences (inheritance, instance creation) - >> they are deliberately kept separate because they are language >> artifacts intended to model different concepts (entities vs. >> behavior). Even if it is technically possible to mix in classes the >> same way as modules (and I believe Jörg's assessment is correct here) >> it probably does not make much sense to allow this from a conceptual >> point of view. > > I disagree. This sort of explanation has no material bases --how does > the conceptual distinction help us? I think it hurts us for a few > reasons. > > 1) It disrupts auto-creation of namespaces. E.g. > > class Y::X > end > > What is Y, a class or module? Because of the distinction between class > and module you have to pre-define it. This futher leads to "bottom > requiring" --using require at the bottom of scripts instead of at the > top. I don't understand what you mean here. Can you elaborate or provide an example? > 2) The developer is forced to choose between two arbitrarily limited > mechanisms for modeling behavior. Classes can only re-used once per > subclass and not at all via modules. Modules can be reused extensively > but do not naturally include their singleton methods. These > limitations lead to a number of code smells from overly limited use of > inheritance to included/ClassMethods hooks. Why is not including their singleton methods a strong limitation? > 3) Using tools for conceptually different cases does not require that > their be physically different tools for each case. I wrench is just as > good for tightening a nut as it is for loosening one. If I had to use > two separate tools it would be quite annoying. I believe this is a bad analogy. If at all you would have to take "tightening and loosing nuts" and "hammering". In that case it's quite obvious that you would not normally use the same tool for the job. > Following your conceptual distinction argument we could just as easily > argue for separate entities for mixins and namespaces since those are > also different uses of modules. Clearly no one wants that. Actually I thought about mentioning this because namespacing is yet another concept which happens to have two tools - module and class. It probably feels very natural because all major OO languages do support this. And it does have some merits. > I have never heard of a solid reason for the division between classes > and modules --my guess is that there is no real reason other than that > it how it was done originally, and changing it now means 1) a major > version bump and 2) an overhaul of the implementation. I find that distinction pretty clear as I have tried to point out earlier. Somehow you seem to be using the language quite differently than me and have run into limitations that do not exist for me. Maybe we can take the discussion a bit further to find out more about this. Kind regards robert -- remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/
From: Robert Klemme on 25 Jun 2010 08:30 2010/6/25 Intransition <transfire(a)gmail.com>: > On Jun 24, 11:58 am, Robert Klemme <shortcut...(a)googlemail.com> wrote: > >> I don't understand what you mean here. Can you elaborate or provide an example? > > > # lib/foo/baz.rb > > class Foo::Baz > ... > end > > > # lib/foo.rb > > require 'foo/baz' > > class Foo > ... > end I always thought the proper idiom was # lib/foo.rb class Foo ... end # optionally: require 'foo/baz' # lib/foo/baz.rb require 'foo' class Foo::Baz ... end Alternative: # lib/foo.rb class Foo ... autoload :Baz, 'foo/baz' end # lib/foo/baz.rb (as above) >> > 2) The developer is forced to choose between two arbitrarily limited >> > mechanisms for modeling behavior. Classes can only re-used once per >> > subclass and not at all via modules. Modules can be reused extensively >> > but do not naturally include their singleton methods. These >> > limitations lead to a number of code smells from overly limited use of >> > inheritance to included/ClassMethods hooks. >> >> Why is not including their singleton methods a strong limitation? > > Well it is strong. That's a strange answer to my question... > In fact, if it wasn't for that, the difference be > almost ephemeral. But strong != good. I know that matz has cited this > issue in the past, but I have never seen an explanation of why it is > so bad to get the module singleton methods? Clearly in many cases that > is exactly what people want, which is why they ClassMethods hack is so > common. Let my first try to understand what you mean. If I get you properly you want module A def self.wok puts "I work for the class" end def foo puts "happy instance" self.class.wok end end class X include A end X.new.foo -> "happy instance" "I work for the class" I personally would write def foo puts "happy instance" A.wok end and be done so I suspect you mean something else. > If it were the other way around, and the singleton methods > were passed along, but you didn't want them to be... well I would like > to know under what circumstances that it would really be a problem. If > those singleton methods were simply being used as functions (M.f) then > they can easily be placed in a separate module. If they were being > used as a DSL for the module itself, why would the including class be > any worse off by gaining the DSL too? Maybe there is some reason one > can find, but even so it seems to me that it must be the rarity. If I get your drift you want to use a single module to define a DSL and apply it at the same time, e.g. module DSL def self.smart_attribute(sym) class_eval "def #{sym}; puts 'Look Ma, how smart I am!'; @#{sym}; end" end smart_attribute :name end and then class Foo include DSL smart_attribute :age end f = Foo.new f.name f.age I personally would separate the DSL out into another module because that is more modular. Not all classes might need smart attribute "name" so this would be a better choice IMHO. > In > all my use of modules I can't think of case where having the singleton > level would have been a problem and in many, if not most, I actually > wanted it and had to resort to some hoop jumping. > > In fact, and I think this is worth pointing out... I never ever design > a module to be utilized via #extend. In such usecases I actually do > this: > > module M > def self.append_features(base) > base.extend self > end > end > > Of course, rather than fuss about it, we could also have the best of > both worlds (imagine that). I, for one, would be happy with a new > method to go along with #include and #extend that provided both > singleton and instance levels. I believe automatically propagating class methods might cause confusion because it is not obvious. At least I haven't had the need for this (which might be due to the different tasks we tackle with Ruby). >> > 3) Using tools for conceptually different cases does not require that >> > their be physically different tools for each case. I wrench is just as >> > good for tightening a nut as it is for loosening one. If I had to use >> > two separate tools it would be quite annoying. >> >> I believe this is a bad analogy. If at all you would have to take >> "tightening and loosing nuts" and "hammering". In that case it's >> quite obvious that you would not normally use the same tool for the >> job. > > Maybe so. But a class is a subclass of module. So they are very > similar tools, unlike a hammer and a wrench. My point was simply that, > as craftsman, we'd rather have just one tool, if it can do the job two > just as well. > >> > Following your conceptual distinction argument we could just as easily >> > argue for separate entities for mixins and namespaces since those are >> > also different uses of modules. Clearly no one wants that. >> >> Actually I thought about mentioning this because namespacing is yet >> another concept which happens to have two tools - module and class. >> It probably feels very natural because all major OO languages do >> support this. And it does have some merits. >> >> > I have never heard of a solid reason for the division between classes >> > and modules --my guess is that there is no real reason other than that >> > it how it was done originally, and changing it now means 1) a major >> > version bump and 2) an overhaul of the implementation. >> >> I find that distinction pretty clear as I have tried to point out >> earlier. Somehow you seem to be using the language quite differently >> than me and have run into limitations that do not exist for me. Maybe >> we can take the discussion a bit further to find out more about this. > > Perhaps b/c I have done a lot of meta-programming? Not sure. It would > be interesting to know more, I agree. I believe this points into the right direction. I'm not doing as much meta programming - probably because I am not involved in creating frameworks in Ruby. I rather use the language to solve day to day problems. So far my need for DSL's has been scarce. So where were you really hurt by not automatically propagating class methods? I can't think of a case but obviously you have one in mind. Thanks for the discussion! Kind regards robert -- remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/
From: Intransition on 25 Jun 2010 06:50 On Jun 24, 11:58 am, Robert Klemme <shortcut...(a)googlemail.com> wrote: > I don't understand what you mean here. Can you elaborate or provide an example? # lib/foo/baz.rb class Foo::Baz ... end # lib/foo.rb require 'foo/baz' class Foo ... end > > 2) The developer is forced to choose between two arbitrarily limited > > mechanisms for modeling behavior. Classes can only re-used once per > > subclass and not at all via modules. Modules can be reused extensively > > but do not naturally include their singleton methods. These > > limitations lead to a number of code smells from overly limited use of > > inheritance to included/ClassMethods hooks. > > Why is not including their singleton methods a strong limitation? Well it is strong. In fact, if it wasn't for that, the difference be almost ephemeral. But strong != good. I know that matz has cited this issue in the past, but I have never seen an explanation of why it is so bad to get the module singleton methods? Clearly in many cases that is exactly what people want, which is why they ClassMethods hack is so common. If it were the other way around, and the singleton methods were passed along, but you didn't want them to be... well I would like to know under what circumstances that it would really be a problem. If those singleton methods were simply being used as functions (M.f) then they can easily be placed in a separate module. If they were being used as a DSL for the module itself, why would the including class be any worse off by gaining the DSL too? Maybe there is some reason one can find, but even so it seems to me that it must be the rarity. In all my use of modules I can't think of case where having the singleton level would have been a problem and in many, if not most, I actually wanted it and had to resort to some hoop jumping. In fact, and I think this is worth pointing out... I never ever design a module to be utilized via #extend. In such usecases I actually do this: module M def self.append_features(base) base.extend self end end Of course, rather than fuss about it, we could also have the best of both worlds (imagine that). I, for one, would be happy with a new method to go along with #include and #extend that provided both singleton and instance levels. > > 3) Using tools for conceptually different cases does not require that > > their be physically different tools for each case. I wrench is just as > > good for tightening a nut as it is for loosening one. If I had to use > > two separate tools it would be quite annoying. > > I believe this is a bad analogy. If at all you would have to take > "tightening and loosing nuts" and "hammering". In that case it's > quite obvious that you would not normally use the same tool for the > job. Maybe so. But a class is a subclass of module. So they are very similar tools, unlike a hammer and a wrench. My point was simply that, as craftsman, we'd rather have just one tool, if it can do the job two just as well. > > Following your conceptual distinction argument we could just as easily > > argue for separate entities for mixins and namespaces since those are > > also different uses of modules. Clearly no one wants that. > > Actually I thought about mentioning this because namespacing is yet > another concept which happens to have two tools - module and class. > It probably feels very natural because all major OO languages do > support this. And it does have some merits. > > > I have never heard of a solid reason for the division between classes > > and modules --my guess is that there is no real reason other than that > > it how it was done originally, and changing it now means 1) a major > > version bump and 2) an overhaul of the implementation. > > I find that distinction pretty clear as I have tried to point out > earlier. Somehow you seem to be using the language quite differently > than me and have run into limitations that do not exist for me. Maybe > we can take the discussion a bit further to find out more about this. Perhaps b/c I have done a lot of meta-programming? Not sure. It would be interesting to know more, I agree. > -- > remember.guy do |as, often| as.you_can - without endhttp://blog.rubybestpractices.com/
First
|
Prev
|
Next
|
Last
Pages: 1 2 3 4 Prev: Doubt regarding Testing with RSpec. Next: General Confusion |