www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Once Again, Feature Request: Nested classes in separate scope

reply downs <default_357-line yahoo.de> writes:
Needed: a way to add a nested class to some other class without the
nested class having to be inside the scope of the outer class.

I believe this to be important, because it would let us express the
relation of "belongs-to", forming the symmetric opposite of "has-a".

It came up in the context of user interfaces, where every Window
belongs-to a WindowManager. And yes, I know you can give the Window
class a reference to the WindowManager that controls it, but that feels
unclean. It's like, "The WindowManager has many Windows, and each Window
has the original WindowManager". The relations are screwed up.

Syntax proposal (just an idea):

class Foo {
	int e; this() { e=4; }
}

class Bar in Foo : Object {
	void test() { writefln(e); }
}

void main() {
	auto foo=new Foo;
	auto bar=foo.new Bar; // behaves like a nested class
	bar.test(); // prints 4
}

Whaddya think? :)
 --downs
Nov 05 2007
next sibling parent reply "Vladimir Panteleev" <thecybershadow gmail.com> writes:
On Tue, 06 Nov 2007 04:48:43 +0200, downs <default_357-line yahoo.de> wr=
ote:

 Needed: a way to add a nested class to some other class without the
 nested class having to be inside the scope of the outer class.

 I believe this to be important, because it would let us express the
 relation of "belongs-to", forming the symmetric opposite of "has-a".

 It came up in the context of user interfaces, where every Window
 belongs-to a WindowManager. And yes, I know you can give the Window
 class a reference to the WindowManager that controls it, but that feel=

 unclean. It's like, "The WindowManager has many Windows, and each Wind=

 has the original WindowManager". The relations are screwed up.

 Syntax proposal (just an idea):

 class Foo {
 	int e; this() { e=3D4; }
 }

 class Bar in Foo : Object {
 	void test() { writefln(e); }
 }

 void main() {
 	auto foo=3Dnew Foo;
 	auto bar=3Dfoo.new Bar; // behaves like a nested class
 	bar.test(); // prints 4
 }

 Whaddya think? :)

I think this could be done with the announced "alias foo this;" syntax. (just declare a Foo in Bar and import its namespace) -- = Best regards, Vladimir mailto:thecybershadow gmail.com
Nov 06 2007
next sibling parent reply downs <default_357-line yahoo.de> writes:
Vladimir Panteleev wrote:
 I think this could be done with the announced "alias foo this;" syntax.
 (just declare a Foo in Bar and import its namespace)
 

Yes, but part of the point of the proposal is to avoid using the has-a relationship in situations where it's inadequate or flat out wrong (the Window doesn't own the WindowManager, it's the other way around). Sorry if I'm misunderstanding an OO concept here. --downs
Nov 06 2007
parent reply Regan Heath <regan netmail.co.nz> writes:
downs wrote:
 Vladimir Panteleev wrote:
 I think this could be done with the announced "alias foo this;" syntax.
 (just declare a Foo in Bar and import its namespace)

Yes, but part of the point of the proposal is to avoid using the has-a relationship in situations where it's inadequate or flat out wrong (the Window doesn't own the WindowManager, it's the other way around). Sorry if I'm misunderstanding an OO concept here.

Isn't this the same modal as you often see in UI code, where a window "has a" parent window. The parent window could be said to own the child window but the child still "has a" parent. I'm not sure "has a" necessarily implies ownership, this may be the point which is bothering you? I have a home, but my landlord owns it. I have a job, but I dont own any shares in the compay. ... Regan
Nov 06 2007
parent reply downs <default_357-line yahoo.de> writes:
Regan Heath wrote:
 downs wrote:
 Vladimir Panteleev wrote:
 I think this could be done with the announced "alias foo this;" syntax.
 (just declare a Foo in Bar and import its namespace)

Yes, but part of the point of the proposal is to avoid using the has-a relationship in situations where it's inadequate or flat out wrong (the Window doesn't own the WindowManager, it's the other way around). Sorry if I'm misunderstanding an OO concept here.

Isn't this the same modal as you often see in UI code, where a window "has a" parent window. The parent window could be said to own the child window but the child still "has a" parent. I'm not sure "has a" necessarily implies ownership, this may be the point which is bothering you? I have a home, but my landlord owns it. I have a job, but I dont own any shares in the compay. ... Regan

