Re: object system...



Responding to cr88192...

If yes, then you are wrong, because there are even no one OO language
with perfect perfomance.
There is no 3GL that provides perfect performance;
for that you need machine language.
I disagree. General purpose languages of xGL are not desined to
decrease performance of previous (x-1)GL.
It's true that they aren't *designed* to be slower. But the price of abstraction is performance. Any nontrivial C program with be 30-50% slower than hand-tuned Assembly. Any C++ program will be 50-200% slower than the C program.

not always the case.
C is usually fairly comparable to "mundane" assembly...
I'm afraid that is just not true. See my response to Riemenschneider. C isn't even as fast as other systems programming languages.


yes, but then again, one can debate then what is "mundane" assembly...

OK. But somewhere along the line I specified "hand-tuned Assembly" as the context of my comments.

much of the time, C++ is performance competitive with C (assuming of course that the C and C++ code operate similarly, for example, the C++ code looks like C, or the C code uses an object-based approach).
The only conceivable situation where this would be true is when one is using C++ as a "Better C". In that situation one essentially has a C program with strong typing and is not using any OO constructs more complicated than a simple Class.


yes, this is actually what I meant...

in the case where one uses C++ almost exactly the same as C, the compiler output is usually very similar...

OK. Not surprising since C++ was originally developed as a preprocessor for C (cfront). In addition, Stroustrup's stated design goal was to enable high performance. Unfortunately the thing he sacrificed for that was OOA/D.

But this is an OO forum. Using C++ as a Better C is just bad OO development. Effectively one is just doing C development so one would expect comparable results.

there are many other cases where "abstraction" can be leveraged to the advantage of the compiler writer, allowing the production of more efficient code than would have been produced otherwise.

a simple example of this would be compiler provided complexes or vectors, where a compiler may be able to leverage the capabilities of the processor (such as specific opcodes for vector handling, ...), and thus generate faster code than would happen if the coder were to just write a big mass of scalar code.
The very best an optimizing compiler could possibly do is write code that is exactly as fast as that produced by a competent Assembly developer. But the compiler can't understand the specific problem being solved. So there will always some problem solutions where the Assembly programmer can beat the optimizer by tailoring the Assembly to that specific problem. Been there; done that.

The only reason those cycle-counting Assembly gurus are a vanishing breed is that it takes them too long to do it. So the economics drives putting money into optimizing compilers that *usually* do *almost* as good as the Assembly guru.


yeah...

but, alas, this is back to the issue of said guru consistently applying optimization, which is slow and requires lots of thinking.

the compiler does it faster, and a coder when faced with a larger problem will usually resort back to slow-ass approaches (and/or huge amounts of copy/paste).

the usual approach then is to do most of the codebase in a language like C or C++, and only fall back to ASM when needed for performance reasons (so, we use ASM for some tight vector-math code, but not for things like the windows event callback, ...).

Right. It is a matter of economics, not computer science.

but, the point was, for example, in C is it more efficient to make use of the compiler-provided complexes (such as the "float _Complex" type), or hand-write ones' own complexes using scalars, function calls, and structs?...

in general, this compiler provided abstraction, will somewhat beat out the performance of doing it manually.

for example:
c=a*b;

I can remember when one always wrote

x = a + a + a

instead of

x = 3 * a

because the FORTRAN compiler was too dumb to realize that a multiply instruction took at least an order of magnitude more cycles than an addition. B-) Those were the Good Old days when programmers were Real Men. But I digress...


may generate a chunk of specialized x87 or SSE instructions, but:
c=myapiMulComplex(a, b);

involves the function call overhead, the cost of copying the structs around on the stack, much less efficiently crafted ASM output (since the compiler may lack most of the context from the caller), ...

I'm not talking about the application developer providing better code. (You could do this with a BLISS macro, though, as I explained elsewhere and it would be inline.) I am talking about what the compiler does with the constructs the language defines.

