Elegance of Reason

Sunday, January 29, 2006

What Was Old is New Again

I don't know if blogs are going to kill Usenet, but today even I got to realize that the overlap is significant - better late than never, I guess. Usenet was network news, and you subscribed to the groups carrying topics of interest in order to get informed of novel developments.

Now, one of my interests is programming language design and evolution, and today's news came from blogs: a presentation about speculations about the future of Java in an otherwise unrelated posting over at The Cliffs of Inanity, and a very promising concept from none other than Bjarne Stroustrup himself from the blog of Carlo Pescio. This was the kind of news that was usually found on Usenet, possibly buried in a thread flaming one language over the other; this was the kind of news that made Usenet an useful source of news.

Now, don't get me wrong: Usenet had a number of cool features, and news dissemination was only one of them; for example, the news might be actually somewhat ... old, but it was brought up while discussing a topic of interest and often to illustrate a point, giving it context and depth; in the above cases, for example, the link about Java lacks this, but there is at least a glimmer of hope that Pescio will write about Bjarne's work in depth at a later date.

Saturday, January 28, 2006

Why the Anti GNU Movement ?

Following a link from a news site, I found the Anti GNU Movement and read the two posts there. The author, apparently, wants to convince fellow programmers that they should not release code under the GPL. Since the code I post is usually under such license, I feel compelled to state my reasons.

For context, please note that I fail to be incensed, one way or the other, at "communism". I live in what is considered a traditionally "communist" region of Italy, yet I used to vote against communism because of the chance it might somehow help the Soviet Union; when that risk went away, I lost interest. I thought the same should happen to most people, except maybe a few extremists, but over the following years I kept noticing that communism had became a recurrent scare word.

At first guess, I believe this is because a number of people who enjoy some degree of power over others (a communist might use "employer" and "workers" as examples, but this is so 20th century) would lose such power should those who are subjected to such power make good of their numbers and together oppose its exercise. The notion that such concerted effort has often a leader, which becomes the one wielding power, is of no comfort to those who risk losing whatever power they have; and losing something you have is an undisputed source of fear.

Having something to lose would explain why some software companies actively oppose the GPL. Microsoft, for example, is probably afraid of losing the control it enjoys over its desktop users, and possibly acrimonious because its attempt to gain control of the server failed; there's no indication that Microsoft is losing money under any shape or form, so it's not about money, it's about control.

However, although Microsoft got the ball rolling, they're not alone in saying that the GPL is communism; a number of voices over the Internet sing to the same tune, and the above blog is but one.

Unless this is another case of astroturfing, one would be tempted to think that some opponents of the GPL, especially among small companies and professional freelancers, actually resent the fact that the GPL makes building something they can sell on top of the work of others, without giving neither money nor code back, riskier than they would like.

Yet, opponents of the GPL include, for example, Alexander Terekhov, whose postings on subjects such as POSIX thread programming make it hard to believe he would need to sink that low. I cannot dismiss the possibility, therefore, that opposition to the GPL may be borne of purely ideological reasons; after all, it is a document where ideology plays a substantial role.

As you might have noticed, this author has not stated yet his reasons for using the GPL; for one, unlike the above, I am not at risk of losing something from doing so - I fail to see how I would incur any inconvenience should Microsoft lose control of the desktop, for example. Furthermore, I cannot help but regard those who would like to exploit somebody else's GPL code without giving back as parasites, so any step taken to hinder such activities looks to me as the moral equivalent of basic hygiene. Last, the activities of others releasing their code under the GPL provided me with a computing environment which I find more desiderable than other alternatives, so I have reason to believe that in doing the same I might be able to contribute back, thus furthering my own interests. It's as simple as that.

Sunday, January 22, 2006

The Good, the Bad and the Ugly, Part 2

Why was the code from Part 1 bad ? For example, it did not check the result of a number of function invocations. Although in the strictest sense there is nothing actually wrong with that, as it is envisioned by the C language, more often than not all it tells the reader is that the writer just didn't think of it. The language also provides the way to tell the maintenance programmer "I am really not interested in the result, only in the side effects" (if you're not interested in the result of a function without side effects, there is no point in calling it, does it ?): cast the result to void, e.g.


/*
popen-test: experiment with popen(), Part 2.

Copyright © 2006 Davide Bolcioni <dbolcion@libero.it>

Distributed under the GPL,
see http://www.gnu.org/copyleft/gpl.html.

*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char* argv[])
{
int rc = EXIT_FAILURE; /* Optimist */

if (argc > 1) {
FILE* sink;

(void)fflush(NULL); /* all open files */
sink = popen(argv[1], "w");

if (sink) {
int status;
int i;

/* NB: deliberately allow to test for no output sent */

for (i = 2; i < argc; i++) {
(void)fputs(argv[i], sink);
}

status = pclose(sink);

/* The return value is funny */

if (status == -1) {
(void)fprintf(stderr,
"%s: pclose() failed.\n",
argv[0]);
}
else if (WIFEXITED(status)) {
rc = WEXITSTATUS(status);
(void)fprintf(stderr,
"%s: child did an exit(%d).\n",
argv[0], rc);
}
else if (WIFSIGNALED(status)) {
(void)fprintf(stderr,
"%s: child terminated by signal %d.\n",
argv[0], WTERMSIG(status));
}
else if (WIFSTOPPED(status)) {
(void)fprintf(stderr,
"%s: child stopped with signal %d.\n",
argv[0], WSTOPSIG(status));
}
else {
(void)fprintf(stderr,
"%s: I don't think we're in Kansas anymore, Toto.\n",
argv[0]);
}
}
else {
(void)fprintf(stderr,
"%s: popen(\"%s\") failed.\n",
argv[0], argv[1]);
}
}
else {
(void)fprintf(stderr,
"Usage: %s <command> [<arg>]...\n", argv[0]);
}

