OT: Programming Expansibility

Jeffery via Digitalmars-d digitalmars-d at puremagic.com
Wed Oct 21 07:17:14 PDT 2015


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.









More information about the Digitalmars-d mailing list