Sunday, February 22, 2009

Minimal Abstraction

Ask yourself: don't you often have the feeling that your brand-new 1024-core desktop SUV with 4 TB RAM and hard disk space beyond perception takes aeons to boot or to start up some application? (If the answer is no, come back after the next one or two OS updates or so.)

I don't want to rant about any particular operating system or application—the choice is far too big. Still, honestly, one thing I am often wondering about (and I guess I'm not all alone) is why modern software is so huge and yet feels so slow even on supposedly fast hardware.

All those endless gigabytes of software (static in terms of disk space consumption and dynamic in terms of memory consumption) and all those CPU cycles must be there for a purpose, right? And what is that purpose, if not to make me a, well, productive and hence happy user of the respective software? There must be something wrong with complexity under the hood.

During that conversation about Niklaus Wirth with my friend Michael Engel in Dortmund which got me started about computer science's tendency to regard its own history too little, and during which I had also mentioned my above suspicion, Michael pointed me to an article by Wirth, titled A Brief History of Software Engineering, which appeared in the IEEE Annals of the History of Computing in its 2008 July–September issue. This article contains a reference to another of Wirth's articles titled A Plea for Lean Software, which was published in IEEE Computer in February 1995.

The older article phrases just the problem I pointed out above, in better words than I could possibly use, and it did so more than a decade ago. So it's an old problem.

Here's a quotation of two "laws" that Wirth observes to be at work: "Software expands to fill the available memory. ... [It] is getting slower more rapidly than hardware becomes faster. ..." According to Wirth, their effect is that while software grows ever more complex (and thus slow), this is accepted because of advances in hardware technology, which avoid the performance problems' surfacing too much. The primary reason for software growing "fat" is, according to Wirth, that software is not so much systematically maintained but rather uncritically extended with features; i.e., new "stuff" is added all the time, regardless of whether the addition actually contributes to the original idea and purpose of the system in question. (Wirth mentions user interfaces as an example of this problem. His critique can easily be paraphrased thus: Who really needs transparent window borders?)

Another reason for the complexity problem that Wirth identifies is that "[t]o some, complexity equals power". This one is for those software engineers, I guess, that "misinterpret complexity as sophistication" (and I might well have one or two things in stock to be ashamed about). He also mentions time pressure, and that is certainly an issue in corporate ecosystems where management does not have any idea about how software is (or should be) built, and where software developers don't have any idea about user perspective.

I must say that I wholeheartedly agree with Wirth's critique.

Digression. Wirth offers a solution, and it's called Oberon. It's an out-of-the box system implemented in a programming language of the same name, running on the bare metal, with extremely succinct source code, yet offering the full power of an operating system with integrated development tools. One of the features of the Oberon language, and also one that Wirth repeatedly characterises as crucial, is that it is statically and strongly typed.

Being fond of dynamic programming languages, I have to object to some the ideas that he has about object-oriented programming languages and typing.
  • "Abstraction can work only with languages that postulate strict, static typing of every variable and function."
  • "To be worthy of the description, an object-oriented language must embody strict, static typing that cannot be breached, whereby programmers can rely on the compiler to identify inconsistencies."
Well, no.

My understanding of abstraction (and not only in programming languages) is that it is supposed to hide away complexity by providing some kind of interface. To make this work, it is not necessary that the interface be statically known, as several languages adopting the idea of dynamic typing show. Strict and static typing in this radical sense also pretty much excludes polymorphism, which has proven to be a powerful abstraction mechanism. (Indeed, Wirth describes what is called "type extension" in Oberon, which is called "inheritance" elsewhere.) It is correct that static strict typing allows for compilers to detect (potential) errors earlier, but abstraction works well and nicely with languages that don't require this.

It is puzzling to read that an OOP language must be statically and strictly typed to be rightfully called an OOP language. Ah, no, please, come on! Even as early as 1995, there were programming languages around that one would have greatest difficulties to classify as not being OOP languages in spite of their being dynamically typed. Moreover, it is an inherent property of living systems (which the object-oriented paradigm has always strived to capture) that objects in them assume and abandon roles during their lifetimes—something which to capture statically is hard.

Finally, it is interesting to note that the successor of the Oberon system, A2, features a window manager that supports "possibly semi transparent windows". Do you see the irony in this? End of digression.

As stated above, I really share Wirth's opinion that there is too much complexity in software, and I believe this is still true today. What can be done about it? Regarding operating systems, we depend on diverse device drivers even more than a decade ago, so we need a certain degree of abstraction to allow operating systems to talk to different hardware. Regarding convenience and user experience, the occasional bit of eye candy makes working with systems undoubtedly more comfortable. We should still ask ourselves whether it's really, really necessary though, and perhaps concentrate on the really important things, e.g., responsiveness.

So what to do? I don't really have a definitive answer, but I believe that the idea of minimal abstraction is worth a look. The "minimal" in the term does not necessarily mean that systems are small. It means that the tendency to stack layers upon layers of software on top of each other is avoided.

Minimal abstraction is the principle at work in frameworks such as COLA (a tutorial is available here) or in the work on delegation-based implementations of MDSOC languages I kicked off with Hans Schippers. I also believe that the elegance and (in a manner of speaking) baffling simplicity of metacircular programming language implementations (more recently, such as Maxine) are definitely worth a look.

I am sure it is possible to avoid complexity as we have to observe it today, and to make software more simple, better understandable and maintainable, and I believe the above is a step in that direction.

No comments:

Post a Comment