From: "boris_stitnicky (Boris Stitnicky)" Date: 2012-05-09T20:49:33+09:00 Subject: [ruby-core:44966] [ruby-trunk - Feature #5632] Attempt to open included class shades it instead. Issue #5632 has been updated by boris_stitnicky (Boris Stitnicky). Back to the original issue, having module A; class X; def hello; puts 'hello' end end end module B; include A end then using "class X" statement inside B module does not behave as you say, "X = defined?(X) ? X : Class.new", but as "X = self.constants(false).include?(:X) ? X : Class.new", which is undocumented and yet has to be memorized - a surefire recipe for unwanted language exploration session in irb. Let me say in detail what problem I was solving back then. It was a Petri net with place and transition classes in its namespace: module Petri class Place # define Petri net place (marking property etc.) end class Transition # defines Petri net transition (firing, enabled-disabled etc.) end class Net # a collection of connected Places and Transitions end end Having defined this, I said to myself: Now I'll make a special kind of a Petri net, that can do some addtional tricks: module ChemicalPetri include Petri NA = 6.022e23 # teach it Avogadro's number class Transition # teach transitions Arrhenius equation end end In fact, I expected to work on a deep subclass of 'Petri' module. But "class Transition" gave me a brand new class silently, and there I had to forget about chemical equations and hit irb. After 1 day, I figured out I have to write: class Transition < Transition # teach transitions Arrhenius equation end Yet, "class Transition" was the first thing that jumped to my mind to get what I wanted. From retrospective, there are 3 logically justified behaviors for "class Transition" statement in this situation: 1. Opening the ancestor's class, explicitly: class Petri::Transition; # do modifications end This is most "logical", because Transition constant is already there, but opening ancestor's assets in offspring modules is hardly a good habit. 2. Creation of a brand new class, explicitly: Transition = Class.new; class Transition; # do modifications end Less logical, but justifiable behavior, encouraging bad habits less. 3. Operation on a subclass, explicitly: class Transition < Transition; # do modifications end Perhaps least logical, but I suspect that most frequently needed behavior. I lean towards concluding, that in this situation "class X" statement is always intuitively ambiguous and perhaps should always warn, explaining what exactly is it doing, no matter which of the 3 behaviors is chosen in Ruby implementation. User should explicitly either ask for the parent's class (class A::X), or explicitly create a new class (X = Class.new), or explicitly subclass parent's X (class X < X). Since "class X < X" requires good understanding of what's going on behind the scenes, perhaps there should be a new statement(s) controlling this kind of subclassing behavior, something like "subclass_also X", "deep_subclass_also X". (These are really not good suggestions) In sum, I'm trying to convey my feelings that once Ruby is used as a math language to make slightly more complex object models, deep subclassing might be an everyday need and should be provided for in the language itself, rather than expecting users to write their own gems for this. ---------------------------------------- Feature #5632: Attempt to open included class shades it instead. https://bugs.ruby-lang.org/issues/5632#change-26552 Author: boris_stitnicky (Boris Stitnicky) Status: Assigned Priority: Normal Assignee: mame (Yusuke Endoh) Category: Target version: 3.0 # Hello everyone. I'm not a very advanced ruby user, and I # would like to provide and outsider report on certain ruby # behavior that might surprise newbies. module A class X def hello; puts 'hello' end end end module B include A end B::X.new.hello => hello # As expected. # But when I tried to add new functionality to X, ... module B class X def goodbye; puts 'goodbye' end end end B::X.new.hello => NoMethodError # I was surprised, that my .hello method disappeared, # when all I was trying to do, was to improve X in B. # I actually somehow expected to work on a subclass # of X, like this: module C include A class X < X def goodbye; puts 'goodbye' end end end # My suggestions are: # 1. I consider 'class X < X' syntax a little bit # mysterious. How about making this a default # behavior for 'class X' statements? # 2. If the above is not considered beneficial, I # would welcome if 'class X' statement warned # when shadowing an existing name. People might # often assume that they are opening an existing # class, rather than getting a brand new one # shadowing the previous one. If people really # want a brand new shadowing class without warning # they could use explicit 'X = Class.new'. -- http://bugs.ruby-lang.org/