return rc;
}

/*
vim:sw=2:nowrap
*/


As you can see, the code is definitely getting uglier. The above tells us that the writer was not interested in the result of the functions, but tells us very little about why: he might be ignoring a result he should heed.

Handling the result of a function which is called primarily for its side effects is harder than it seems, because the result typically tells if the function was "successful", i.e. the intended side effects actually occurred. If the function returns failure, the caller must handle the fact; as we'll see in the next part, this significantly complicates the code.

Because of the effort involved in handling failures, there is a strong temptation to pretend that side effect causing functions cannot fail; some of you might even say that in the above concrete case, the fprintf() invocations cannot fail. To let the maintenance programmer know that a specific invocation cannot fail, just use assert():


assert(fprintf(stderr,
"Usage: %s <command> [<arg>]...\n", argv[0]) > 0);


This code is actually wrong; if the code is built with NDEBUG, the assert() gets removed during preprocessing and no fprintf() invocation would occur in its place. The ugly-as-hell but correct option is to remember the result in a variable:


int written = fprintf(stderr,
"Usage: %s <command> [<arg>]...\n", argv[0]);
assert(written > 0);


which, unless we want to get into reusing variables or to devise a different variable name for each invocation of fprintf(), gets uglier still:


{
int written = fprintf(stderr,
"Usage: %s <command> [<arg>]...\n", argv[0]);
assert(written > 0);
}


The assert() condition in the examples above is not very tight, since fprintf returns the number of characters written; we're essentially saying that we're positive that fprintf() wrote something. If we wanted to say that we're sure that fprintf() unfailingly wrote everything, we'd have to write a much more involved condition.

There is a trap hidden in there, as in the example below


{
int written = fprintf(stderr,
"%s", argv[1]);
assert(written > 0);
}


the assertion would fire incorrectly if argv[1] had zero length, and the maintenance programmer would have to decide whether the original author did not look up what the return value of fprintf() means, or argv[1] was supposed to have a positive length.

So, when handling results for functions called for their side effect, we can state our convincement that a failure cannot occur with assert(), admit our lack of interest in the matter by casting to void, or handle the failure ourselves, which we'll discuss in Part 3.

Saturday, January 14, 2006

The Good, the Bad and the Ugly, Part 1

This is a detour from shell programming to the halcyon days of C programming, initially born of the necessity to investigate a surprising incident involving popen(), which however I am turning into a kind of mini-series. Consider the following code:


/*
popen-test: experiment with popen().

Copyright © 2006 Davide Bolcioni <dbolcion@libero.it>

Distributed under the GPL,
see http://www.gnu.org/copyleft/gpl.html.

*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char* argv[])
{
int rc = EXIT_FAILURE; /* Optimist */

if (argc > 1) {
FILE* sink;

fflush(NULL); /* all open files */
sink = popen(argv[1], "w");

if (sink) {
int status;
int i;

/* NB: deliberately allow to test for no output sent */

for (i = 2; i < argc; i++) {
fputs(argv[i], sink);
}

status = pclose(sink);

/* The return value is funny */

if (status == -1) {
fprintf(stderr,
"%s: pclose() failed.\n",
argv[0]);
}
else if (WIFEXITED(status)) {
rc = WEXITSTATUS(status);
fprintf(stderr,
"%s: child did an exit(%d).\n",
argv[0], rc);
}
else if (WIFSIGNALED(status)) {
fprintf(stderr,
"%s: child terminated by signal %d.\n",
argv[0], WTERMSIG(status));
}
else if (WIFSTOPPED(status)) {
fprintf(stderr,
"%s: child stopped with signal %d.\n",
argv[0], WSTOPSIG(status));
}
else {
fprintf(stderr,
"%s: I don't think we're in Kansas anymore, Toto.\n",
argv[0]);
}
}
else {
fprintf(stderr,
"%s: popen(\"%s\") failed.\n",
argv[0], argv[1]);
}
}
else {
fprintf(stderr,
"Usage: %s <command> [<arg>]...\n", argv[0]);
}

return rc;
}

/*
vim:sw=2:nowrap
*/


If you excuse the poor formatting of fprintf(), the above code seems to get the job done: it allows you to experiment with popen() by launching the command specified as first argument and piping the remaining command line arguments, one by one, into its standard input. The result of pclose() is then cracked using a few macros from <sys/wait.h>.

This is, however, bad code; it appears to work, fulfills the intended purpose and, because of this, tempts the casual onlooker into believing that it can be reused as is, or as an example of using popen(). If it didn't work, or didn't fit its initial purpose, it would be discarded with little harm done.

In subsequent entries, it is my intent to pick at its defects and address them one by one; what I mean to show over the course of the series, however, is that getting code that works and fulfills its intended purpose is only the beginning of the journey. Code should also be correct, by which I do not mean "proven correct"; for those of us who are not into correcteness proofs, "at least not sloppy" would be enough. After getting past the "not sloppy" milestone, the result will be ugly code (yes, uglier than that) which will need a dose of ... elegance to obtain good code.

Whether this author will be up to the task, of course, remains to be seen.