If the language provides abstract constructs like OO generalization to solve routine problems, then the compiler will not be able to write Assembly code that will run as fast as that of an experienced Assembly programmer solving the same problem.

Similarly, if one uses a 4GL with abstract action language for things like relationship navigation, the transformation engine cannot produce C++ code that will be as fast as an experienced C++ programmer could produce solving the same problem directly.

But the transformation engine will be close enough to the C++ programmer and the C++ compiler will be close enough to the Assembly programmer so that economically the 4GL will win hands down over manual C++ development and manual C++ will win hands down over manual Assembly development.

in my case, I consider it more in this latter sense, or more specific, as in the sense of "interface" as used in languages like Java or C#...
I figured that out when I realized you were a type maven. B-)


?...

You believe an interface defines what an object is. B-)

So "interface" defines all logical properties of its abstraction (what
to do): responsibilities, access, protocols (sequences of usage) etc.

The dividing between interface ("what to do") and implementation
("how to do") is artificial, not natural of nature, but anyway
"object" never can "define its responsibilities", because "object" is
an instance of interface.
Ah. I see. You are a type maven. B-) So we need a little primer here on OOA/D.

Types do not even exist in OOA/D (other than knowledge ADTs). OOA/D is based on class systems rather than type systems. The 3GL type systems are a compromise with the hardware computational models. As a result they do not reflect a number of fundamental OO design issues.

and, some of us really value conventional value types as well...
???


this seemed like you were advocating dispensing with any of the built in value types, and instead implementing "everything" as a class instance (implying then, for example, that numerical types are pass-by-reference, have methods, ... and that operations produce new instances of the class).

Au contraire. for example, the computing space is full of things like String, Array, Stack, etc. that are essentially just data holders. As a result they are ubiquitous in object *implementations*.

But they are tactical solutions so one is not very interested in them at the OOA/D level. So they tend to appear as scalar ADT knowledge attributes in the design and one only gets interested in the details during OOP.


usually, this quickly leads to a "slippery slope" of horrible performance (and the garbage collector going berserk...).

even then, as I see it, even if the performance impact is avoided (value types are implemented normally, but just fake having a class), I don't feel this to be an ideal design (our built-in value types just don't need huge masses of methods).

I am much more inclined to think of implying a generic manipulation to a type, and not of having operactions be a method of the type.

so, I would much rather type:
sin(PI/2);
than:
(PI/2).sin();

even if it were possibly the case that the above were, in some cases, implemented as a piece of syntactic sugar. such as no generic 'sin' function existing, but the language having a trick to allow methods to be called on an object following the same syntax as an ordinary function call.

however, such a trick could lead to potential semantic issues (such as, what is 2 'sin' functions exist, with one inside the object, but the other in the caller's scope...).

class Foo
{
Foo foo_add(Foo this, Foo that);
}

'this' as a method argument implying that the method be called as if it were a function accepting itself...

or whatever...

Wow. You and Kazakov really should get out more. B-) You need to get out of the mud of 3GL type systems and smell the OOA/D roses where none of this stuff matters. (More precisely, it matters to a different trade union, like compiler or transformation engine designer, rather than the application developer union.)

For example, in the MDA profile I use for OOA modeling, the entire abstract action language syntax for describing behaviors is described adequately in two pages and the UML elements used could be described in another five pages (only a small subset of UML is necessary). That is, the level of abstraction is high enough so that the notation syntax is so simple that nuances such as yours simply don't arise.

Yet the models are rigorous enough that they can be executed to validate that functional requirements have been resolved properly. (In translation systems it is common practice to apply the same test cases for functional requirements to the OOA models that one applies to the final executable; just the test harness changes.)

