Sunday, September 17, 2006

First Principles in Politics and Programming Languages

One of the advantages of not having gone to see the Black Dahlia last night (which looks very interesting!) is that I got Friedman's homework done. This is an enormous burden lifted. Friedman's class is my favorite (by far) this semester, but the work can be overbearing. A (now-departed) professor of mine used to say that getting a difficult program to run is better than sex. Friedman's class is some measure of evidence for that theory. I started the homework at around noon and finished near 11. Factoring in breaks and general time wasting, I'd say I put in about 7 hours on it. Sometime around 11 it was obvious that I had solved all the problems - the rest of it was just mechanical copying and pasting. But the mechanical copying-and-pasting was enormous fun. I was guaranteed to get the right answer at that point, and at each stage I could stop, run the damn thing, and pat myself on the back for my cleverness!

Friedman's class is supposed to be about programming languages, although the only one we ever use is Scheme. His argument is that he's teaching the general principles of programming languages, that we can go out for ourselves and see how these principles are applied in Haskell, C/C++, Java, et al. I'm sure this will turn out to be correct. But a naive viewer (which still includes me?) could be forgiven for thinking that the real point of the class is to spread the Gospel According to Scheme - or else to extoll the virtue of a particular programming style that starts from correctness and moves to efficiency. Neither of these (obviously) has to do with programming languages as a general subject (at least not directly), but they're nevertheless VERY USEFUL THINGS, and I'm learning more in this class than I have ever packed into a comparable amount of time in my life.

I actually finished the assignment proper a little earlier than 11 - but Friedman always includes some bonus problems that it's a good idea to try to do. What's interesting about the "bonus" is that it's usually a little easier than the regular problems - provided you understood the regular problems on a deeper, conceptual level. That's, of course, a big proviso. But your ability to do the bonus problems quickly is a nice benchmark for whether you're really getting everything out of the class that you can. This week I did, and programming the bonus problems was, as I said, just a matter of mechanical copy-and-paste transformations on programs I had already written for the other section.

