www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - OT: Programming Expansibility

reply Jeffery <Jeffery gmail.com> writes:
It kinda dawned on me that there are two major issues with 
programming:
[Internal code = code created by you/me, User code = code created 
by the user to use our code]

1. Extensibility -

When we write code that will be used(by anyone), essentially it 
is wrapped in another layer of indirection. Each time, each 
"flaw" in the reduces the code flexibility and hence 
extensibility suffers.


2. Dependency Levels -

When we create explicit dependencies of dependencies then the 
flaws become magnified. Since the number of "cross-references" is 
generally exponential. (e.g., old school programming basically 
ignores this factor, which is why it is so hard to maintain).

---------

Example: [Assume no null objects and everything public]

Internal:

class Mine
{
    void foo();
}

User:

class Yours
{
     Mine m;
}

----

Yours y;
y.m.foo();  <-- a 2-level dependency

This is hard to maintain!!!!

Since no changes can be made to the interface of Mine.(we could 
obviously make it much worse by having parameters for foo)

class Yours
{
    Mine m;
    void foo() { m.foo; }
}

y.foo(); <-- much better, since if m changes, it won't break 
anything that depends on Yours since foo will exist. Simply 
updating Yours.foo is enough to work with m is enough. This is 
basic oop of course.


---

But if one logically extends this, would we not want remove 
multiple indirections? This creates a sort of chain of 
dependencies rather than a complex structure of them. (sort of 
like chain mail vs a chain)

Again, this is basic oop.

But going further:

What would we not want to make all indrections only one level 
deep?

e.g.,

No, Hers.His.Theirs.Ours.Yours.Mine.foo();

Each level creates a serious dependency issue if Ours.foo is 
changed. We have to deal with 6 levels of dependencies scattered 
across an untold number of real dependencies. It is an 
exponential increase in complexity!
(every line of that uses some form of indirection to get at foo 
creates a new flaw)


But there seems to be a solution!!

Never allow one to have more than one level of indirection.

E.g., Hers.foo(); (foo is not mine foo but a wrapped version).


Having more than one level reduces extensibility, makes code 
harder to maintain, etc.

But essentially this requires wrapping all the methods at each 
level. This is linear rather than exponentially but still a lot 
of typing. (but ultimately not more than the typing that one has 
to do to fix the flaws that each level of indirection creates)


What I'm thinking is that it would be better to expose wrappings 
for every member of every field and all the members of every 
field object. (this, of course, is exponential too)

This allows us to always use only one level of indrection(e.g., 
Yours.foo rather than Yours.Mine.foo).

D can already do this using OpDispatch!!! (this allows us to do 
it programmatically)

IDE's could do this also by simply providing default wrappers to 
for all cases. (sort of like implement interface in Visual 
Studio, but done automatically... or reflection could be used, 
etc)


The problem, of course, is name collisions and name 
pollution(sort of related).

Is there a solution?

Possibly a new symbol is needed to prevent both name collisions 
and allow for such capabilities? e.g., Yours.Mine`foo. In this 
case, Mine`foo is not Mine.foo in the standard sense, but simply 
calling a member of Yours called Mine`foo.

But there is a better way?

If compilers only allows one level of indirection, then 
Yours.Mine.foo makes sense. The first . is standard member 
access. The Mine.foo part is not another level of indirection of 
simply name disambiguation. (it's like Mine`foo)

In this case though, we'd have effectively two levels of 
indirection and not really solved the problem.

But if we can "alias" Mine inside Yours then we have made some 
headway.

e.g.,

class Yours
{
    alias Mine = NotMine;
    void Mine.foo();    // actually calls NotMine.foo
}

Of course D can already do this too!!

Ultimately we are trying to gain the "dependenciness" of a flat 
hierarchy with the separation that nesting provides.

D seems to provide some great mechanisms to allow one to treat 
things as a semi-flat hierarchy but still have the indirection 
capabilities.

Is there more that can be done? It seems that most languages give 
the programmer full freedom to die in quicksand(you can choose as 
little and as much levels of indirection you want to use in any 
place). D allows some ability for the programmer to create a 
systematic way to deal with the problem but still requires the 
programmer to deal with this issue(e.g., we would still have to 
implement OpDispatch and use aliases, etc)

Is there a more consistent approach? A language that is naturally 
designed around preventing dependency explosion yet not 
compensating with requiring a large amount of boilerplate code(in 
which the compiler would actually take care of)?