The most important such issue in this thread context is the separation of message and method. Because 3GLs all employ procedural message passing there is no distinction; the message is simply the procedure signature. Since we name procedures by what they do, the message becomes a very specific imperative to do something (Do This). Unfortunately, that implies that the sender of the message knows (A) that the receiver exists, (B) that the receiver does something specific, and (C) that doing it is the next thing to do in the solution. All of those things trash OO encapsulation and implementation hiding because it makes it easy to construct the sender so that it depends on what the receiver does.

yes, this is typically the case, but not always the case...

for example, it is possible to implement a declarative API in terms of linear calls, where each call serves to inform the callee of the next element in the structure, rather than telling it what to do about it.
The use of "call" here is very telling. One of the problems with 3GL type systems is that they are married to procedural message passing. That invites one into a procedural Do This view of the solution as a sequence of operations to be decomposed. That leads to the hierarchical dependencies of the legendary Spaghetti Code. The OO paradigm strives to eliminate those hierarchical dependencies. But because of the 3GL type system compromises the way one must do that is with a different mindset towards design, not OOPL programming.

So it doesn't matter how the interface is defined. The critical mindset for OO development is that an interface only defines messages, not operations. Once one has that mindset, one can construct objects without worrying about sequences of operations and messages can be announcements (I'm Done) rather than procedural imperatives (Do This). That allows one to connect the dots for flow of control later by routing messages.


oh, ok...

yes, I think I know what you are getting at now, only that this particular style is often difficult in C apart from making dependency issues worse (either the creation of bi-directionally dependent APIs, the ugly use of lots of callbacks, ...).

I don't see it as a language issue. C++ was originally a preprocessor for C so one can always get to an equivalent procedural representation. (Translation transformation engines routinely do this going from an OOA model to C, Assembly, or even MS Word documentation.) More to the point, you don't need to implement OOA/D constructs literally. For example, when implementing generalization the OOPLs apply a lot of baggage around virtual jump tables because they need a general implementation of the construct that includes things like polymorphic dispatch, implementation inheritance, and complex constructors. But for a specific problem OOA/D most of its generalizations can be implemented with a couple of nested structs.

Also the business rules and policies from the problem space that dominate the dynamics are going to get encoded pretty much the same way in C, C++ or an AAL. Thus everybody uses the same arithmetic, logical, and bitwise operators while gluing them together with the same sort of expression syntax. That's because in the end software is about computation and we've had many centuries of experience describing algorithms.

So I think the OOness is really a mindset issue. It is about how one *organizes* the software. In an OO context we are concerned with dependencies and coupling so we define little puddles of code in a fashion that minimizes dependencies and coupling. To do that we have methodological crutches like problem space abstraction, encapsulation, implementation hiding, separation of message and method, yadda, yadda, yadda.

As an example of what I mean, I recall an article for the '70s that compared various Structured methodologies. Advocates of each methodology actually solved a real problem and the results were compared. The interesting conclusion was that all but one of them looked remarkably similar. The odd man out was produced from Michael Jackson's JSD methodology, which looked very different from the other solutions even though it used all the same diagrams as everyone else.

The reason was that Jackson was ahead of his time because a lot of his design techniques were very similar to later OOA/D methodologies even though he had no notion of an 'object'. Thus he set great store in notions like cohesion, functional isolation, API encapsulation, subject matters extracted from the problem space, and whatnot. Though he didn't articulate it the same way, he clearly had an understanding of coupling issues and the problems of hierarchical dependencies in functional decomposition.

The bottom line is that one could take his design, update it for modern UML bells & whistles, and one would be hard pressed to see how it differed from good OOA/D practice today. But Jackson was a Structured Programming guy, not an OO guy. Yet he somehow incorporated OO-like ideas in a procedural design.

So I would expect basically the same bubbles & arrows to be used in the design (most of the UML diagrams are thinly veiled diagrams from Structured Programming like DFDs) and the same syntactic elements in the 3GL when doing OOA/D/P. But I would expect the way boundaries were defined and the way program units were organized to be quite different than in a procedural development.

