www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - restructuring name hiding around the notion of hijacking

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Today's D has a very strong, principled notion of hijacking: for any 
given function call, if the call candidates are found in different 
modules, the call is invalid. I think that works great.

Lately I've been thinking of using the same notion of hijacking as a 
replacement for symbol hiding in inheritance hierarchies. Right now, if 
a derived class defines a symbol, that symbol simply hides whatever 
homonym symbol (unless it overrides it). There are some warnings about 
hiding sometimes, but it's all kind of fuzzy.

How about just using hijacking? The basic idea is that a use of a symbol 
in a class should not hijack a homonym symbol defined in a different module.


What do you think?

Andrei
Sep 30 2009
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2009-09-30 22:01:54 -0400, Andrei Alexandrescu 
<SeeWebsiteForEmail erdani.org> said:

 Today's D has a very strong, principled notion of hijacking: for any 
 given function call, if the call candidates are found in different 
 modules, the call is invalid. I think that works great.
 
 Lately I've been thinking of using the same notion of hijacking as a 
 replacement for symbol hiding in inheritance hierarchies. Right now, if 
 a derived class defines a symbol, that symbol simply hides whatever 
 homonym symbol (unless it overrides it). There are some warnings about 
 hiding sometimes, but it's all kind of fuzzy.
 
 How about just using hijacking? The basic idea is that a use of a 
 symbol in a class should not hijack a homonym symbol defined in a 
 different module.
 
 
 What do you think?
I think it's a good idea, but there should be a way to *override* static functions. In fact I sometime wonder if it'd be a good idea to disallow hijacking of global variables with local variables inside functions too, but that's more triky to do. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Sep 30 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Michel Fortin wrote:
 On 2009-09-30 22:01:54 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> said:
 
 Today's D has a very strong, principled notion of hijacking: for any 
 given function call, if the call candidates are found in different 
 modules, the call is invalid. I think that works great.

 Lately I've been thinking of using the same notion of hijacking as a 
 replacement for symbol hiding in inheritance hierarchies. Right now, 
 if a derived class defines a symbol, that symbol simply hides whatever 
 homonym symbol (unless it overrides it). There are some warnings about 
 hiding sometimes, but it's all kind of fuzzy.

 How about just using hijacking? The basic idea is that a use of a 
 symbol in a class should not hijack a homonym symbol defined in a 
 different module.


 What do you think?
I think it's a good idea, but there should be a way to *override* static functions.
That has the same risks. The problem right now is that in order to use a class, you must absorb the definition of that class and that of each superclass of it, all the way up to Object. With hijacking thwarted, you can specify stuff in the base class that you can be sure will continue to work the same in derived classes. I believe this makes using classes quite a lot easier and safer.
 In fact I sometime wonder if it'd be a good idea to disallow hijacking 
 of global variables with local variables inside functions too, but 
 that's more triky to do.
