www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Access modifier for extensions

reply "Boyd" <gaboonviper gmx.net> writes:
Hi there,

I have an idea for an access modifier that I wanted to throw out 
there, in the hopes that it will resonate with someone.

Basically I've found that in practice, when building a module, I 
always need to keep two kind of users in mind. Those who want to 
use it, and those who want to extend it.

For extending, there is the 'protected' attribute, but it's 
specific for class overriding only. Very often, extensions are 
not merely limited to derived classes. What I find myself wanting 
is more like the 'package' attribute, except it needs to work 
outside of the package as well.

So right now, the only viable choice is making all functionality 
for users and extenders 'public'.

So let's imagine the 'extendable' attribute for a moment. Here's 
what I would like it to do:

Let's say someone built a GUI library, and without access to the 
library, I want to add support for another platform.

------
module buttoncontrol

public class ButtonControl
{
     public Event pressEvent;
     extendable void click(Point position);
}
------

Now, you don't want a user to be able to generate a click. But 
someone whose purpose it is to extend the library, should be able 
to do it.

extender
------
import extendable buttoncontrol;

void click(Point position)
{
     auto button = findButtonUnderCursor(position);
     button.click(position);
}
------

user
------
import buttoncontrol;

void createUi()
{
     auto button = new ButtonControl();
     button.click(Point(10,10)); // ERROR, no access to click 
function
}
------

So, that's the general idea. I haven't thought very hard about 
the syntax part, so ignore that. Let me know what you think. Is 
it useful? feasible? worth the effort?

Cheers,
Boyd
Jan 16 2014
next sibling parent "Rikki Cattermole" <alphaglosined gmail.com> writes:
Something that I have been doing quite heavily recently is a 
registration approach. I.e. in a module constructor (shared) you 
call a function giving it a class type that most likely inherits 
another class/interface. From there code is generated/executed at 
compile time for that function call. Very useful for registering 
e.g. routes.

However an issue with this approach I've seen while porting glfw 
to D is that sure this works but how can I do e.g. new 
Window(640, 480, "My title here"); when it needs to hook into the 
registration system to grab the real window implementation that 
you have set to use by default (or overridable by adding the name 
to the call).

This is essentially the factory pattern.

Now something that allows me to say the function allocates this 
class but not its children would really be useful. As I could get 
it to go to the registration system and grab the allocated result 
from there.

Based upon this what you're thinking of is essentially 
add/override methods on a class which is really isn't good D 
code. Too much like c++ and ugly.
Perhaps what we could have to accomplish your part while making 
it obvious is a sorta like how c#'s event support is.

Now we could implement this as a mixin template. I.e. it could 
generate:

static void method_addHandler(T)(del)
in {
  ... // check if del is a delegate with matching definition
  // first arg must be previous value
} body {
  ... // add arg to list
}

T method(ARGS) {
  ... // run handlers while passing previous value
}

With this you could also pass e.g. a lambda or some delegate to 
the mixin to say this is my default output of this method. Can 
also do some checks for default return types ext.

I don't know how to handle access modifiers for this. However 
pretty much everything else is doable without changing the front 
end specially for this use case. Will depend though on the 
allocation api and if it'll allow you to return a child class 
type. I don't see why it wouldn't though.

