Solar wrote:I guess we're both right to some extend. Yes, perhaps today's software world is dominated by web frontends and user apps.
But...
...those aren't usually written in C or C++ (which was what spawned this discussion about the semantics of . versus ->).
In the places where you
do want to employ C/C++, cache hit / chache miss usually
does matter.
OK, yes, that is true. Somewhat less so when talking about Python, but topic drift is a thing and the specific matter at hand was the C/C++ member indirection operator.
I am still not convinced that Brendan's point about cache misses and language design (specifically, his disdain for letting the compiler choose how to lay it out) really is correct, nor do I think it is even relevant to QByte's complaint - what QByte was asserting was that
in C, the member indirection operator was redundant, because using ordinary member operator would always be unambiguous.
While I get that Brendan's point is that it gives a clear indication of one place where a cache miss is likely to occur, I find it amusing since, well, it isn't a relevant point - there were no TLB or data cache lines on either the PDP-7 or PDP-11, yet Dennis Ritchie felt that the syntax was necessary anyway. His point is correct for modern systems so far as it goes (even if I don't think that it makes the difference he things it does), but it doesn't speak to why the syntax was there in the first place.
(For newer members, I should add that this is a long-term sticking point between me and Brendan, and between Brendan and several others such as Embryo. In the case of my OS design, the use of code synthesis would make the sort of exact knowledge impossible, as the code to be used doesn't exist until runtime, and may change over the course of the process lifetime. Whether Massalin's approach is feasible on modern DOoOX superscalar CPUs is, as far as I know, an open research question.)
I would add that, even if I took exception to Brendan's particular reply, QByte's statement was... questionable, once one looks at Ritchie's actual motives for that addition - and the fact that he did was noteworthy, as Ritchie was notoriously reluctant to add
anything he didn't think absolutely necessary.
So yes, there is a reason why the syntax isn't exactly redundant, even if the desired result can be reached without it. To illustrate, let's take this example code:
Code: Select all
int an_int, an_array[10], *a_ptr;
struct Foo_List {
int data;
Foo_List* next;
} *bar;
struct Foo_List baz, quux[16];
I assume that QByte's argument is that if
bar is a pointer, then the compiler has the information it needs to know that
needs to indirect the member operator. However, this reasoning has three problems, one related to Ritchie's design philosophy, the second relating to how C handles memory and structs, the third relating to syntactic ambiguity and complications arising from such.
The first problem is simply that it is an exception to the rule that there needs to be a clear separation of operations on the pointer and operations on the object pointed to. This relates directly to the problem Plauger was talking about with Algol-68, which lacked that clarity. You would have to know that
bar was a pointer in order to understand this snippet's runtime behavior, which is part of what the member indirection operator makes explicit.
The second part is that C treats everything more or less as chunks of memory, and in the original C especially, typing was seen more as a guideline for how what code to generate for a given operator than as an error checking mechanism. In particular, both arrays are just blocks of memory which the compiler treats as broken into pieces of a given size, and a pointer to a type doesn't differentiate pointing to a single item from pointing to an array. So, if you were to write
Then
an_int would get the first item of
an_array, because as far as the compiler cares, it is just copying one int-size block of memory to another. In older compilers, the same holds true with
since the compiler didn't care if assigning a pointer to an arbitrary integer might not make sense (and there was no type checking to catch it - deliberately, to allow for literal pointer constants without requiring any special syntax or even an explicit cast).
Now, if memory serves, structs weren't in the language originally, but were added relatively early on (recall that most languages of that time didn't have them at all, and for that matter most didn't have pointers or any other form of indirection, either; most of those which did have indirection, such as Lisp or SNOBOL. used it exclusively and silently). Whenever they came in, they too were just blocks of memory, with struct variables just treated as the base for the member offsets. Arrays of structs were blocks of memory broken up into pieces the size of that struct type.
And once again, a pointer to a struct type didn't differentiate between a single struct and an array of said structs. As far as I know, this is still the case in modern C, though C++ gets a bit more elaborate about it.
Regardless of whether you think this is a good design or not, it fit what Ritchie was trying to accomplish, and despite his later advocacy for it, he originally wasn't expecting anyone outside of Bell Labs to ever see that language. Law of Unintended Consequences, yo.
So the problem here is that in order to use the member operator with struct pointers the way Qbyte seems to want, the language would either need to keep track of what the pointer is pointing to - which would mean added run-time code to keep track of it, and it would always need to include it since the older compilers weren't smart enough to check for special cases - or just ignore the 'problem' just as it did with scalar arrays.
The final problem is that the line
is ambiguous. Does the indirection operator apply to bar, or to bar.next? For the approach QByte wants, you would need to use
for the former and
for the latter. Is this less gratuitous than
is? I suspect that is a question flame wars could be fought over.