I explain in TDPL that that's not a good idea. Let me paste the text: ============= A symbol defined inside a scope hides a homonym symbol hanging out outside all scopes: \begin{D} uint widgetCount; ... void main() { writeln(widgetCount); // writes the global symbol auto widgetCount = getWidgetCount(); writeln(widgetCount); // writes the local symbol } \end{D} The first call to writeln prints the global widgetCount symbol and the second accesses the locally-defined widgetCount . Should there be a need for accessing the global symbol after it has been masked, prefixing it with a ``.''---as in writeln(.widgetCount) ---will do, as first mentioned on page~\ref{pg:dotSyntaxForScoping}. However, it is illegal to define a symbol that would mask a symbol in an enclosing compound statement: \begin{D} void main() { auto widgetCount = getWidgetCount(); // let's now open a nested block { auto widgetCount = getWidgetCount(); // error! } } \end{D} As long as masking does not occur, it's legal to reuse the same symbol in different compound statements: \begin{D} void main() { { auto i = 0; ... } { auto i = "eye"; // fine ... } double i = 3.14; // fine too } \end{D} The rationale of this setup is simple. Allowing global symbol masking is necessary for writing good, modular code that's assembled out of separately-compiled parts; you don't want the addition of a global variable to suddenly render various innocent bystanders uncompilable. On the other hand, enclosing-scope masking is useless as a modularity device (as there's never the case a compound statement spans multiple modules in~\dee) and most often indicates either an oversight aspiring to become a bug, or a cancerous function that's grown out of control. Andrei
Oct 01 2009
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2009-10-01 12:29:39 -0400, Andrei Alexandrescu 
<SeeWebsiteForEmail erdani.org> said:

 I think it's a good idea, but there should be a way to *override* 
 static functions.
That has the same risks. The problem right now is that in order to use a class, you must absorb the definition of that class and that of each superclass of it, all the way up to Object. With hijacking thwarted, you can specify stuff in the base class that you can be sure will continue to work the same in derived classes. I believe this makes using classes quite a lot easier and safer.
But it breaks one pattern of mine. In the D/Objective-C bridge I have a few static functions and variables that must be redefined for each subclass defining an Objective-C interface. With your proposal I'd have to give them a different name for each subclass. For instance, the "objcClass" function in: NSObject.objcClass will give you a pointer to the Objective-C class NSObject, while in: NSString.objcClass it will give you a pointer to the Objective-C class NSString, because objcClass has been reimplemented in the D version of the NSString class even though it derives from NSObject which has its own. If you can't override a static function, how do you implement this? I'd suggest that a static function could be made final which would remove the possibility of redefining it in a subclass. But in abscence of "final", you should still be able to "override" a static function in a subclass (perhaps the override keyword should be required). -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Oct 01 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Michel Fortin wrote:
 On 2009-10-01 12:29:39 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> said:
 
 I think it's a good idea, but there should be a way to *override* 
 static functions.
That has the same risks. The problem right now is that in order to use a class, you must absorb the definition of that class and that of each superclass of it, all the way up to Object. With hijacking thwarted, you can specify stuff in the base class that you can be sure will continue to work the same in derived classes. I believe this makes using classes quite a lot easier and safer.
But it breaks one pattern of mine. In the D/Objective-C bridge I have a few static functions and variables that must be redefined for each subclass defining an Objective-C interface.
I'd say that's a questionable practice (but then I don't know any more details). Andrei
Oct 01 2009
next sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2009-10-01 23:52:28 -0400, Andrei Alexandrescu 
<SeeWebsiteForEmail erdani.org> said:

 Michel Fortin wrote:
 On 2009-10-01 12:29:39 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> said:
 
 I think it's a good idea, but there should be a way to *override* 
 static functions.
That has the same risks. The problem right now is that in order to use a class, you must absorb the definition of that class and that of each superclass of it, all the way up to Object. With hijacking thwarted, you can specify stuff in the base class that you can be sure will continue to work the same in derived classes. I believe this makes using classes quite a lot easier and safer.
But it breaks one pattern of mine. In the D/Objective-C bridge I have a few static functions and variables that must be redefined for each subclass defining an Objective-C interface.
I'd say that's a questionable practice (but then I don't know any more details).
Well, essencially you can have a D class that act as a wrapper to an Objective-C class, or you can also have the reverse: a D class exposing itself as an Objective-C class. In all cases, the type hiearchy is preserved, so if you have NSString as a subclass of NSObject on the Objective-C side, you'll have the same on the D side. The NSString wrapper must have different static members than NSObject, binding it to a different Objective-C class so it can call the right methods on it (and so it allocates the right function), but those members have the same role (just a different value per class) and must be accessible for any class declaring an Objective-C interface (so the bridge can swap between the Objective-C and D value when calling a function on the other side). So it turns out that I'm implementing a mechanism somewhat alike classinfo for storing Objective-C related class-level data, and for that to work I need to reimplement any function accessing this data in each subclass that binds to a different Objective-C class. If I could attach my class-related data to the ClassInfo of a specific class (so it could be retrieved at runtime) and if static functions had access to the classinfo of the class they're called from (practically making them member function of the corresponding ClassInfo) the situation might be different though, and much less code would be needed. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Oct 02 2009
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Michel Fortin wrote:
 If I could attach my class-related data to the ClassInfo of a specific 
 class (so it could be retrieved at runtime) and if static functions had 
 access to the classinfo of the class they're called from (practically 
 making them member function of the corresponding ClassInfo) the 
 situation might be different though, and much less code would be needed.
It has crossed my mind more than once to put an Object userdata[string] member somewhere in TypeInfo or Classinfo (in fact the two will be soon merged). That way client code would be able to plant their own arbitrary data on a per-class basis. Andrei
Oct 02 2009
prev sibling parent reply Max Samukha <spambox d-coding.com> writes:
On Thu, 01 Oct 2009 22:52:28 -0500, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:

Michel Fortin wrote:
 On 2009-10-01 12:29:39 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> said:
 
 I think it's a good idea, but there should be a way to *override* 
 static functions.
That has the same risks. The problem right now is that in order to use a class, you must absorb the definition of that class and that of each superclass of it, all the way up to Object. With hijacking thwarted, you can specify stuff in the base class that you can be sure will continue to work the same in derived classes. I believe this makes using classes quite a lot easier and safer.
But it breaks one pattern of mine. In the D/Objective-C bridge I have a few static functions and variables that must be redefined for each subclass defining an Objective-C interface.
I'd say that's a questionable practice (but then I don't know any more details). Andrei
It may be questionable but it is used quite often. The technique can be illustrated by altering your example of automatic code injection: class Counted { mixin(Derived) { // Insert here stuff that must be "pasted" for each subclass // of Counted (including Counted itself). // Use "Derived" as the name of the current subtype of Counter private static uint _counter; uint staticCounter() { return _counter; } static if (is(Counted == Derived)) uint getCounter() { return staticCounter; } else override uint getCounter() { return staticCounter; } } ... } The counter variable is now incapsulated. If the counter is, for example, an object that should be lazily created, then you cannot get away without the static function any more. BTW, your example shows that 'override' being optional may actually be a good idea and in this particular case allows to avoid the static check and code duplication
Oct 02 2009
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2009-10-02 08:29:09 -0400, Max Samukha <spambox d-coding.com> said:

 class Counted {
      mixin(Derived)
      {
          // Insert here stuff that must be "pasted" for each subclass
          // of Counted (including Counted itself).
          // Use "Derived" as the name of the current subtype of
 Counter
          private static uint _counter;
          uint staticCounter() { return _counter; }
 
          static if (is(Counted == Derived))
              uint getCounter() { return staticCounter; }
          else
              override uint getCounter() { return staticCounter; }
      }
      ...
 }
 
 The counter variable is now incapsulated.
Hum, I think you forgot to make staticCounter static, as in: static uint staticCounter() { return _counter; } -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Oct 02 2009
parent Max Samukha <spambox d-coding.com> writes:
On Fri, 2 Oct 2009 08:54:49 -0400, Michel Fortin
<michel.fortin michelf.com> wrote:

On 2009-10-02 08:29:09 -0400, Max Samukha <spambox d-coding.com> said:

 class Counted {
      mixin(Derived)
      {
          // Insert here stuff that must be "pasted" for each subclass
          // of Counted (including Counted itself).
          // Use "Derived" as the name of the current subtype of
 Counter
          private static uint _counter;
          uint staticCounter() { return _counter; }
 
          static if (is(Counted == Derived))
              uint getCounter() { return staticCounter; }
          else
              override uint getCounter() { return staticCounter; }
      }
      ...
 }
 
 The counter variable is now incapsulated.
Hum, I think you forgot to make staticCounter static, as in: static uint staticCounter() { return _counter; }
Yes, I do it all the time. Thanks!
Oct 02 2009