OT: Re: Perl Peeves
- From: Bruce Cook <bruce-usenet@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx>
- Date: Thu, 05 Feb 2009 00:24:40 GMT
Peter J. Holzer wrote:
On 2009-02-03 14:59, Bruce Cook
<bruce-usenet@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx> wrote:
Uri Guttman wrote:
PJH> Who said I don't like it? I never said such a thing. I don't even"PJH" == Peter J Holzer <hjp-usenet2@xxxxxx> writes:
think PJH> it is very complex. The number 0, the strings '' and '0'
and the undef PJH> value are false, all other scalars are true.
i agree this is a simple and clear rule. why people fuss over it, i
don't know. let perl be perl. forcing a specific value to be the one
'true' false (sic :) is a waste of time and anal. it is like normalizing
for no reason and it bloats the code. even the concept of using the
result of a boolean test for its numeric or string value bothers me. a
boolean test value (of any kind in any lang) should only be used in a
boolean context. anything else is a slippery shortcut that makes the
code more complex and harder to read.
That's basically where I'm coming from - I have an immediate cringe when
I see the result of a test being used as an int.
I find this odd from someone who claims to have an extensive background
in assembler and early C programming. After all, in machine code
everything is just bits. And early C had inherited quite a bit (no pun
intended) from that mindset, if mostly via its typeless predecessors
BCPL and B.
It's basically a background thing. As you say everything is just bits. The
earlier compilers I work with were all 16 bit, and literally everything was
a 16-bit int, pointers, *everything* (even when chars were passed into
functions, there were passed 16-bit (to satisfy even-byte boundary
constraints), manipulated 16-bit, you just ignored the top 8-bits of the
word in certain operations. To add to this, the compilers didn't do a lot
of sanity checking, the compiler just assumed you knew what you were doing
and would faithfully "just do it". Early compilers didn't have function
prototyping (a function prototype was a syntax error), void was a keyword
introduced to the language later, so void * was unheard of in most code.
(If you wanted to check your code out for bad stuff you ran lint over the
code and it would give you a whole bunch of error lines, fairly much what
you'd expect to get as warnings, and in some case outright errors from a
modern compiler)
This engendered very fast and loose usage of ints for everything. In a lot
of early code you'd see pointers, chars and all sorts declared as int and
some truely horrendous coding:
foo (a,b) {
int a,b; /* We don't know what a and b are at this point, just declare them
int for now */
struct bar *z;
struct frotz * y;
/* First word that a points to signals a bar or not */
if(*(int *)a == 1) {
z= a;
...
} else {
y=a;
...
}
Code could have been done properly using unions, however that was work and
because everyone knew what was really happening in the background why
bother?
This all came crashing down when we started porting to other platforms,
which had different architecture driven rules.
We still didn't have compilers that would point our our stupidity, so to
make code portable took a great deal of self-discipline. One of the big
things you had to get right was data typing integrity (even though C didn't
explicitly type anything much)
It became quite common for a project to have a project-wide header file
which defined the projects' base datatypes and one of the common ones that
turned up was:
typedef int bool;
This didn't mean bool was special, declaring it just signaled to the
programmers that they were dealing with an int that had certain meaning.
In systems programming you would get things like this simplistic example:
bool is_rx_buffer_full (int buffer) {
....
return (qio_buffer_set[buffer]->qio_status & QIO_RX_FLAGS);
}
note that this function is declared as returning bool, which implies that
what it returns should only be used in a conditional expression. If you
tried to use it as an int, you could, but you wouldn't get what you
expected.
....
step_func= operations[buffer][is_rx_buffer_full(buffer)];
/* oh shit, we seem to have wandered off into the weeds -
no runtime bounds checking in C so it's time to fire up the
debugger to find why we're crashing. */
knowing that is_rx_buffer_full is bool, we would have done it this way:
step_func= operations[buffer][is_rx_buffer_full(buffer)?1:0];
we could have made is_rx_buffer_full() return 0 or 1, but that would have
taken extra code and on 16 bits you simply didn't just do extra code to make
things pretty. In most cases that return value would just have been used in
a condition and so would be fine (and that's what bool in this header says
it's for). 0 or non-0 served well enough for what it was to accomplish.
if you wanted to use a bool as an int, you disciplined yourself to normalize
it then.
This was all done by convention - no typing in the language, but if the
programmer ignored the implied typing, you could almost guarantee there'd be
tears.
The whole industry hit the portability issue at about the same time. This
lead to a lot of the modern features of C, including posix, function
prototypes, a lot of the standard header files, many of the standard
compiler warnings and of course the C standards. Others decided that C was
just stupid for portability and created their own language (DEC used BLISS,
which was an extremely strongly typed language and served them well across
many very different architectures)
I find it odd that
normalization of bool results is built into the compiler,
What "normalization of bool results is built into the compiler"?
Consider:
c= (a || b)
as you say, these are just ints like everything else in C.
Easiest way to compile that on most architectures would be:
mov a,c
bis c,b ; bis being the '11 OR operator
however if you need to make sure your result is 1 or 0
mov a,c
bis c,b
beq 1$ ; normalize !0 to 1
mov #1,c
1$:
(obviously here I'm ignoring architecture implementation of the actual
storage of a,b & c for clarity )
[...]
You make the mistake of assuming a-priory that there must be such a
thing as a "logical" or "boolean" type. C has no such thing. The
operators !, ==, <, &&, etc. all return results of type int.
(1 == 1) does not return "true", it returns "1", just like the δ(1,1)
returns 1. You may not like that, but that's the way it is in C.
and not useful in itself.
What I meant was literally that the construct !0 is not useful in itself.
I find it very useful that operators built into the language returned a
defined result. If anything, C has too many "implementation defined" and
"undefined" corners for my taste.
Yes, but I think it's also one of the strengths of C. You define your own
rules to make it fit to your needs for a particular project and as long as
you're consistent and design those rules properly it all works.
Modern languages try to address these undefined corners, but it often makes
them difficult to use for some applications.
Bruce
.
- Follow-Ups:
- Re: OT: Re: Perl Peeves
- From: Peter J. Holzer
- Re: OT: Re: Perl Peeves
- References:
- Re: Perl Peeves
- From: Bruce Cook
- Re: Perl Peeves
- From: Peter J. Holzer
- Re: Perl Peeves
- From: Uri Guttman
- Re: Perl Peeves
- From: Bruce Cook
- Re: Perl Peeves
- From: Peter J. Holzer
- Re: Perl Peeves
- Prev by Date: Re: error printing page using LWP::Simple
- Next by Date: Re: what is the best way to convert a formatted time to a unix epoch
- Previous by thread: Re: Perl Peeves
- Next by thread: Re: OT: Re: Perl Peeves
- Index(es):
Relevant Pages
|