I think I mentioned that if one gets the OOA/D right, then the fact that the OOPL marries message and method does not matter. That's because the mindset of the OOA/D will prevent the message sender from hierarchically depending on the message receiver before one ever gets to OOP. So the compromises in the OOPLs become benign.

So I don't think one needs anything special in the way of syntax or implementations. Recording an OO design in an OOPL will be easier but an OOPL isn't a necessary condition for implementing the design. IOW, the difference will lie not in the way one accesses program units but in the content of the program units.

IOW, one does not think about sequences of operations (calls) until the very last thing one does in an OO design. Nor does one think in terms of one object doing something for another object. One designs object methods in complete isolation from the solution context. It is only in the DbC context of connecting the flow of control dots that one thinks about contracts in terms of 'client' and 'service'. But then the 'client' is quite generic; it is *some* object that accesses the service, not a particular object.


well, this is different...

It's classic OOA/D. Every OOA/D book I've seen starts with the static description: objects, relationships, responsibilities. And every OOA/D methodology I know stresses abstracting the *intrinsic* nature of the problem space entity in terms of self-contained, cohesive responsibilities.

Different authors put different wrappers on it. So Jacobson's designs are driven by use cases and he has a special classification of objects while Rebecca Wirfs-Brock's designs are driven by roles. But under the hood they all abstract the problem space to provide static structure first and they all advocate defining intrinsic properties. Flow of control and DbC contracts (if used; a lot of authors just supply guidelines for collaboration in Sermon On The Mount mode) are dealt with only after one has all the ducks already on the pond.

To use my Tinker Toy analogy, one identifies which colored wheels (objects) one needs and makes sure they have the right sockets (properties) first. Then one connects them up to form a useful shape (solution) by connecting them with spokes (messages). Thin as analogies go, but cute.

Third, objects are not assembled into larger structures; that is much more of a functional programming concept. There are only three basic levels of abstraction in an OO application: subsystem, object, and responsibility. The elements at each of those levels are peers and they collaborate (send messages to one another) directly as peers. They are logically indivisible at the containing level of abstraction. The only "structures" one can form are through relationships (e.g., a GoF Composite pattern).


ok, it may depend that I have a good deal of exposure to FP as well, and so my thinking and coding practices often mix together FP and OO approaches (along with good old imperative and procedural approaches, ...).

<Hot Button>
Alas, FP and OO approaches are vastly different -- so much so that the approaches are fundamentally incompatible. Thus FP is inherently hierarchical while a major goal of the OO paradigm is to eliminate such hierarchies. FP militantly rejects persistent state while it is critical to the OO paradigm. Those two things alone make the approaches fundamentally incompatible.

One advantage FP has is that it is much more intuitive in a computational environment. So you don't see a lot of FP design books because almost everyone already knows how to do functional decomposition. Meanwhile there is a steep learning curve for OO and one really can't do it properly without following an OO design methodology.

OO methodologies are synergistic in that lots of things play together so that the whole design approach is better than the sum of the parts. The problem with that is that it is rather fragile because a lot of different things have to play together. So when one tries to Mix & Match practices from different methodologies one can break the synergy so that things stop playing together well. Then one may end up with a hybrid methodology that has none of the benefits of either of the source methodologies.

So I get very nervous whenever someone starts suggesting things like making functions first class objects or using composition inheritance to create hybrid objects. Those things aren't in the OO paradigm because they don't play together well with the elements that are already there. Doing that sort of thing tends to break the synergy of the OO paradigm and one loses the the key benefit: maintainability.

Both the OO paradigm and FP work very well in meeting their respective goals. But if one tries to hybridize them I believe there is great risk that none of the goals will be achieved satisfactorily.
</Hot Button>



--
Life is the only flaw in an otherwise perfect nonexistence
-- Schopenhauer

H. S. Lahman
H.lahman@xxxxxxxxxxx
software blog: http://pathfinderpeople.blogs.com/hslahman/index.html
.


Quantcast