Basically most languages have virtually no consistent view on the 
issue. D has the tools but they feel more like "add ons" after 
the fact rather than a natural part of the language that is 
designed to help the programmer with such issues.
Oct 21 2015
parent reply Idan Arye <GenericNPC gmail.com> writes:
On Wednesday, 21 October 2015 at 14:17:15 UTC, Jeffery wrote:
 *snip*
I think you are looking at it wrong. Object composition should either be public or private. If it's public, it should be perfectly fine for user code to be fully aware that a `Yours` has a `Mine` named `m`. Or, if we look at a more concrete example: class Name { public string first; public string last; } class Person { public Name name; } I see no harm done in user code calling `person.name.first`, because a `Name` is just a type, just like `string` or `int` - the only difference is that `Name` is user defined. In these cases, I find it ridiculous for a library wrap all the functionality of another library instead of just passing it on. A real life example: Java used to have a really crappy datetime module in it's standard library(Java 8 got an improved datetime module). Someone made a third party datetime library called "Joda Time"(http://www.joda.org/joda-time/), which is considerably better, and many libraries require it. If these libraries followed your rule, they would have to wrap the functions of library, so instead of `someEvent.getTime().getMonth()` they'll have to implement in the `Event` class a `getMonth` method so you could `someEvent.getMonth()`, and the same with all the methods in the rich interface provided by Joda Time. Does that seem reasonable to you? Now, while it's true that the fact that it comes from a third party library may make it more prune to bugs and breaking changes(which I assume are what you mean by "flaws"), these flaws don't really grow exponentially on the chain of indirection. The flaw *potential* might grow exponentially, since the number of possible chains can grow exponentially, but the number of chains actually used if far smaller! By insisting on a single level of indirection, you are actually making things worse: - Since you need to wrap methods for the user code, you needlessly materialize many possible chains into existence, triggering many possible flaws that no one would have to deal with otherwise. - You make it impossible for users to deal with flaws in libraries you depend on. Even if your library should not be affected by that flaw, your users now depend on you to deal with it even if they should have been able to deal with it themselves. So, a public composition is a public dependency and should not be hidden by the Law of Demeter. How about private composition? If a composition is private, you should not be able to access it via `y.m.foo()` - but not because it's too long an indirection chain, but because it's a private member field! The outside world should not care that `Yours` has a `Mine` named `m` - this composition is supposed to be encapsulated. The thing is - just like automatically defining getters and setters for all member fields breaks encapsulation, so does automatically defining proxy wrappers for all the methods of the member field. It might solve other problems(like lifetime and ownership problems), but it will not achieve the basic purposes of encapsulation, like allowing you to change the internal fields without affecting users of the outer object. If you change a method of the internal object, the methods of the outer object will also change. So, this type of wrapping is not good here either. `Yours` shouldn't just have a method for invoking `m`'s `foo()`. If `Yours` has a functionality that requires invoking `m.foo()`, the implementation of that functionality can call `m.foo()` directly. Otherwise, there is no reason for any method of `Yours` to call `m.foo()` - certainly not as automatic, thoughtless means to allow users - that shouldn't even be aware of `m`'s existence - to have access to it.
Oct 22 2015
parent reply Jeffery <Jeffery gmail.com> writes:
On Thursday, 22 October 2015 at 16:13:09 UTC, Idan Arye wrote:
 On Wednesday, 21 October 2015 at 14:17:15 UTC, Jeffery wrote:
 *snip*
I think you are looking at it wrong. Object composition should either be public or private. If it's public, it should be perfectly fine for user code to be fully aware that a `Yours` has a `Mine` named `m`. Or, if we look at a more concrete example: class Name { public string first; public string last; } class Person { public Name name; } I see no harm done in user code calling `person.name.first`, because a `Name` is just a type, just like `string` or `int` - the only difference is that `Name` is user defined. In these cases, I find it ridiculous for a library wrap all the functionality of another library instead of just passing it on. A real life example: Java used to have a really crappy datetime module in it's standard library(Java 8 got an improved datetime module). Someone made a third party datetime library called "Joda Time"(http://www.joda.org/joda-time/), which is considerably better, and many libraries require it. If these libraries followed your rule, they would have to wrap the functions of library, so instead of `someEvent.getTime().getMonth()` they'll have to implement in the `Event` class a `getMonth` method so you could `someEvent.getMonth()`, and the same with all the methods in the rich interface provided by Joda Time. Does that seem reasonable to you? Now, while it's true that the fact that it comes from a third party library may make it more prune to bugs and breaking changes(which I assume are what you mean by "flaws"), these flaws don't really grow exponentially on the chain of indirection. The flaw *potential* might grow exponentially, since the number of possible chains can grow exponentially, but the number of chains actually used if far smaller! By insisting on a single level of indirection, you are actually making things worse: - Since you need to wrap methods for the user code, you needlessly materialize many possible chains into existence, triggering many possible flaws that no one would have to deal with otherwise. - You make it impossible for users to deal with flaws in libraries you depend on. Even if your library should not be affected by that flaw, your users now depend on you to deal with it even if they should have been able to deal with it themselves. So, a public composition is a public dependency and should not be hidden by the Law of Demeter. How about private composition? If a composition is private, you should not be able to access it via `y.m.foo()` - but not because it's too long an indirection chain, but because it's a private member field! The outside world should not care that `Yours` has a `Mine` named `m` - this composition is supposed to be encapsulated. The thing is - just like automatically defining getters and setters for all member fields breaks encapsulation, so does automatically defining proxy wrappers for all the methods of the member field. It might solve other problems(like lifetime and ownership problems), but it will not achieve the basic purposes of encapsulation, like allowing you to change the internal fields without affecting users of the outer object. If you change a method of the internal object, the methods of the outer object will also change. So, this type of wrapping is not good here either. `Yours` shouldn't just have a method for invoking `m`'s `foo()`. If `Yours` has a functionality that requires invoking `m.foo()`, the implementation of that functionality can call `m.foo()` directly. Otherwise, there is no reason for any method of `Yours` to call `m.foo()` - certainly not as automatic, thoughtless means to allow users - that shouldn't even be aware of `m`'s existence - to have access to it.
I'll have to re-read this again when I get some more time but it seems your main argument is the work required to "wrap".(your Joda example) (I don't see how wrapping breaks encapsulation, in fact, it adds another layer of encapsulation, which isn't breaking it, is it?) The work argument was my whole point though. If the compiler internally wrapped all unwrapped members(easy to do as it is just a simple forwarding proxy) and D itself can do this with just a few lines of code and opDispatch, there is little work the programmer actually has to do. One could talk about the performance hit of all the wrapping indirections, but I imagine these could all be optimized away. Ultimately the issue is with the human element as a perfectly intelligent with infinite memory and speed could manage all the dependency issues easy. The issue about private wrapping is moot as I mentioned about all members being public in the examples. I should have stated that in general. Obviously wrapping private members wouldn't render the "private" meaningless. I'm simply talking about reducing many levels of indirection to one or two by allowing the compiler/language do deal with the internals for us. In a since, all public interface methods and field's methods are implemented automatically and, by default, are proxies. It probably sums up what I'm talking about if not a bit terse. (everything public) class X { void foo(); } class Y { X x; } in this case, to access foo through Y we have two levels of indirection: y.x.foo(); This creates dependencies. What if X is external? foo is changed(e.g., renamed)? Now everything that depends on y and foo will be broken(e.g., a.b.y.x.foo())! As we add more layers(more complexity) it becomes unmanageable. This is the standard state of modern programming. Refectoring is king but all in the name of fixing dependencies. Instead, what if we have class Y { private X x; void foo() { x.foo(); } } In this case, Anything nothing on outside see's x. Hence there are no dependencies on X!! X can change in any way and only Y will be broken. We fix Y, we've fixed all the dependencies on Y! (which, only works well, if all dependencies do the same thing we have done above) Hence class B { private Y y; void foo() { y. foo(); } } A doesn't know about Y, only about B. So fixing Y also fixes A and B! Of course, we can only have one level of indirection using this technique. a.b, b.y, y.x, *not* a.b.y.x. It's very similar to the chain analogy. In a single connection chain. If one link breaks we only have to fix it with the two adjacent ones. In chain mail. If one link breaks, we have to fix multiple ones(e.g., 4). In the real world, it's like having 3D's of chain mail(instead of 2). The only real downside I can see is identifier explosion. (if Y has to wrap all the methods we will surely have other issues to deal with) Anyways, I'll be back! ;)
Oct 22 2015
parent reply Idan Arye <GenericNPC gmail.com> writes:
On Thursday, 22 October 2015 at 17:01:55 UTC, Jeffery wrote:
 ... (I don't see how wrapping breaks encapsulation, in fact, it 
 adds another layer of encapsulation, which isn't breaking it, 
 is it?)


 The work argument was my whole point though. If the compiler 
 internally wrapped all unwrapped members(easy to do as it is 
 just a simple forwarding proxy) and D itself can do this with 
 just a few lines of code and opDispatch, there is little work 
 the programmer actually has to do.

 The issue about private wrapping is moot as I mentioned about 
 all members being public in the examples. I should have stated 
 that in general. Obviously wrapping private members wouldn't 
 render the "private" meaningless.
I did not argue that it's a lot of work - I argue that getting the compiler to do that for you is a bad idea. The point of encapsulation is that you make a concise choice of which members to wrap and how to wrap them. The compiler can't make these design choices, so if the wrapping is done automatically by the compiler it'll simply wrap everything in a straightforward manner, and you miss the whole point of the encapsulation.
Oct 22 2015
parent reply Jeffery <Jeffery gmail.com> writes:
On Thursday, 22 October 2015 at 18:36:46 UTC, Idan Arye wrote:
 On Thursday, 22 October 2015 at 17:01:55 UTC, Jeffery wrote:
 ... (I don't see how wrapping breaks encapsulation, in fact, 
 it adds another layer of encapsulation, which isn't breaking 
 it, is it?)


 The work argument was my whole point though. If the compiler 
 internally wrapped all unwrapped members(easy to do as it is 
 just a simple forwarding proxy) and D itself can do this with 
 just a few lines of code and opDispatch, there is little work 
 the programmer actually has to do.

 The issue about private wrapping is moot as I mentioned about 
 all members being public in the examples. I should have stated 
 that in general. Obviously wrapping private members wouldn't 
 render the "private" meaningless.
I did not argue that it's a lot of work - I argue that getting the compiler to do that for you is a bad idea. The point of encapsulation is that you make a concise choice of which members to wrap and how to wrap them. The compiler can't make these design choices, so if the wrapping is done automatically by the compiler it'll simply wrap everything in a straightforward manner, and you miss the whole point of the encapsulation.
Oh, Well, I don't think it is arbitrary. If you expose a public member, then you are not encapsulating it and exposing it to the public. The compiler does know this. Hence adding any wrapper does not change the encapsulation. If x is a public field of a class, then how can wrapping it hurt anything. If you expose the internal public members of x(if any), then this might create less encapsulation than you want. If you want to hide some public members of x then surely it is not hard to inform the compiler to do so? Explicitly marking the public members of x you want to hide should be easy enough: public class X { public void foo(); } public class A { public X x; private x.foo; // Marks x as private so compiler does not implement wrapper for x.foo } (of course, better notations would exist). Essentially the logic is "wrap by default". Since most of the time the programmer is creating wrappers, this seems it would save a lot of work?
Oct 22 2015
parent Idan Arye <GenericNPC gmail.com> writes:
On Friday, 23 October 2015 at 05:17:47 UTC, Jeffery wrote:
 Oh, Well, I don't think it is arbitrary. If you expose a public 
 member, then you are not encapsulating it and exposing it to 
 the public.

 The compiler does know this. Hence adding any wrapper does not 
 change the encapsulation.
My point is that automatically wrapping every method of the public fields makes the wrapping pointless. Your idea seems to me like sugar around the Law of Demeter. But the point of the Law of Demeter is not to reduce the number of dots per expression - it's to force interactions with the inner objects of a composition to be done via methods of the outer objects. By automatically exposing all methods of the inner objects via wrappers you are missing the entire point of LoD - even if user code can do `yours.foo()` instead of `yours.m.foo()`, it still interacts with `yours.m` without any meaningful mediation of `yours`.
 If x is a public field of a class, then how can wrapping it 
 hurt anything. If you expose the internal public members of 
 x(if any), then this might create less encapsulation than you 
 want. If you want to hide some public members of x then surely 
 it is not hard to inform the compiler to do so? Explicitly 
 marking the public members of x you want to hide should be easy 
 enough:

 public class X
 {
    public void foo();
 }
 public class A
 {
    public X x;
    private x.foo;  // Marks x as private so compiler does not 
 implement wrapper for x.foo

 }

 (of course, better notations would exist).

 Essentially the logic is "wrap by default". Since most of the 
 time the programmer is creating wrappers, this seems it would 
 save a lot of work?
Unless there is a good reason to do otherwise(a good reason is something like to-not-break-existing-code, not something like to-save-some-typing) the default should be the more restricting option. Programmers will add the extra syntax to lift the restriction when the actually need it removed, but they usually won't bother to add the restriction when their code works perfectly fine without it.
Oct 23 2015