Nested class/module namespace

Hi,

···

In message “Re: Nested class/module namespace” on 03/08/13, ts decoux@moulon.inra.fr writes:

But “::Foo = 42” is not allowed either.

Then was is this case ?

cpath : tCOLON3 cname
{
$$ = NEW_COLON3($2);
}

You are right (as usual). The other bug confused me.
Thank you.

						matz.

Call it personal bias, but whenever an argument is made that a problem
will
only show up with a huge code base, I immediately question the validity of
the so-called “problem”.

That is a good point, and one I have often thought of,
but have been hesitant to express.

C’est la vi.

Ma non, c’est la emacs. :slight_smile:

Hal

···

----- Original Message -----
From: “Nathaniel Talbott” nathaniel@NOSPAMtalbott.ws
To: “ruby-talk ML” ruby-talk@ruby-lang.org
Sent: Wednesday, August 13, 2003 9:49 AM
Subject: Re: Nested class/module namespace

Nathaniel Talbott wrote:

But that common ancestral namespace isn’t arbitrary (and it certainly isn’t
just to save me typing): it’s a logical separation of my code in to related
parts. If, when I’m doing that separation, I deem that two classes should go
in to the same module, it follows that those two pieces of code are closely
related enough to (a) not worry about namespace clashes, and (b) to need to
easily access one another’s namespace.

Fair enough, but what about all the constants of all the preceeding
namespaces you opened? The rationale for polluting your namespace with
these, is what I find arbitrary. A naive example:

module Text
class Parser;end
module SGML
class Parser;end
module HTML
class Parser; end
end
module XML
class Parzer; end # typo
def self.parse_xml
Parser.new # uh oh, this turns into an Text::SGML::Parser
end
end
end
end

Now this typo is easily resolved, once you notice that the program
doesn’t do what you thought it did. (If you notice it.) But why should
one open the entire namespace of Text if you’re just working on, say,
the XML module? Do you really want to deal with all the (potential junk)
that your co-workers fling into the Text namespace?

module Text::SGML::XML
class Parzer; end # typo
def self.parse_xml
Parser.new # uh oh
end
end

This will blow up with a nice NameError, in accordance with the “fail
early” principle for catching bugs. So far so good.

I’ll admit that I still don’t understand what benefit this new behavior
adds, even if I were to separate out my constants in to their own
namespaces and include them. I see doing that as perhaps making this new
behavior bearable, but I don’t see any benefit in this new behavior when
working like that.

Confession time: I probably wouldn’t do that either. I’m too lazy.
However, there is a middle ground. Back to the parser example.

Lets say you want to inherit the HTML and XML parser from the SGML
parser, since entity resolving and a few other things are quite similar.

Now this won’t work:

class Text::SGML::XML::Parser < Parser
end

But this will:

module Text::SGML
class XML::Parser < Parser
end
end

This might seem like a step backwards, as we have more than one body.
However, notice that I “cut off” the namespace at Text::SGML, telling
the reader that I am in need of it, and go on to implement all the
methods of the XML parser that depends on the SGML stuff. I also avoid
including the XML namespace, just in case there is something icky there.

In the next step, I’ll implement some basic methods inside the parser
that doesn’t depend on SGML nor XML:

class Text::SGML::XML::Parser
def boring_bookkeeping
end
end

Here I cut of the namespaces completely, telling the reader:
“Just working on the parser innards, nothing to see here, move along!”

Then there will be methods that need other parts of the XML namespace,
and I’ll do something like:

module Text::SGML::XML
class Parser
def pure_xml_stuff
end
end
end

Now I seem to have been opening the Parser class umpteen times and for
what? For separation of concerns. This could also have been done with
mixins, I seem to recall this having been both discussed and advocated
here earlier:

module Text
module SGML
module XML
module SGMLSpecificStuffForXMLParser
end
module XMLSpecificStuffForXMLParser
def pure_xml_stuff
end
end
module UnspecificStuffForXMLParser
def boring_bookkeeping
end
end
class Parser
include SGMLSpecificStuffForXMLParser
include XMLSpecificStuffForXMLParser
include UnspecificStuffForXMLParser
end
end
end
end

