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.







Reply via email to