John McCarthy died today.

K

Keith Thompson

BartC said:
So things can be done about it, such as only allowing (a=b) to return a
value when used on the rhs of another assignment. Or requiring an explicit
operator to extract the value of (a=b). It would be a dramatic change to the
language, but it will never be done because opponents will insist that
anyone who gets = and == mixed up is just a lousy programmer and should be
looking for a simpler occupation.

It will never be done because it would break existing code. It has
little or nothing to do with some imagined elitism.

Actually, I can think of a way to address the issue without breaking
existing code. (I don't think this would be adopted, and it probably
wouldn't be worth the added complexity.)

Define ":=" as an alias for "=", the simple assignment operator.
Keep "=" and "==" as they are. Provide an *optional* method
(perhaps a new #pragma) to turn on a warning for any use of "=".

Existing code would be unaffected. New code would be unaffected
if the author doesn't choose to use the new feature.
 
B

BartC

Actually, I can think of a way to address the issue without breaking
existing code. (I don't think this would be adopted, and it probably
wouldn't be worth the added complexity.)

Define ":=" as an alias for "=", the simple assignment operator.
Keep "=" and "==" as they are. Provide an *optional* method
(perhaps a new #pragma) to turn on a warning for any use of "=".

Existing code would be unaffected. New code would be unaffected
if the author doesn't choose to use the new feature.

Looks good to me. But just surprised you are actually suggesting changing
C's assignment operator to ":=", even if you're making it optional.

The only problems I can see are the confusion when := and = are used in the
same file (because someone using := will be maintaining or reading code that
uses =, and vice versa). And where :=, = and == are allowed at the same
time, people using := are possibly more likely to want to use = for
equality.
 
K

Kaz Kylheku

It will never be done because it would break existing code. It has
little or nothing to do with some imagined elitism.

Of course, there is always the preprocessor.

#define eq(x, y) ((x) == (y))
#define set(x, y) ((x) = (y))

while (!eq(set(ch, get(stream)), EOF)) {
/* ... */
}
 
K

Keith Thompson

BartC said:
Looks good to me. But just surprised you are actually suggesting changing
C's assignment operator to ":=", even if you're making it optional.

Why is that surprising?

In any case, I'm not advocating it, merely suggesting that it's a
possibility. If we were designing C from scratch today, I'd advocate
using ":=" for assignment. But we're thoroughly stuck with "=" for
assignment. It's tempting to provide a mechanism to use ":=" as an
alternative, but the only thing more confusing than a language that uses
"=" for assignment (and permits it in expressions) is one that uses both
"=" and ":=".
The only problems I can see are the confusion when := and = are used in the
same file (because someone using := will be maintaining or reading code that
uses =, and vice versa). And where :=, = and == are allowed at the same
time, people using := are possibly more likely to want to use = for
equality.

*If* we wanted to incrementally replace "=" by ":=" for assignment,
we could have a #pragma that causes ";=" to be recognized and "="
to be forbidden. But the more I think about it, the less I actually
like the idea.
 
K

Keith Thompson

Kaz Kylheku said:
Of course, there is always the preprocessor.

#define eq(x, y) ((x) == (y))
#define set(x, y) ((x) = (y))

while (!eq(set(ch, get(stream)), EOF)) {
/* ... */
}

And of course:

#define BEGIN {
#define END }
#define IF if (
#define THEN } {
#define ELSE } else {
#define ENDIF }

and so on.
 
N

Nick Keighley

On 11/07/2011 02:49 PM, Kaz Kylheku wrote:
...



I would not recommend any change to C itself, because the need for
backwards compatibility prevents any meaningful fix to this problem.
Either change would be "insane" because of the huge amount of existing
code that would be broken.

As a feature of a brand new language, I wouldn't characterize the idea
that assignment should be a statement type and not an expression type
insane. It's a normal feature of many existing languages.

so are assignment expressions

I've always quite liked the idiom

int c;

while ((c = getchar ()) != EOF)
process_char (c);

it often saves clumsy code

int c = getchar ();

while (c != EOF)
{
process_char (c);
c = getchar ();
}
 
M

Malcolm McLean

Define ":=" as an alias for "=", the simple assignment operator.
Keep "=" and "==" as they are.  Provide an *optional* method
(perhaps a new #pragma) to turn on a warning for any use of "=".
Or you could make the warning non-optional.

A utility to go through old C code changing every incidence of "=" to
":=" would be quite simple to write, and would ship with every new
compiler.
 
J

James Kuyper

so are assignment expressions

I've always quite liked the idiom

int c;

while ((c = getchar ()) != EOF)
process_char (c);

it often saves clumsy code

int c = getchar ();

while (c != EOF)
{
process_char (c);
c = getchar ();
}

I use the idiom myself frequently. I like the fact that it allows me to
write only a single call to getchar(). However, I think it is a clumsy
construct.

There's a need I've felt for a long time, and I just realized that
there's a feature that could be defined which would meet that need. I'm
inventing this idea while I'm writing this, so please be patient with
any lack of elegance.

What I want is to define a condition for a looping construct, and two
separate blocks of code that get executed repeatedly until the condition
is met, but the condition is tested between the two blocks, rather than
before or after a single block. It would be sort-of half-way between a
while() and a do-while(). I'm horrible at naming things - I'll just use
"token_n" to indicate the new as-yet unnamed keywords. The grammar
productions for this new kind of iteration statement would be:

token_1 statement_1 while (condition) statement_2 token_2
token_1 declaration while (condition) statement_3 token_2

The semantics would be to execute statement_1 unconditionally, then test
the condition. If the condition is false, terminate the entire
statement. Otherwise, execute the second statement, followed by the
first statement, and test the condition again; repeat until the
condition is false. If the both statements are compound statements, they
are treated as a single block for purposes of scope rules. If
statement_1 is a compound statement, and statement_2 is not, statement_2
is treated for purposes of scope rules as being inside of statement_1's
block. The break and continue statements work exactly as they do in
other iteration statements.

The second grammar rule is treated as being equivalent to

token_1 {declaration} while (condition) statement_3 token_2

The example you gave above could then be written as

token_1
int c = getchar();
while(c != EOF)
process_char(c);
token_2

I would prefer that to either of the alternatives you gave above. Note,
among other things, that the scope of 'c' is restricted to this new
iteration statement, something that's not possible with the other
alternatives.
 
J

James Kuyper

On 11/07/2011 05:06 PM, Keith Thompson wrote:
....
Actually, I can think of a way to address the issue without breaking
existing code. (I don't think this would be adopted, and it probably
wouldn't be worth the added complexity.)

Define ":=" as an alias for "=", the simple assignment operator.
Keep "=" and "==" as they are. Provide an *optional* method
(perhaps a new #pragma) to turn on a warning for any use of "=".

I think it would make more sense for use of "=" to become a syntax error
if that pragma is turned on. That would mandate a diagnostic, but also
give implementations permission to refuse to translate the code.
 
B

BartC

Malcolm McLean said:
Or you could make the warning non-optional.

A utility to go through old C code changing every incidence of "=" to
":=" would be quite simple to write, and would ship with every new
compiler.

Well, every "=" not preceded by "<", ">" and perhaps ":" (where a file has
already been converted), and not followed by another "=".

But what about "=" used to initialise data? Does that also change to ":="?
For a static initialisation, it isn't really assignment.

Or "=" occurring in comments and string literals; sometimes you want these
to be left alone, but if it's commented-out code, or a string literal which
happens to contain C source code, then they need to be changed! Unless it's
C source code intended to produce old-style source...

Or where a "=" occurs in a macro, in a way that would later be matched to
another string to form "<=" or "==".

See, these things are never that easy...

And what about where someone has written a=b when they had actually meant
a==b, but somehow the program works. Once converted to a:=b, it's then less
likely that the error will be spotted later on.
 
L

lawrence.jones

James Kuyper said:
What I want is to define a condition for a looping construct, and two
separate blocks of code that get executed repeatedly until the condition
is met, but the condition is tested between the two blocks, rather than
before or after a single block. It would be sort-of half-way between a
while() and a do-while().

That's the generalized do-while loop that I've proposed (not entirely
seriously but not entirely in jest, either) in the past:

do statement1 while ( expression ) statement2

Existing code just uses a null statement for statement2.
 
P

Phil Carmody

Kaz Kylheku said:
What if in some insane language, subtraction were written "++" and addition
"+"?

Well, there's many a fairly sane language where addition can be
written "- -", why not run with that as an example instead?

Phil
 
K

Kaz Kylheku

I use the idiom myself frequently. I like the fact that it allows me to
write only a single call to getchar(). However, I think it is a clumsy
construct.

There's a need I've felt for a long time, and I just realized that
there's a feature that could be defined which would meet that need. I'm
inventing this idea while I'm writing this, so please be patient with
any lack of elegance.

Common Lisp loop macro:

;; pretend (pop *stream*) is analogous to getchar().
(defvar *stream* '(1 2 3 4 5 EOF))

(loop for ch = (pop *stream*) until (eq ch 'EOF) do (print ch))
1
2
3
4
5

The FOR = clause combines initialization and stepping, so the UNTIL test is
interposed in between the first initialization and the first stepping.

You can separate the initialization and stepping using THEN:

;; print powers of two below 100
(loop for x = 1 then (* 2 x) while (< x 100) do (print x))

This suggests a possible extension to C (a backward compatible one).

Suppose the increment could be omitted in the for loop (including the
semicolon)? The initialization could then serve as the increment:

for (ch = getchar(); ch != EOF) {

}

This could even work with:

for (int ch = getchar(); ch != EOF) {

}

The semantics could be that if the step expression is missing, the continuation
of the loop proceeds by re-entering the entire construct, as if by a backwards
goto to a fictitious label:

contin:
for (int ch = getchar(); ch != EOF) {

continue; // goto contin;
}

Another possibility would be that on entry, "int ch = getchar()" behaves
as an initialization, but on subsequent iterations, its behavior turns
into the assignment "ch = getchar()".

The choice would make a semantic difference if C ever gets lexical closures.
Lexical closures could tell the difference between ch being the same object on
every iteration or a newly instantiated binding. Re-using the same variable is
more consistent with imperative iteration.

The problem with all this is syntax: for (A;B) is too similar to for (A;B;)
which already has a meaning. One little semicolon leads to a drastic semantic
change.

What if a keyword were used:

for (int ch = getchar(); ch != EOF; continue) {

}

Nah, way too intuitive!

for (int ch = getchar(); ch != EOF; static) {

}

That's better! :)
 
J

James Kuyper

Common Lisp loop macro:

;; pretend (pop *stream*) is analogous to getchar().
(defvar *stream* '(1 2 3 4 5 EOF))

(loop for ch = (pop *stream*) until (eq ch 'EOF) do (print ch))
1
2
3
4
5

The FOR = clause combines initialization and stepping, so the UNTIL test is
interposed in between the first initialization and the first stepping.

You can separate the initialization and stepping using THEN:

;; print powers of two below 100
(loop for x = 1 then (* 2 x) while (< x 100) do (print x))

This suggests a possible extension to C (a backward compatible one). ....
What if a keyword were used:

for (int ch = getchar(); ch != EOF; continue) {

}

That would be fine when the first part of the loop is a single
expression statement. It's not greatly dissimilar from the idiom I
already routinely use:

for (int ch; (ch=getchar()) != EOF; )
{
}

In the standard as it's currently written,

for ( clause-1 ; expression-2 ; expression-3 ) statement

clause-1 can be an expression or a declaration; expression-2 and
expression-3 can only be expressions. only the final part is a
statement. What I'm talking about could be implemented through a
modification of the syntax to

for ( clause-1 ; statement-1 expression ) statement-2

This would be backwards-compatible because "expression-2 ;" would
qualify as an expression-statement. Non-trivial wording would have to be
proposed to cover how the loop condition is determined if statement-1 is
anything other than an expression-statement. I'm not going to bother
working out what that wording should be, because I favor doing this as a
modification of the do-while statement, which seems more natural.
 
K

Keith Thompson

James Kuyper said:
There's a need I've felt for a long time, and I just realized that
there's a feature that could be defined which would meet that need. I'm
inventing this idea while I'm writing this, so please be patient with
any lack of elegance.

What I want is to define a condition for a looping construct, and two
separate blocks of code that get executed repeatedly until the condition
is met, but the condition is tested between the two blocks, rather than
before or after a single block. It would be sort-of half-way between a
while() and a do-while(). I'm horrible at naming things - I'll just use
"token_n" to indicate the new as-yet unnamed keywords. The grammar
productions for this new kind of iteration statement would be:

token_1 statement_1 while (condition) statement_2 token_2
token_1 declaration while (condition) statement_3 token_2

while (1) {
int c = getchar();
if (c == EOF) break;
process_char(c);
}
 
J

James Kuyper

while (1) {
int c = getchar();
if (c == EOF) break;
process_char(c);
}

OK, so there's no need for a new language feature. I generically dislike
while(1) loops, which is probably why I didn't think of that solution.
I'll keep it in mind for the future.
 
K

Keith Thompson

Malcolm McLean said:
Or you could make the warning non-optional.

A utility to go through old C code changing every incidence of "=" to
":=" would be quite simple to write, and would ship with every new
compiler.

Are you serious?

Even if I thought it was a good idea, a language change that would
require modifying practically every C source file ever written
would never be accepted.

Modifying source files, even mechanically, is never a zero-cost
operation. Source control systems would show *huge* diffs between
successive versions of the same file. Suppose, for example,
that foo.c version 1 uses "=" for assignment, version 2 makes
some functional changes (10 lines changed), version 3 changes
all the "=" to ":=" (with no other changes), and version 4 makes
some more functional changes (7 lines changed). Imagine trying
to compare versions 1 and 4 to see what the actual changes were.
(No, we're not going to modify every diff tool in existence to
ignore "=" vs. ":=".) And that's assuming the "=" to ":=" change
isn't combined with other modifications.

And version 4 will not compile with earlier compilers. Now that I
think about it, that's a very strong argument against my original
idea.

Using "=" for assignment was not, in my opinion, a good idea. Any
attempt to change it now would be far worse.
 
E

Edward Rutherford

Keith said:
Are you serious?

Even if I thought it was a good idea, a language change that would
require modifying practically every C source file ever written would
never be accepted.

Modifying source files, even mechanically, is never a zero-cost
operation. Source control systems would show *huge* diffs between
successive versions of the same file. Suppose, for example, that foo.c
version 1 uses "=" for assignment, version 2 makes some functional
changes (10 lines changed), version 3 changes all the "=" to ":=" (with
no other changes), and version 4 makes some more functional changes (7
lines changed). Imagine trying to compare versions 1 and 4 to see what
the actual changes were. (No, we're not going to modify every diff tool
in existence to ignore "=" vs. ":=".) And that's assuming the "=" to
":=" change isn't combined with other modifications.

And version 4 will not compile with earlier compilers. Now that I think
about it, that's a very strong argument against my original idea.

Using "=" for assignment was not, in my opinion, a good idea. Any
attempt to change it now would be far worse.

With this attitude, the language will never improve!
 
J

James Kuyper

Keith Thompson wrote: ....

With this attitude, the language will never improve!

Whenever you propose a backwards-incompatible change to the language,
the benefits of the change need to be balanced against the costs due tot
he incompatibility. The change should not be done unless the benefits
outweigh the costs by a substantial margin This constrains the
possibilities for improvement, but it does not prevent improvement.
 
K

Kaz Kylheku

With this attitude, the language will never improve!

Inextensible languages like C do improve or at least evolve by creating
spin-offs which are new languages. Some are backward-compatible (Objective C).
Some have a lot of backward compatibility (C++). Others are incompatible
(Java).

Speaking of which, even though Java is not meant to be compatible with C, its
assignment operator is = and comparison is ==.

Oops!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top