But, AFAIK, you cannot do the namespace part of the separation of
concerns with this approach. If the restrictions of the new M::C style
is intended, the possibility to do this separation is one of the
features it brings.

Call it personal bias, but whenever an argument is made that a problem will
only show up with a huge code base, I immediately question the validity of
the so-called “problem”.

An admirable attitude. :slight_smile:

The truth is, until someone writes that 10kLOC
project, we don’t know. And this is not an issue I’ve heard is a problem for
any practicioners. As a matter of fact, as a practicioner working on a
fairly large Ruby app, I have no trouble with namespace pollution, and I do
get tired of typing “module x; class x; module x; module x; class x” ad
nauseum. I was hoping this new nesting would save me that, but it doesn’t
look like it will. C’est la vi.

I cannot prove it, nor will I attempt, as matz’ decisions will suffice
for me. This is more of a “gut feeling” that comes from my (limited)
experience that was semented securely after being bulldozed by a more
knowledgeable ex-co-worker on the “fail early” issue. But that was in C++.

But I enjoy programming in Ruby and detest C++; why would I want Ruby to
emulate it?

My point here was that the old way emulates C (only #includes), while
the new style emulates C++. You may detest one or the other, but the
namespace issue is one thing I like about C++ over C. Or am I the only
one who gets prefix paranoia reading things like the Ruby source code? :stuck_out_tongue_winking_eye:

I guess, in summary, I like descendent code having easy access
to constants defined in its ancestors’ namespace. I don’t think it’s a
problem having this behavior (even in large projects),

In Ruby this might be true. Personally I’ll feel safer applying some
“lessons learned” from other languages pro-actively.

Perhaps this is where we diverge - I’d prefer to make programming easier now
(by perhaps having more than I need) rather than prematurely fix problems I
think I might have.

Yes, but there is more than premature fixes on my mind. Readability and
expressiveness are my biggest reasons for using Ruby and IMHO this new
feature adds to that.

···


([ Kent Dahl ]/)_ ~ [ Kent Dahl - Kent Dahl ]/~
))_student_/(( _d L b_/ (pre-) Master of Science in Technology )
( __õ|õ// ) )Industrial economics and technological management(
_
/ö____/ (_engineering.discipline=Computer::Technology)

Hi –

Nathaniel has identified a difference between the two cases above when one
tries to access a constant, say X::C (which is not defined in the above
code).

If there is a difference, then there must be a difference in the
“execution context”. To my way of thinking, the only thing that should be
important in an execution context is the value of “self”. If that is not
the case, we need an explanation, and probably a justification. At the
moment, I think the elegance of Ruby is being stretched a bit.

Keep in mind, though, that this isn’t the only such situation in Ruby.
See my previous post with the class_eval example; also, here’s another
situation where ‘self’ can be the same but visibility of constants is
different:

class C
X=1
end

c = C.new

class << c
def a; p self; p X; end
end

def c.b; p self; p X; end

c.a
c.b # error: unknown constant X

(Having several such situations may not make it more attractive to
you, but I just wanted to get this one on the radar.)

David

···

On Wed, 13 Aug 2003, Gavin Sinclair wrote:


David Alan Black
home: dblack@superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

Because it’s not nested at all. It is confusing for me, if
it behaves like nesting. Nested constant scope was
introduced only for one purpose.

module M
class C
C.foo # want to access C here.
end
end

Oops… I guess I’ve been using it incorrectly :-). So it’s not for this
reason?

module M
class C
M::FOO # want to access M here.
end
end

In the cases like

class M::C
end

you don’t need dereferencing like above.

Well, this seems a little confusing to me:

class M::C
def C.m
end
end

=> NameError: uninitialized constant M::C::C

If it had nested scope, this wouldn’t happen, right?

Nathaniel

<:((><

···

Yukihiro Matsumoto [mailto:matz@ruby-lang.org] wrote:

Readability and expressiveness are my biggest reasons for
using Ruby and IMHO this new feature adds to that.

The combination of the two techniques shows a good use of this that I hadn’t
considered (and I may adopt it). However, I still fear that it may hurt the
readability of code for those that don’t understand the distinction between
the two. Perhaps the usefulness of the technique will outweigh the need to
always explain the distinction between the two concepts, but I wish that
distinction were somehow more self-evident.

Anyhow, thanks for the great explanation, Kent.

Nathaniel

<:((><

···

Kent Dahl [mailto:kentda+news@stud.ntnu.no] wrote:

[…] To my way of thinking, the only thing that should be
important in an execution context is the value of “self”. […]

Keep in mind, though, that this isn’t the only such situation in Ruby.
See my previous post with the class_eval example;

I agree with Nathan that what goes on in “eval” and friends is a
different matter, although I’d be happy if it were less surprising.

also, here’s another situation where ‘self’ can be the same but
visibility of constants is different: […]
(Having several such situations may not make it more attractive to
you, but I just wanted to get this one on the radar.)

One more case I’d like an explanation and justification for :slight_smile:

Gavin

···

On Wednesday, August 13, 2003, 9:39:28 PM, dblack wrote:

Hi,

Because it’s not nested at all. It is confusing for me, if
it behaves like nesting. Nested constant scope was
introduced only for one purpose.

module M
class C
C.foo # want to access C here.
end
end

Oops… I guess I’ve been using it incorrectly :-). So it’s not for this
reason?

module M
class C
M::FOO # want to access M here.
end
end

I didn’t express my idea correctly. M too should be covered by nested
constant scope.

Well, this seems a little confusing to me:

class M::C
def C.m
end
end

=> NameError: uninitialized constant M::C::C

If it had nested scope, this wouldn’t happen, right?

Right, but you were going to define M::C, not plain C, were you?

						matz.
···

In message “Re: Nested class/module namespace” on 03/08/13, “Nathaniel Talbott” nathaniel@NOSPAMtalbott.ws writes:

Hi –

···

On Wed, 13 Aug 2003, Gavin Sinclair wrote:

On Wednesday, August 13, 2003, 9:39:28 PM, dblack wrote:

[…] To my way of thinking, the only thing that should be
important in an execution context is the value of “self”. […]

Keep in mind, though, that this isn’t the only such situation in Ruby.
See my previous post with the class_eval example;

I agree with Nathan that what goes on in “eval” and friends is a
different matter, although I’d be happy if it were less surprising.

I always thought that klass.class_eval and obj.instance_eval were
defined precisely as “switching self to {klass|obj}” for the
duration of the string or block, so I’d think they relate closely
to the “self”-centered (so to speak :slight_smile: view.

David


David Alan Black
home: dblack@superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

“Nathaniel Talbott” writes:

Oops… I guess I’ve been using it incorrectly :-). So it’s not for
this reason?

module M
class C
M::FOO # want to access M here.
end
end

I didn’t express my idea correctly. M too should be covered
by nested constant scope.

OK.

Well, this seems a little confusing to me:

class M::C
def C.m
end
end

=> NameError: uninitialized constant M::C::C

If it had nested scope, this wouldn’t happen, right?

Right, but you were going to define M::C, not plain C, were you?

Ah, now I begin to understand (but perhaps not agree). I think of M & C in
‘class M::C’ as separate entities. But it seems that you think of M::C in
‘class M::C’ as a single entity. I guess our intuitive view of what that
construct represents is in conflict. Of cousre, yours always wins…

My biggest concern is not so much for my code, as I can either leave it
as-is or just adapt it to use this new construct as it makes sense. My
concern more lies in the fact that when I saw that ‘class M::C’ now worked,
I thought that it would work exactly like ‘class M; class C end; end’, but
it didn’t. Having two very similar constructs that behave in such different
ways seems surprising, and I fear it will become a frequent puzzlement for
new users. Plus, while I can understand it working that way, I still can’t
give a pragmatic reason other than, “It would confuse matz if it were the
other way.”

Nathaniel

<:((><

···

Yukihiro Matsumoto [mailto:matz@ruby-lang.org] wrote:

Nathaniel Talbott wrote:

My
concern more lies in the fact that when I saw that ‘class M::C’ now worked,
I thought that it would work exactly like ‘class M; class C end; end’, but
it didn’t.

My 2 cents, I thought the same as Nathaniel when I read about class M::C.

Having two very similar constructs that behave in such different
ways seems surprising, and I fear it will become a frequent puzzlement for
new users.

I agree.

···

Chris
http://clabs.org/blogki