Fair. Okay, let's drop the ownership debate and reformulate the problem to expressing that class Bar exists in the _context_ of class Foo. I agree that this relationship could be modelled by "class Foo { Bar context; alias context this; " as proposed before, but isn't this exactly what nested classes are supposed to be? Things that only exist in the context of something else? I simply think it would be better to extend this existing feature to allow Foo to be in a separate scope from Bar, instead of using what essentially amounts to a workaround. Nonetheless, thanks for your reply. :) --downs
Nov 06 2007
parent reply Regan Heath <regan netmail.co.nz> writes:
downs wrote:
 Okay, let's drop the ownership debate and reformulate the problem to
 expressing that class Bar exists in the _context_ of class Foo.
 I agree that this relationship could be modelled by "class Foo { Bar
 context; alias context this; " as proposed before, but isn't this
 exactly what nested classes are supposed to be? Things that only exist
 in the context of something else? I simply think it would be better to
 extend this existing feature to allow Foo to be in a separate scope from
 Bar, instead of using what essentially amounts to a workaround.
 Nonetheless, thanks for your reply. :)
  --downs

I'm not sure I understand the "seperate scope" requirement, what's wrong with: --[windowmanager.d]-- module windowmanager; class WindowManager { class Window { private this() { } } Window[] children; Window createWindow() { children ~= new Window(); return children[$-1]; } } --[windowmanager_main.d]-- module windowmanager_main; import windowmanager; void main() { auto m = new WindowManager(); auto w1 = m.createWindow(); auto w2 = m.createWindow(); auto w3 = m.new Window(); //this is not accessible } Is the problem that you need to allow users to derive custom window types from Window? I'm not sure if it's possible to derive from an inner class or what the syntax would be if it was... Regan
Nov 06 2007
parent reply downs <default_357-line yahoo.de> writes:
Regan Heath wrote:
 I'm not sure I understand the "seperate scope" requirement, what's wrong
 with:
 
 --[windowmanager.d]--

 --[windowmanager_main.d]--
 module windowmanager_main;
 import windowmanager;
 
 void main()
 {
     auto m = new WindowManager();
     auto w1 = m.createWindow();
     auto w2 = m.createWindow();
     auto w3 = m.new Window();  //this is not accessible
 }
 

That becomes a problem when you'd like to support many different types of windows and, well, don't want them _all in one file_.
 Is the problem that you need to allow users to derive custom window
 types from Window?

Well, that too :p
 I'm not sure if it's possible to derive from an
 inner class or what the syntax would be if it was...
 

I don't see why it shouldn't be, theoretically. Though I also don't know what the syntax is (or would be). It doesn't seem to be possible at all right now.
 Regan

--downs
Nov 06 2007
parent downs <default_357-line yahoo.de> writes:
David Wilson wrote:
 
 Getting the existing rule relaxed might be easier than getting new
 syntax implemented.
 
 
 David.
 

I'd very happily settle for that, considering that what I really, honestly wanted was the syntax candy of a nested class without the same-fileness this currently entails. --downs
Nov 06 2007
prev sibling parent reply downs <default_357-line yahoo.de> writes:
Vladimir Panteleev wrote:
 On Tue, 06 Nov 2007 12:11:04 +0200, downs <default_357-line yahoo.de> wrote:
 
 Vladimir Panteleev wrote:
 I think this could be done with the announced "alias foo this;" syntax.
 (just declare a Foo in Bar and import its namespace)

Foo in Bar, because that implies a circular ownership relation. --downs

Why do you call it a "circular relation"? Foo's declaration or implementation is still Bar-free. And technically speaking, this is done in the same way - a nested class's "parent" class is nothing but a hidden member.

See the example. I need a Foo that owns many Bar's, but each Bar can also access all methods of Foo - the idea is that the Bars can only exist in the _context_ of a Foo, which is I think what inner classes were supposed to do. Sorry if I'm misunderstanding it. --downs :)
Nov 06 2007
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"downs" <default_357-line yahoo.de> wrote in message 
news:fgpev2$rl6$4 digitalmars.com...
 Vladimir Panteleev wrote:
 On Tue, 06 Nov 2007 12:11:04 +0200, downs <default_357-line yahoo.de> 
 wrote:

 Vladimir Panteleev wrote:
 I think this could be done with the announced "alias foo this;" syntax.
 (just declare a Foo in Bar and import its namespace)

