Does D have too many features?

H. S. Teoh hsteoh at quickfur.ath.cx
Sun Apr 29 20:45:58 PDT 2012


On Sun, Apr 29, 2012 at 04:40:37PM +0200, Jacob Carlborg wrote:
[...]
> * Do-while loops, how useful are those actually?

OK, that got me all riled up about looping constructs, and now I'm
provoked to rant:

Since the advent of structured programming, looping constructs have
always been somewhat b0rken. Yes they worked very well, they are clean,
powerful, gets rid of almost all cases of needing goto's, etc.. But the
point of a loop is that *the entry point does not always correspond with
the exit point (where the loop condition is tested)*. The problem with
both do-loops and while-loops is that they are just straitjacketed
versions of a more general looping construct.

At its most fundamental level, a loop consists of an entry point, a loop
body, and some point within the loop body where the loop exits.
Sometimes you need multiple exits, but most cases only need a single
exit point. So a loop (unrolled) might look like this:

	codeBeforeLoop();
	// Loop body begins here
	doSomething();
	if (!loopCondition)
		goto exitLoop;
	doSomethingElse();
	doSomething();
	if (!loopCondition)
		goto exitLoop;
	doSomethingElse();
	...
	exitLoop:
	codeAfterLoop();

Note that I deliberately split the loop body into two parts, before the
loop condition and after the loop condition. This is because you often
have code like this:

	auto line = nextLine();
	while (!eof()) {
		processLine(line);
		line = nextLine();
	}

Notice the duplicated nextLine() call. That's stupid, the initial call
to nextLine() is actually already the beginning of the loop. Why do you
need to repeat it again inside the loop? Because while-loops always
tests the loop condition at the beginning of the loop. OK, what about
using a do-loop instead?

	do {
		auto line = nextLine();
		if (!eof())
			processLine(line);
	} while (!eof());

OK, we got rid of the duplicated nextLine() call, but now we introduced
a duplicated eof() test. This is also stupid. The only reason it has to
be written in this stupid way is because do-loops always test the loop
condition at the end of the loop body, but you need to evaluate the
condition BEFORE the second half of the loop body (processLine) is run.
But once you've evaluated that condition, the test at the end of the
loop body is redundant.

So you might say, OK, just write this then:

	for(;;) {
		auto line = nextLine();
		if (eof())
			break;
		processLine(line);
	}

Well, finally you have something sane. The loop condition now correctly
appears in the middle of the loop body, which is where it should've been
all along. Only problem is, writing for(;;) is misleading, because
you're not looping indefinitely, there's precisely one exit condition.
Conveying intent is very important in writing good code, and this code
breaks that principle.

So really, what is needed is a sane looping construct that unifies while
loops, do-loops, and exit-in-the-middle loops. Something like this:

	loop {
		// first part of loop body
	} exitWhen(!loopCondition) {
		// second part of loop body
	}

It doesn't have to be this exact syntax, but the point is that you need
to be able to express loop exits from the middle of a loop body without
needing to resort to for(;;) and break, when the loop is a good ole
simple loop with a single entry point and a single exit point. (More
complex loops that need continue's and break's are, well, more complex,
so it's OK to sprinkle if conditions in them like we do now.)

</rant>


[...]
> * Infix notation for calling any method taking one argument
> * Basically any symbol is allowed in method names
> 
> That is:
> 
> 1 + 2
> foo bar foo_bar
> 
> Would be translated to:
> 
> 1.+(2)
> foo.bar(foo_bar)
> 
> That is a very general way to handle operators and let the user
> create new operators, not just overloading existing ones.
[...]

While personally, I like the idea of being able to create new infix
operators, it will cause too big a change to D syntax, and probably
cause lots of breakages with existing code, as well as make the
lexer/parser much harder to implement (given the existing D features).

You also then have to deal with operator precedence between arbitrary
user-defined operators, which is non-trivial in general (though it's
workable if you impose some constrains -- but it's probably way beyond
the scope of D2 or even D3).


T

-- 
Trying to define yourself is like trying to bite your own teeth. -- Alan Watts


More information about the Digitalmars-d mailing list