But this is just my thoughts on the matter. I'm sure somebody 
will destroy it!
Jan 16 2014
prev sibling next sibling parent "Gary Willoughby" <dev nomad.so> writes:
On Thursday, 16 January 2014 at 09:28:18 UTC, Boyd wrote:
 Hi there,

 I have an idea for an access modifier that I wanted to throw 
 out there, in the hopes that it will resonate with someone.

 Basically I've found that in practice, when building a module, 
 I always need to keep two kind of users in mind. Those who want 
 to use it, and those who want to extend it.

 For extending, there is the 'protected' attribute, but it's 
 specific for class overriding only. Very often, extensions are 
 not merely limited to derived classes. What I find myself 
 wanting is more like the 'package' attribute, except it needs 
 to work outside of the package as well.

 So right now, the only viable choice is making all 
 functionality for users and extenders 'public'.

 So let's imagine the 'extendable' attribute for a moment. 
 Here's what I would like it to do:

 Let's say someone built a GUI library, and without access to 
 the library, I want to add support for another platform.

 ------
 module buttoncontrol

 public class ButtonControl
 {
     public Event pressEvent;
     extendable void click(Point position);
 }
 ------

 Now, you don't want a user to be able to generate a click. But 
 someone whose purpose it is to extend the library, should be 
 able to do it.

 extender
 ------
 import extendable buttoncontrol;

 void click(Point position)
 {
     auto button = findButtonUnderCursor(position);
     button.click(position);
 }
 ------

 user
 ------
 import buttoncontrol;

 void createUi()
 {
     auto button = new ButtonControl();
     button.click(Point(10,10)); // ERROR, no access to click 
 function
 }
 ------

 So, that's the general idea. I haven't thought very hard about 
 the syntax part, so ignore that. Let me know what you think. Is 
 it useful? feasible? worth the effort?

 Cheers,
 Boyd

This can be achieved with traditional OOP design patterns and i have to agree with the above poster this is too much like C++ hacking and looks horrible.
Jan 16 2014
prev sibling next sibling parent "Boyd" <gaboonviper gmx.net> writes:
On Thursday, 16 January 2014 at 11:18:07 UTC, Gary Willoughby 
wrote:
 This can be achieved with traditional OOP design patterns and i 
 have to agree with the above poster this is too much like C++ 
 hacking and looks horrible.

I think I misrepresented my case by mucking up the example. I don't care about any individual use case. Ignore it. What I'm looking for is a way to represent this: class AThing { public void DoSomethingAnyoneCanDo(); public_ish void DoSomethingThatRequiresExpertise(); } The first method in the class, is something you want to expose to any user. The second is meant for the guy who wants access to the more advanced stuff, where you have to know what you are doing. Inheriting from the class is not an option, or at least unwanted. Basically I need the 'package' attribute, but for modules outside the package, even modules I have no control over, or know anything about. Am I really the only who ever found the need for such a thing? Because I see situations where this could be pretty damn handy, all the time.
Jan 16 2014
prev sibling next sibling parent "Gary Willoughby" <dev nomad.so> writes:
On Thursday, 16 January 2014 at 17:45:40 UTC, Boyd wrote:
 On Thursday, 16 January 2014 at 11:18:07 UTC, Gary Willoughby 
 wrote:
 This can be achieved with traditional OOP design patterns and 
 i have to agree with the above poster this is too much like 
 C++ hacking and looks horrible.

I think I misrepresented my case by mucking up the example. I don't care about any individual use case. Ignore it. What I'm looking for is a way to represent this: class AThing { public void DoSomethingAnyoneCanDo(); public_ish void DoSomethingThatRequiresExpertise(); } The first method in the class, is something you want to expose to any user. The second is meant for the guy who wants access to the more advanced stuff, where you have to know what you are doing. Inheriting from the class is not an option, or at least unwanted. Basically I need the 'package' attribute, but for modules outside the package, even modules I have no control over, or know anything about. Am I really the only who ever found the need for such a thing? Because I see situations where this could be pretty damn handy, all the time.