Foo in Bar, because that implies a circular ownership relation. --downs

Why do you call it a "circular relation"? Foo's declaration or implementation is still Bar-free. And technically speaking, this is done in the same way - a nested class's "parent" class is nothing but a hidden member.

See the example. I need a Foo that owns many Bar's, but each Bar can also access all methods of Foo - the idea is that the Bars can only exist in the _context_ of a Foo, which is I think what inner classes were supposed to do. Sorry if I'm misunderstanding it. --downs :)

Given your example, the "ownership" of the Bars by Foo isn't really expressed. In your example, the Bars know about Foo, but Foo does not know about Bar. And it can't, because how can you compile code that would know about all possible objects that use it as context? So the statement that the Foo owns many Bars implies that the Foo must have an aggregate of Bars. That being said, I think you are getting stuck up on the point that if a class contains a member, it owns that member *and* all data pointed to by the member. This isn't necessarily true, and is defined by the programmer, not the compiler. What the class owns is a *reference*. Whether it owns the memory pointed to by the reference or not is entirely up to you. As for your example, I would rewrite it like this: class Foo { int e; this() { e=4; } } class Bar { // Bar does not own foo, it only references it Foo foo; this(Foo f) { foo = f; assert(foo !is null);} void test() { writefln(foo.e); } } void main() { auto foo=new Foo; auto bar=new Bar(foo); // behaves like a nested class bar.test(); // prints 4 } Then derived classes derive from Bar, and all is happy. It isn't possible to create a derivative of Bar without an instance of Foo. the only difference in code usage is the new statement, but it's not that different. Derivatives of Bar must use their foo by referencing the foo member, but I think that might be a Good Thing. -Steve
Nov 06 2007
parent downs <default_357-line yahoo.de> writes:
Steven Schveighoffer wrote:
 
 That being said, I think you are getting stuck up on the point that if a 
 class contains a member, it owns that member *and* all data pointed to by 
 the member.  This isn't necessarily true, and is defined by the programmer, 
 not the compiler.  What the class owns is a *reference*.  Whether it owns 
 the memory pointed to by the reference or not is entirely up to you.
 

To be brutally honest, the ownership stuff was mostly retrojustification; what I was really after was the syntax candy :p Ah well. Can't have everything. --downs
Nov 06 2007
prev sibling next sibling parent "Vladimir Panteleev" <thecybershadow gmail.com> writes:
On Tue, 06 Nov 2007 12:11:04 +0200, downs <default_357-line yahoo.de> wrote:

 Vladimir Panteleev wrote:
 I think this could be done with the announced "alias foo this;" syntax.
 (just declare a Foo in Bar and import its namespace)

Yes, but part of the point of the whole thing was to avoid declaring a Foo in Bar, because that implies a circular ownership relation. --downs

Why do you call it a "circular relation"? Foo's declaration or implementation is still Bar-free. And technically speaking, this is done in the same way - a nested class's "parent" class is nothing but a hidden member. -- Best regards, Vladimir mailto:thecybershadow gmail.com
Nov 06 2007
prev sibling parent "David Wilson" <dw botanicus.net> writes:
On 11/6/07, downs <default_357-line yahoo.de> wrote:
 That becomes a problem when you'd like to support many different types
 of windows and, well, don't want them _all in one file_.

 Is the problem that you need to allow users to derive custom window
 types from Window?

Well, that too :p
 I'm not sure if it's possible to derive from an
 inner class or what the syntax would be if it was...


At present this throws the error: a.d(11): class a.Inner2 super class Inner is nested within Outer, not a If this was made legal, would you still have a problem? Conceptually, it seems fine to me that a subclass of an inner class should inherit its access to the outer class. Certainly if this wasn't intended, a class designer could specify "final" on the original inner class. class WindowManager { class Window {} } class ToolWindow : WindowManager.Window { } (BTW: I thought window managers governed windows, not owned them? Also, a button is a kind of window :)
 I don't see why it shouldn't be, theoretically. Though I also don't know
 what the syntax is (or would be). It doesn't seem to be possible at all
 right now.

Getting the existing rule relaxed might be easier than getting new syntax implemented. David.
 Regan

--downs

Nov 06 2007