Each of the bonus problems, it seemed to me, was meant to illustrate some of the points of the "proper programming style" aspect of the course. The first one was about the power of abstraction. We had to write four versions of essentially the same program for the four assigned questions. The first bonus question was just sort of a "wouldn't it be nice if you'd only just written one that implemented the other four?" And with some mechanical copying and pasting that removed the specifics of the implementation from the code, it was done in a matter of minutes. (There's a lesson there for linguists like Noah who think that the details of the interface have something to say about the underlying engine.) The second bonus program merely illustrated that we could then transform this representation into a different style, squeezing even more abstraction out. The second problem, in other words, was meant to hammer the "slow, stepwise, mechanical transformations from correct programs to correct programs" aspect - which is the most useful thing that Friedman has to teach, in my opinion.

After having done all this, I had about an hour before I could reasonably expect to be able to sleep, and I mostly spent it marveling at how nice Scheme is - specifically how easy it must be to write a compiler for it (compared to other languages). It's such a simple, elegant little thing! The minimalism is what makes it. Scheme doesn't offer you much, but what it gives you is all philosophically grounded in the theories of computation. You have absolutely nothing more than you need, but what you do have is chosen for maximum expressive power.

This turns a lot of people off. They're not used to having to construct their programs from the bottom up. Generally learning how to program involves learning some basic syntax and then seraching the web for all the fancy commands and packages that get you what you need. You put these pieces together, and then spend the rest of your time smoothing out how they talk to each other. One guy in the lab here - a Perl devotee - insists that langauges should be judged by their available libraries (see here for a similar opinion.), for example. And that's certainly legitimate. But I would rather have the principles straight before I built a bunch of random libraries on top of it.

It occured to me then that I think the same way about politics. Programming languages don't seem like a likely analogy, but I think if we stretch it a bit there's a kind of parallel between various programming language philosophies and various political philosophies.

Java is the left wing. It's the Social Democrat approach. Programmers can't be trusted. They make mistakes, fall into traps, etc. So Big Brother Java will be your benevolent guide. In exchange for giving him virtually all control over how you do things and what you can do, what you get in return is great support, and a smooth environment. All Java programmers are created equal -- because Java does most of the programming. It's programming for the masses - the great equalizer. No true aces, but no complete losers either. Trust Java to give you what you need, and you take what's appropriate for you. To each according to his needs. The "from each according to his ability" part comes from the wealth of applications in Java that these masses of people then publish on the web, I guess. And the cost? It's economic, of course, just like with real Socialism. You pay in general lack of freedom (you can only do what Java will let you do), and MEMORY BLOAT. All the layers and layers of bureaucracy built up ensuring that everyone programs the same way take their toll in gobbled-up resources. Memory cells that should be going to actual production (i.e. executing the algorithm) are taken up instead in enforcing the style. So we get equality and security at the price of freedom and efficiency.

C/C++ is the right wing. It's the Nationalist approach. There is no underlying philosophy, really, except that things should run as efficiently as possible, change is BAD, and we have to be realistic and remember that there's a machine. C/C++ rewards work. It's almost like it thinks there's some virtue in debugging - the more of it you do, the more worthy your program is. Oh yeah, and don't dare suggest that the design flaws are, well, flawed. That's the way we've been doing it for years, and if it ain't broke, don't fix it! Doesn't make sense? Quit your cryin', son! When I was your age, it didn't make sense to me either, but you didn't hear me bitchin' and moanin', no sir. I sat down at the keyboard and bled and bled until I made it happen. Pulled myself up by my bootstraps, and you can too. No complainin', just workin'. None of that foo-foo, namby-pamby, whiny programming philosophy bullshit. We didn't get to be the number one programming language in industry by thinking about first principles! We got here by giving people results. we're not interested in helping the programmer. Some programmers are better than others - and their code runs faster than others because our language doesn't bog them down making life easier for the little people that don't know what the crap they're doing. Program hard and get ahead, but don't tell me why it's good - just show me how fast it runs. That's C/C++. It's all hands-off - unless adding it would make things more efficient, in which case they do it (call it the "corporate welfare" escape clause). Taking things out that are stupid or don't work simply can't be done because no one knows how to go back and fix their code to reflect the change.

And then there's Scheme! This is the Libertarian langauge. One could say it has aspects of both the Java and the C/C++ styles, but it might be better to say it's really neither. Scheme is minimalist. It gives you only the skeleton of a programming language, really. The rest of it you have to build yourself. But the nice thing about the skeleton is that it's all philosophically correct. Everything in the system has a clear justification for being there. Scheme sat down and thought about what's fundamental to programming (the way that Classical Liberalism sat down and though about what's fundamental to politics), built a system on top of that, and added nothing else to the mix. Nothing in the language steps on anything else's toes. All the semantics are orthogonal, so to speak. Like Libertarianism in politics, Scheme is all about avoiding unintended consequences. Everything is transparent. It's like Java in that it's meant for programmers more than machines, but it's meant for idealized programmers, not the mass of actual programmers who show up to work every day. In political terms, it has equality before the law (you don't get any special advantage by knowing the machine you're programming for), but not equality of outcomes (it doesn't do the work of programming for you, like Java does). It's like C/C++ in that it doesn't change and it leaves programmers free to do what they need to do. It tries to provide the individual programmer with as much control/power as possible. But the reason it doesn't change is because it got the philosophy right the first time - it isn't to protect old code. And while it provides the programmer with with as much control/power as possible, it doesn't do so to the extent that newbies are shut out, or that code becomes semantically confused. Everyone has the same rights - there is no gaming the system (because there's no "system" to game, really).

One interesting way to stretch this analogy is to relate execution time with economic performance in the real world. Java/Socialism loses massively. It sputters and chokes while C/C++ and Scheme churn out useable, fast code. When C/C++ really works up a sweat, it can outdo Scheme. And I believe this is true in real life too. A rare, well-oiled, hard-working Nationalist system can outperform a Classical Liberal system - but only at very high cost in terms of work hours. Scheme programs will always run faster relative to the number of hours spent writing them, and even in the extreme cases where C/C++ performance is noticeably better, it isn't usually by very much. The transparency of the system that comes with Scheme is worth this small (and only occasional) payoff in terms of performance.

But what about over the long haul? The argument for Java, after all, is not that it's efficient today, but that improvements in hardware will eventually render the efficiency question obsolete. That is, the performance gap disappears over time. And that's more or less always been the Socialist argument in politics too. Right, they concede, only Capitalism can really create wealth. But we'll take a hybrid system that promotes social justice in exchange for rapid development. We all grow together, and the social foundation of society is solid for that great day in the future when we've accumulated enough wealth that the differences in our systems disappear. Once the world creates a critical mass of wealth, your Capitalist advantage will shrink, and the virtues of equality (in this case, a smooth and useable programming environment) will seem more important. We will still be able to build on our code then. Yours will be opaque and obsolete.