By your example i'm getting the impression you want a standard interface for users of your class but want advanced behaviour of the same interface for other users while avoiding inheritance? If this is the case, your answer is delegation[1]. class A { private Delegate delegateObject; public void setDelegate(Delegate delegateObject) { this.delegateObject = delegateObject; } public value doSomethingAnyoneCanDo() { if (this.delegateObject) { return this.delegateObject.doSomethingThatRequiresExpertise(); } // Do something anyone can do. } } [1]: http://en.wikipedia.org/wiki/Delegation_pattern P.S. Don't confuse this with the D 'delegate' type.
Jan 16 2014
prev sibling next sibling parent "Boyd" <gaboonviper gmx.net> writes:
On Thursday, 16 January 2014 at 18:23:16 UTC, Gary Willoughby 
wrote:
 On Thursday, 16 January 2014 at 17:45:40 UTC, Boyd wrote:
 I think I misrepresented my case by mucking up the example. I 
 don't care about any individual use case. Ignore it. What I'm 
 looking for is a way to represent this:

 class AThing
 {
    public void DoSomethingAnyoneCanDo();
    public_ish void DoSomethingThatRequiresExpertise();
 }

 The first method in the class, is something you want to expose 
 to any user. The second is meant for the guy who wants access 
 to the more advanced stuff, where you have to know what you 
 are doing. Inheriting from the class is not an option, or at 
 least unwanted.

 Basically I need the 'package' attribute, but for modules 
 outside the package, even modules I have no control over, or 
 know anything about.

 Am I really the only who ever found the need for such a thing? 
 Because I see situations where this could be pretty damn 
 handy, all the time.

By your example i'm getting the impression you want a standard interface for users of your class but want advanced behaviour of the same interface for other users while avoiding inheritance? If this is the case, your answer is delegation[1]. class A { private Delegate delegateObject; public void setDelegate(Delegate delegateObject) { this.delegateObject = delegateObject; } public value doSomethingAnyoneCanDo() { if (this.delegateObject) { return this.delegateObject.doSomethingThatRequiresExpertise(); } // Do something anyone can do. } } [1]: http://en.wikipedia.org/wiki/Delegation_pattern P.S. Don't confuse this with the D 'delegate' type.

No, I'm talking about two different functions with different functionalities. The desktop PC is probably a good analogy. The normal user just turns it on and off. Others might open up the case, change the CPU multiplier, add some memory, maybe turn down the fans so they make less noise. class DesktopPC { public void turnOn(); public void turnOff(); expertMode void setCpuMultiplier(int value); }
Jan 16 2014
prev sibling next sibling parent "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Thursday, 16 January 2014 at 09:28:18 UTC, Boyd wrote:
 For extending, there is the 'protected' attribute, but it's 
 specific for class overriding only. Very often, extensions are 
 not merely limited to derived classes. What I find myself 
 wanting is more like the 'package' attribute, except it needs 
 to work outside of the package as well.

You can make class members accessible to a module by reintroducing them in a derived class using an alias. It even works for static and final methods, and field variables. Example: /////////////////////////////////////////////////////////// module library; class Button { protected void click() {} } /////////////////////////////////////////////////////////// module user; import library; void main() { auto button = new Button(); button.click(); // NG } /////////////////////////////////////////////////////////// module extender; import library; class ExtendedButton : Button { // reintroduce protected member as private to this scope private alias Button.click click; } void main() { auto button = new ExtendedButton(); button.click(); // OK } /////////////////////////////////////////////////////////// HTH.
Jan 16 2014
prev sibling parent "Boyd" <gaboonviper gmx.net> writes:
On Thursday, 16 January 2014 at 18:50:26 UTC, Vladimir Panteleev 
wrote:
 On Thursday, 16 January 2014 at 09:28:18 UTC, Boyd wrote:
 For extending, there is the 'protected' attribute, but it's 
 specific for class overriding only. Very often, extensions are 
 not merely limited to derived classes. What I find myself 
 wanting is more like the 'package' attribute, except it needs 
 to work outside of the package as well.

You can make class members accessible to a module by reintroducing them in a derived class using an alias. It even works for static and final methods, and field variables. Example: /////////////////////////////////////////////////////////// module library; class Button { protected void click() {} } /////////////////////////////////////////////////////////// module user; import library; void main() { auto button = new Button(); button.click(); // NG } /////////////////////////////////////////////////////////// module extender; import library; class ExtendedButton : Button { // reintroduce protected member as private to this scope private alias Button.click click; } void main() { auto button = new ExtendedButton(); button.click(); // OK } /////////////////////////////////////////////////////////// HTH.

While it would work in this case, extending the class is not always an option. Plus it seems quite a big and dirty-looking work around for something that should, in my opinion, be quite simple.
Jan 16 2014