[comp.lang.c] Implicit testing of 0

nevin1@ihlpf.ATT.COM (00704A-Liber) (01/23/88)

In article <3208@bunker.UUCP> garys@bunker.UUCP (Gary M. Samuelson) writes:
.1.  I don't find the implicit test for zero "distasteful" -- I find
.	hard-to-read code distasteful.  Actually, "taste" is irrelevant.
.	Cost of development and cost of maintenance are.  Hard-to-read
.	code costs too much.  I enjoy solving puzzles as much as the
.	next guy, but programmers should be paid to write programs,
.	not solve puzzles.

I agree with you on this.  But, implicit testing for zero is not hard to read
code; in most circumstances, it is easier to read (see comments point #3). (BTW,
I'm not sure whether you are for or against implicit testing; you never really
say.)

.2.  It is irrelevant whether I have programmed in assembly.  As it
.	happens, I have.  I'm very good at it, and I write both
.	assembly and C according to the "write for the reader"
.	principle.

The "readers" I write C and (used to) write assembler for are other C and
assembly language programmers.  I developed my style of coding mainly from
looking at a large collection of others' works.  Any many other people have
used implicit testing in their programs.

It is not irrelevant whether or not you have programmed in assembly.  This is
where the notion of implicit testing comes from.  Most assembly language
programmers do NOT usually do explicit comparing of values before branching
(unless necessary); they rely on the knowledge of how each instruction modifies
the test flags (I know this probably doesn't generalize to all assembly
languages; no flames, please).  When one goes from assembler to C one brings
their style with them; ie, if they used implicit testing in assembler and C
will support it, then they will probably use it in C.

.3.  I don't buy the "conciseness" argument -- In the first place,
.	1 character is not significantly more concise than 2.  In
.	the second, "conciseness" is only a virtue, in my opinion,
.	to the extent that it provides readability.

First off, 1 character is significantly more concise than 2 if you have to use
that character 1000 times.  Also, you propose later (point #5) that
'if ((x = f(n)) != 0)' replace 'if (x = f(n))', which is an increase of one set
of parentheses and 5 characters.  You are proposing I do 5 times more work! :-)

Secondly, readability, to an extent, is in the eye of the beholder.  A lot of
what implicit testing is used for is checking for error conditions on the
return of a system call.  If an error occurred it returns 0; otherwise, it
returns a useful value.  When I see code which looks like

if (!(var = system_call(parameters)))
	exit_with_error_status();
do_some_other_stuff_with(var);

I think to myself:  var is the result of system_call(); but, if system_call()
failed, some error handling is done.

When I see code which looks like:

if ((var = system_call(parameters)) == 0)
	exit_with_error_status();
do_some_other_stuff_with(var);

I think to myself:  var is the result of system_call().  If it does not equal 0,some error handling is done.  I have to ask myself:  what does a return value
of 0 mean?  Answer:  system_call() failed.

With this reasoning, the first is more readable than the second because I think
more abstractly about it.  I don't really care that 0 means false; I just want
it to do something special if the result is false.

A possible argument against this is that I may be making false assumptions
about case 1 (ie, 0 does not always mean the system call has failed).  But, if
I always coded using case 2 I would eventually think the same as in case 1 and
hence make exactly the same types of mistakes.

.4.  While it may be true that typing "mian" for "main" is as likely as
.	typing "=" for "==", the linker will tell me that I mispelled
.	"main" by reporting "undefined symbol."  Neither the compiler,
.	the linker, nor lint (of which I am an ardent advocate) will
.	tell me if "=" should be "==".

Lint doesn't know what you are trying to do.  If it did, why would we need
programmers in the first place? :-)  And what happens if you call one of your
functions mian(); lint won't tell you that you mispelled main.  (This kind of
thing can happen if you make one function called hello() and another function
called Hello(); or look at the different forms of exec(); they are pretty close
to each other.)

.5.  When I see "if (x = fn())", I know what it means, but sometimes I
.	am not sure whether the author meant "if (x = fn())" or
.	"if (x == fn())".  I spend a lot of time trying to figure out
.	what other programmers meant, even after I figure out what
.	they wrote.  "if( (x = fn()) != 0 )" is longer, but it is
.	totally unambiguous to both the author and the (human) reader.

When I am reading code, I can usually tell whether the author meant assignment
or comparison by the context (does he use the x later, was he using x for a
different purpose before, etc.).  Both are unambiguous UNLESS you think that
the author did something wrong, in which case the second one is easier to see
what is actually happening (vs looking at what is happening conceptually).
This is one of the few drawbacks of implicit testing (languages with many side
effects such as Lisp and Icon suffer from this).


Since most of the standard functions are designed with implicit testing in
mind, it is conceptually easier to understand code which takes advantage of
implicit testing.  This makes it more readable, and hence preferable.
-- 
 _ __			NEVIN J. LIBER	..!ihnp4!ihlpf!nevin1	(312) 510-6194
' )  )				"The secret compartment of my ring I fill
 /  / _ , __o  ____		 with an Underdog super-energy pill."
/  (_</_\/ <__/ / <_	These are solely MY opinions, not AT&T's, blah blah blah

john13@garfield.UUCP (John Russell) (01/25/88)

In article <3484@ihlpf.ATT.COM> nevin1@ihlpf.UUCP (00704A-Liber,N.) writes:
>This is one of the few drawbacks of implicit testing (languages with many side
>effects such as Lisp and Icon suffer from this).

I'm surprised no one has mentioned the Icon syntax in the context.

=
==
===
:=
:==
:==:
+==
++==

Those are from memory, but doesn't it get about that unwieldy?

John
-- 
"You are lying scum!"
	-- member of Parliament James Fulton was expelled for saying this to
	   Canadian Prime Minister Brian Mulroney; use of the word "lying"
	   was deemed unparliamentary, although the "scum" was acceptable

john13@garfield.UUCP (John Russell) (01/25/88)

(Haven't seen 3208@bunker yet in its entirety so I'll be brief)

>In article <3208@bunker.UUCP> garys@bunker.UUCP (Gary M. Samuelson) writes:
>.1.  I don't find the implicit test for zero "distasteful" -- I find
>.	hard-to-read code distasteful.  Actually, "taste" is irrelevant.
>.	Cost of development and cost of maintenance are.  Hard-to-read
>.	code costs too much.  I enjoy solving puzzles as much as the
>.	next guy, but programmers should be paid to write programs,
>.	not solve puzzles.

Might I propose a solution then?

/* do several assignments to either the success value or 0 if call fails */

if (x = y = z = a = b = c = fn())
...
else	/* I know all my temp variables are set to 0 here */
...

/* test current value of function against expected value */

if (q == fn())	/* or if (fn() == q) which some people prefer */
...

This allows you to save a machine cycle here and there, and do other things
that someone else might find hard to follow, without creating an unreadable
program. I personally find it easier to type out a short comment than to
stretch my fingers for extra parentheses, '!= 0', '!= NULL' etc; I use a single 
key macro to do the /*  */ and leave the cursor in the middle. Positioning 
extra parentheses in the middle of some long expression has bitten me far more 
often than has = vs ==.

John
-- 
"You are lying scum!"
	-- member of Parliament James Fulton was expelled for saying this to
	   Canadian Prime Minister Brian Mulroney; use of the word "lying"
	   was deemed unparliamentary, although the "scum" was acceptable