A Programmer in King Arthur's Court

Occasional commentary about software, software construction, and the business of software.

Wednesday, March 14, 2007

Now I understand why government computer systems suffer so much at the hands of Internet-based attackers. For obvious reasons, I won't point out any specific product problems, nor modes of unsecure programming, nor personal names behind the observed behaviors. But here are some observations:

You wouldn't work on banking software without learning the industry first. If you try, you'll just make a mess of things. And if your employer demands that you try, then you'll serve yourself to go learn the industry as quickly as you can. And you have to assume that future maintainers will do the same. Otherwise, you'll just bog yourself down. You can't hope to program to the lowest common denominator. You have to build good abstractions and then build on the abstractions. Otherwise, you will cause more trouble and re-work than whatever meagre amount of good you may produce.

Software is not fire-and-forget. You don't just send in the twelfth infantry programming division and wait for the enemy to surrender. But there are people who will do the moral equivalent: hire and hope. Some mid-level manager decides that he wants to commission a system, and he has a buddy who cut his teeth writing code to support an abandoned obsolete product in the field. With no formal training and an ego the size of Montana, in comes this dude with two or three programming brain cells to rub together and suddenly people treat him like some kind of god. It all goes to his head as he puts together something which is wonderful in some ways but shoddy in many others. And good luck if he solicits requirements from end-users other than himself.

Scenario A: People will complacently use the new system until the environment changes or some underlying assumption is suddenly violated. Because the dude didn't have abstractions in place, it's not feasable to fix the system in any reasonable amount of time. People have to toss it by the wayside.

Scenario B: The low-caliber programmer from above fails to consider security, and some day some goofball decides to do something malicious. It causes havoc for a few days while people restore from backup tapes, and the goofball eventually gets caught and suffers the consequences. But now an entire division, department, or suchlike has lost a week's work and can't even trust the system they work on to be completely uncompromised.

Scenario C: Someone with a few cents worth of formal training, or the Abraham Lincoln equivalent, joins the effort. Watch out! There will be fireworks. These two people don't get along. One is in fear for his manhood the entire time, and the other is confident and secure in the certain knowledge that long years of research and study amount for something. The former might even have more knowledge of the field of interest, but he'll not dare to divulge that knowledge in the source code or anywhere else for fear of losing some misguided mighty-lingum contest. Therefore, he begins to make counterproductive demands such as "don't add abstractions" and "don't change my code". He questions every decision, and tries to find a way to make it wrong. Eventually, all competence walks away from the project, leaving only the king of an ever-shrinking hill, and the project is back to one of the other scenarios.

Scenario D: The buddy-cum-contractor decides to make the effort it takes to get along with and learn from his new collaborator without ever demanding to be right. This can't happen. Well, maybe it can, but I'll believe it when I see it.

So getting back to the core issue, computer systems are unsecure because the people who create them are not held responsible for doing a professional job. They're not held responsible because their bosses don't know how. And that's because they do not, themselves, understand how to evaluate the quality of work. If it's pretty blue, then it must be high quality. When that's the way systems are developed, it's no wonder that a handful of script kiddies wreak dread havoc on computer systems across the face of the continent.

Saturday, March 10, 2007

The Tao Algebra of Programming

I think it's possible to break a programming language into a set of communicating algebras. There's a procedure algebra which encompases the control structures we commonly think of, as well as exception handling. There's a mathematical algebra, which evaluates expressions in a referentially transparent manner, and there's a storage algebra, which handles data structures and value assignment.

Your typical OO language will try to cram custom data structure operations into the mathematical/function algebra, but really they belong in the algebra of storage and assignment. That's why you can access Java arrays with the square brackets, but you have to call an object method to read an element of a binary tree, and another method to write one.

C++ tries to improve on this by making it possible to override operators, but in my opinion it's terribly clumsy for the person who's doing it, and it doesn't really let you define your own new algebra, but only try to map your operations onto an algebra that C++ already has. This isn't always desirable, because things can get rather confusing. Consider, for example, a vector and matrix manipulation package. You'd like a "dot-product" operator, and a "cross-product" operator. Additionally, you would like the orthography to resemble what mathematicians use. C++ won't let you do that. APL and J are built around a matrix algebra, so they have less trouble in that particular regard, but this is supposed to be a comparison of principles.

Perl additionally provides an algebra of regular expressions, and an algebra of here-documents, and interpolated strings of text, and so on. I firmly believe that it's access to these additional algebras that makes Perl a more expressive language than many others.

Python lacks a true algebra of regexes, and delegates the processing of regexes to an element of something it does have: Classes and objects. So Python is good because its object algebra is well-developed, and leaves Perl in the dust. Furthermore, Python's control algebra happens to be much prettier and less error-prone than the control algebras of the curly-brace languages.

You're less likely to make a mistake when communicating an idea if the idea fits naturally within an algebra of your language than if you must first cast the algebra of the concept into a form acceptable to some other algebra. You can also read the result more easily if you don't have to do the reverse transformation.

Until now, I think most programming language developers have focused most of their attention on getting the best set of algebras. Lisp seems to have only a one algebra: that of the lambda calculus. That makes Lisp programmers few, proud, and illegible. Forth posesses only the algebra of the stack machine and the input stream. That makes Forth programmers fanatical but few in number, and it makes their programs both agile and fragile.

In fact, Lisp and Forth are both very powerful languages, and largely for the same reason: They offer programatic access to the input stream. In Lisp, you get it pre-parsed into list format, while in Forth, you get the actual non-terminal symbols in a sequence. Both of these facilities enable the programmer to create arbitrarily powerful metawords in these languages. On the opposide extreme, C++ now has templates, and C++ templates are Turing-complete. With effort, you can create amazingly powerful metawords in C++ with highly magical properties. See, for example, the book "Template Metaprogramming in C++" for a taste.

Yet, for all this power, none of these languages offer the true ability to extend their algebras. No matter how many macros you write, you'll not write matrix algebra in Lisp, C++, or Forth.

That programming language will ultimately be successful which finds a way to allow custom algebras to be written to plug together in semantically meaningful ways.



Peace be upon you.