It's a decent challenge to Capitalism, I think. And indeed, I will eventually write a post on why, even though I hate Star Trek as a show, I think it provides a fairly accurate social view of the future. I do indeed think equality and material freedom will come. But I think Marx got it wrong as to how. It isn't that we go through a Capitalist stage, and then there's a revolution (Java) that enforces equality. I think what happens instead is that wealth accumulates to the point where it ceases to matter, in some sense, who owns what. We're already seeing the process playing out in software development. You have the option of paying $200 for Windoze, or you can get something almost as good (Linux) for free. And indeed, in terms of actual performance, Linux is already arguably better than Windoze. Windoze survives because of vendor lock-in. It doesn't sell its product based on efficiency so much as based on the behind-the-scenes information sharing it does with other bits of software people want - games and so on. It will eventually be overtaken. This is possible because production costs on software development are virtually nonexistent. A $2000 computer and an internet connection are all that's really needed. People do it for bragging rights. Obviously this doesn't work with the automobile industry ... yet. But there's no reason to believe it won't someday. Once the world has accumulated enough wealth, and robots do most of the physical work, there's no reason to believe that vehicle design etc. couldn't function in the same kind of way. Eventually, I think we do end up with a society somewhat like Star Trek. There aren't billionaires in Star Trek - but only because there don't have to be. Goods are so readily available (thanks to replicator technology, etc.) that it becomes meaningless to charge people for things. Everyone has what they need because there's enough to go around and it's possible to distribute it cheaply. Much as I like shows like Firefly and despise shows like Deep Sleep Nine as drama, as predictions of the future I think Star Trek is probably closer to the mark.

So the short version is, it could work. Java could be right that it eventually won't matter that they bog everything down and clutter things up with their inefficiency (hardware improvements stand in here as an analogy for the capital base). It still wouldn't be the right system to use, though. And that's because there's Scheme in the world. Beautiful, elegant, simple Scheme. It's also a programmer-oriented language, but not in the nasty, underhanded way that Java is. It doesn't cede control of programming to a handful of supposedly-benevolent designers who know better than you do what you "need." Rather, it identified early on what was common to programs, made a language based on those, and has changed very little (if, indeed, at all) since 1975. You can write code in Scheme in full knowledge that it will run 100 years from now. And there's no bloat. The code that you write is very close to the instructions the machine gets, so there's efficiency today and efficiency in every possible future. The standard isn't something that's enforced based on empirical studies of programmer errors. You can't lobby the Scheme Consortium and have your pet projects added in, in other words. And unlike with Java, results do require some work. You have to learn the language and the programming style. But this is something that everyone can do, and painlessly, if they put their minds to it. Meanwhile, because of Scheme's rigorous uniformity, it has the same advantage that Java has in terms of making code easily shareable between users. It's an elegant, useable, non-intrusive system. The best of both worlds and the worst of neither. It won't require vigilant maintenence of the platform on which it was built to keep it going in the future (unlike Java and C/C++) as machines change. As improvements in algorithms are discovered, no one will need to rewrite Scheme, though they definitely will need to rewrite Java.

And, well, I think you get the point. Like all analogies between unlike things, this one is probably replete with holes and counterexamples. Be that as it may, I believe that I am a natural "Schemer." As with most things in life, I prefer things simple, philosophically correct, as free from "unintended consequences" as possible. I like to be in control of what I'm doing, and I'll put in the extra work it takes to learn a system that leaves me in charge. My main complaint with Java is that it's oppressive. Certainly it's nice to use, but only if you're doing "approved" things. I feel like I'm not even programming anymore. And, really, who wants to cede control to that extent only for an (admittedly easily-programmed) end product that isn't even efficient? C/C++ I like (hey, I prefer the Republicans over the Democrats in politics too, though I'm a supporter of neither). But it really can be a giant headache sometimes. It leaves me the control I need, but I think it misses the point on a lot of things. The compiler is for talking to the machine, really. Sure, it's nice to have arcane tricks up your sleeve here and there when you need them - but you so rarely need them. If the compiler is good, then I shouldn't have to worry too much about what's in the hardware, and in any case, all the complexity feels ad hoc. In Scheme, I type in my algorithm more or less as is, and it runs as fast, or very nearly as fast, as can be expected. My code is transparent and easy to maintain. I don't have to pick through a lot of obscure details to see what's going on. The super-efficiency of C/C++ is nice, but not if it means slaving forever just to get things done (and even THEN not really knowing that it works in all cases).

Friedman's lessons are right - and they're lessons for life as well as programming. Start from first principles that you know are correct. If you need something else, get it by operations on these principles. Maybe it takes a little longer than it absolutely has to, but not by much. And anyway, correctness is more important than efficiency. Of course, you can get correctness on the cheap by letting someone else do it for you, but the long-term price is always too high. Stick to your guns, and "what is morally right often turns out to be politically expedient" (to quote a great politician).

Scheme it is.


Post a Comment

<< Home