Why Write Open Source Libraries

Posted by Oliver on January 24, 2008

1. Exploration. I can sample platforms and sample stretch languages without sinking my stakeholders if I fail. Also, it’s easier to try something radical in a small, green field project than in a big one.

2. Altitude training (link TBD). I can make myself jump through hoops that I wouldn’t feel ethical asking someone to pay me to jump through. I did this recently with Sequentially; the next time I needed to simulate concurrent processes in a more serious context, it was a lot easier.

3. Encapsulating components. My answer to Steve Yegge’s problem is to refactor my code into libraries. When I write programs that run on Linux, or the JVM, or a browser, I get those features, but the code sizes of those libraries don’t really count against me. Why not do that with my own code, too.1

This was the motivation for LzOsUtils and Fluently (both didn’t get to projects), most recently: they were part of other projects, but they were respectively large/tricky enough that I wanted to be able to compartmentalize them, and think about them separately.

5. The Golden Rule. I’ve benefitted hugely from open source projects; why not give back a little?

6. You can take it with you! I’ve written things that I want to use in more than one project; this is easier if they’re in library form. That was the motivation for LzTestKit (another didn’t get to project), most recently.

7. Fame and fortune! Just kidding. You get more fame from working on one project (if it’s the right one) for a long time. And there’s more fortune in straight consulting.

8. More days. I’m with Joel and Leo Babauta (ZenHabits): sometimes I can only work productively for four hours a day. The trick is to have more days. If I’ve got another project I can switch to when I burn out for the day on the first one, sometimes I can get another work day out of it. (Another trick for getting another day to move from the home to the office or vice versa, or to switch cafes.)

1 Interestingly, the extra work to some code up so that I never need to look inside it again (docs, test cases, examples) and can remember how it works years later is exactly the same work that I need in order for someone else to use it. I use to think I could take a short cut on things I use myself, but with enough going on or enough years in between, it isn’t true.

The Programmer’s Food Pyramid 26

Posted by Oliver on January 17, 2008

Programmer's Food Pyramid

Update: (1) There’s a discussion (at the moment) on reddit. (2) Thanks to FusionGyro for suggesting the name change to “revising”.

Buy on Zazzle:
Poster
Mousepad
Coffee mug

“Stretch” Languages, or, 28 years of programming 11

Posted by Oliver on February 05, 2006

Recently I reviewed the programming languages I’ve used over the 28 years1 of my programming career. The result is shown in the chart below. (Click on the image to see it full size.)

There are some obvious trends here2. The languages are mostly getting higher level. There are a few “survivors”: languages that I’ve used over the the course of a decade, although discontiguously: C/C++, Common Lisp, and Java. Java has replaced C (except for a stint around 2000 where I went back to low-level graphics programming at Alphamask), and the scripting languages have taken over from Common Lisp — they’re slower, but they’re terse, have better libraries, and are easier to deploy.

It didn’t surprise me that there are so many languages. For one thing, I like languages, and I enjoy learning how to program by learning new ones. For another thing, times changes and programming languages get better. You’d have to be crazy to program in 1970’s BASIC these days. (That’s BASIC, not Visual Basic. The language I cut my teeth on didn’t have functions or structures.) And there just isn’t much call for Z80 assembly any more.

But why so many languages at one time?

The reason is that different languages are good at different things. When I first learned to program, I used BASIC for everything. Then I started using assembly for code that had to be fast, but I stuck with BASIC for doing my homework. (In tenth grade I wrote programs to multiply and invert matrices, so I wouldn’t be bored silly doing it by hand. It took longer, but I had more fun.)

This is what the second chart shows. Again, click on the thumbnail to see the full size version. And beware! — the colors don’t mean the same thing as in the chart above. (Sorry.)

I use four kinds of languages: utility languages, for writing tools and solving problems; application languages, for writing distributable integrated applications; system programming languages, for implementing operating systems and low-level libraries; and a fourth category of languages, which I’ll call “stretch languages”, for learning how to program. (I also dabble with bash for shell programming and R for statistics, but I don’t really think of the way I use them as programming.)

A utility language is useful for solving small problems and for exploring problem domains. It’s what you write your ten-minute program in. If you can use your utility language as your file manipulation language and your build language, all the better. (For a while I did a lot of programming in C and then Java, and I tried to use make and ant for these. Now that I’m using Ruby as my utility language, I’m a lot happier using Rake instead.)

An application language is for building larger, more full-feature programs that other people can use. (This is my own use of the term. Think “desktop application”, or “web application”.) An application language often has requirements for speed, OS integration, and deployability, that a utility language does not.

(”Application” for me used to mean desktop applications. Recently I’ve only written web applications — although I did write a MacOS app a few months ago, in order to learn about the MacOS toolbox. That’s why the “Desktop Application” category in the second chart trails off, to be replaced by languages for writing web applications, and browser-based clients that connect to them.)

Lastly, the point of a stretch language is to teach me new ways to think about programming. This is the opposite of a utility language, although a stretch language can turn into a utility language after I absorb its concepts. A utility language makes it easy to express programs that I have in mind; it gets out of the way, so I only have to think about the problem domain, and not how to program. A stretch language can make it difficult to do even simple things, because I’m still learning the concepts that are necessary to use it idiomatically or, sometimes, to use it at all.

Sometimes a stretch language becomes a utility language or an application language. This was the case with Common Lisp, which took me a while to understand, but for a while was my most productive language.

More often, the concepts that I learn from a stretch language are helpful in using other languages, even if I have to express these concepts by writing the equivalent of macro-expanded code or a VM. For example, I’ve written a continuation-based pattern matcher in Common Lisp and a logic program in Java, even though neither language supports those respective features. Designing the programs as though the features existed made a hard problem tractable.

And finally, learning concepts from one language makes it easier for me to recognize them and start using them when they’re available in a more mainstream language or one with better deployment characteristics. Those of us who were familiar with advise and the CLOS MOP had a leg up on understanding how to use AOP when it came around to Java. The modern round of scripting languages (Python 2.x and Ruby) have MOPs — if you’re familiar with them from Smalltalk, you know how to take advantage of them in Python and Ruby too. And if I ever have to use C++ again, at least I’ll know from Haskell to look for template implementations of combinator libraries.

Here are the languages that I’ve learned from over the years, and what I’ve learned from each one3. Some of the languages that aren’t in my list would be perfectly respectable stretch languages for someone else — I just happened to be familiar with their concepts by the time I got to them. For example, Python is probably a fine way to learn about OO (and my son learned a lot of OO concepts from Logo Microworlds), and I could have learned metaobject programming from Ruby if I hadn’t already learned it from Smalltalk and Common Lisp.

  • BASIC: basic programming
  • Z80: ADTs; recursion
  • Prolog: logic variables; abstracting over control flow
  • CLU: exception handling; coroutines; parameterized data types
  • Smalltalk: OOP
  • Common Lisp: functional objects; meta-object programming
  • Java: concurrency
  • Eiffel: design by contract
  • Haskell: lazy evaluation; type classes
  • LZX (OpenLaszlo): constraint-based programming

Right now my stretch language is Haskell. When I write in Haskell, I feel like a beginning programmer again. I can’t use it when I’m in a hurry, and I don’t use it when I’m more interested in the problem domain than in the craft of programming. Reading Haskell is like reading poetry (I’m very slow at it, and I can’t skim because the new-concept density is very high), and writing Haskell is like writing poetry (something else that takes me forever). As opposed to Python and Ruby, which are more prosaic, and Enterprise Java, which is more like a tax form.


1 I first learned program in seventh grade, from the TRS-80 Level I Basic reference. That makes me both (1) almost 30, and (2) innumerate.

2 In this entire essay, I’m quantifying over the languages I use and the way I use them. YMMV.

3 This list includes more languages than the charts do because it includes languages that I’ve never used commercially. I’ve only ever used Prolog and CLU for college projects, for example, but I learned a lot from them anyway.

Refactoring for Fifth Graders

Posted by Oliver on September 08, 2004

I gave Miles a set of Logo programming problems:
* sv 3 draws a square divided vertically into three columns
* sh 4 draws a square divided horizontally into four rows
* svn 3 4 draws a square with three columns and four rows

(These are going to build towards some work with fractions, but he won’t know that unless he reads my web site. Hi, Miles!)

The first thing he did was place a slider and a button on the screen. The slider ranges from 1 to 10, and the button calls sv@ with the value of the slider. He used these to test the program while he wrote @sv, to quickly try it on different arguments without typing. When he added sh@ he added another button, and so on for @svn.

This looked to me like some sort of hybrid between test-driven development (with a unit testing framework or FIT), and using the command line. It’s more parameterizable than unit tests, but easier to fit into a development cycle than the command line.

What was really interesting, though, was that Extract Method wasn’t a new concept. The initial implementation of sh@ was copy-pasted from @sv, and svn was copy-pasted from both of them. I started on my DRY lecture — “see how this part of sh@ is the same as this part of @sv” — and he jumped the gun. “Oh, cool, you can use aliases?!” he exclaimed, before I even modified any code.

The analogy is between files in a directory and callees in a method. If directory could transclude its contents — list one of its children’s contents as its own — then the analogy would be exact.

Aspect-Oriented Programming with mod_rewrite 1

Posted by Oliver on August 17, 2004

I spent part of my vacation last month working on my web site. One change I wanted to make was to put a banner on every page, and a directory-specific column on the left. Nothing fancy by web design standards, but an adventure for a hobbiest without a CMS.

The first time I had to make a number of pages with a common template was in 1995, and I wrote a Lisp program to generate them. The next time I made a web site, I used Barry Warsaw’s Python program to create some pages. (I still didn’t have access to a server with server-side includes.)

Last year when I moved my site to a modern server, I used PHP include to add navigation elements to some of the pages. But this time I didn’t relish editing a number of HTML files, and, more to the point, I was ready to try something new.

The Problem

Let’s say that software/index.html looks like this:

<h1>This is my software page</h1>
<p>Welcome to my software page</p>

Furthermore, let’s say that there’s a file categories.html in the same directory:

<ul class="categories">
  <li><a href="lisp">Lisp software</a></li>
  <li><a href="python">Python software</a></li>
</ul>

I would like a request for http://osteele.com/software/index.html to retrieve this content:

<html>
...
<ul class="categories">
  <li><a href="lisp">Lisp software</a></li>
  <li><a href="python">Python software</a></li>
</ul>
<h1>This is my software page</h1>
<p>Welcome to my software page</p>
...
</body>
</html>

where the <html>...<body> and ...</body></html> are generated by header.php and footer.php.

A conventional solution would be to modify index.html so that it included some header and footer material as well as the actual content:

<?php include('header.php'); ?>
<h1>This is my software page</h1>
<p>Welcome to my software page</p>
<?php include('footer.php'); ?>

Of course, then I have to edit all my other files too, and any new files I add. And if I ever change the header and footer includes, I have to edit them all again. (For example, it’s likely that include('header.php') will become include($_ENV['DOCUMENT_ROOT'] . 'header.php') — now how many files will I need to touch?)

The include lines are domain-specific boilerplate. Like all boilerplate, they distract for the content of my source files, they add to the overhead of creating and maintaining the site, and they’re a chance for something to go wrong.

It seems backwards to edit the inside of a number of files just to control what gets shown on the outside (before the beginning and after the end). If each of the files is treated the same, something outside the file itself should do the wrapping.

Wrapping with RewriteRule

I used the Apache mod_rewrite module to solve this problem. I configured my .htaccess file so that a request for a URL with the path /index.html is rewritten into a request for the file /wrap.php. Within the body of wrap.php, the expression $_ENV['REQUEST_URI'] evalutes to the original path, in this case /index.html. The code in wrap.php can do whatever it wants with this pathname — show the original file, show just its name, or process it in some more sophisticated way.

My .htaccess file contains the following:

RewriteEngine On
RewriteBase /
RewriteRule ^.*.html$ /wrap.php [QSA]

and wrap.php looks like this:

wrap.php

<?php
  $file = $_ENV['DOCUMENT_ROOT'] . $_ENV['REQUEST_URI'];
  $sidebar = dirname($file) . '/sidebar.html';
  include('header.php');
  if (file_exists($sidebar)): include($sidebar); endif;
  include($file);
  include('footer.php');
?>

This strategy has several advantages over modifying every content file. The includes can be added or removed from any number of files without editing each file (just modify the regular expression in the RewriteRule); the includes don’t need to be copy/pasted to each existing file and each new file; and the includes can be maintained separately from the content.

AOP

The problem that motivated this — how to inject behavior into a set of source entities without modifying each one — is the same as one of the motivations for Aspect-Oriented Programming (AOP).

A “Hello World” of AOP is the problem of adding timing code to a set of methods. Adding timing code by hand to a single method is, like the header/footer problem above, a simpe matter of wrapping the method’s implementation at the source level. A method such as this:

void f() {
  do_something();
}

is replaced by this:

void f() {
  long startTime = System.currentTimeMillis();
  try {
    do_something();
  } finally {
    Logger.logTime(startTime, System.currentTimeMillis());
  }
}

AOP lets us leave the method body source unchanged, and place the timing code somewhere else (in a method on Aspect or Interceptor, depending on the AOP framework.) The timing code has a hole in it where the original method is spliced in — this is like the parameter to a function or macro. The location of this hole is signalled in the source by the proceed keyword in AspectJ, the JoinPoint.proceed() method in AspectWerkz, or the Invocation.invokeNext() method in JBossAOP.

Using AOP to insert the timing code has three advantages over modifying the method source: the timing code can be applied and removed without modifying the source; the same timing code can be applied to a number of methods without repeating it; and the timing code can be maintained separately from the methods. These are the same advantages of the RewriteRule+PHP solution to wrapper pages.

RewriteRule and AOP

In the language of AOP, the code that is injected into a program is advice. The location where it is injected is a join point. A set of locations is a pointcut; pointcuts are generally defined as a pattern or predicate that selects locations from a program.

In the timing example, the timing code (long time = ..., etc.) is the advice, the f@ method is the join point, and the pointcut is a specification such as @call(void C.f(void)) (in AspectJ, and if f@ is a method on @C).

In the web page wrapper, the advice is wrap.php, the pattern in the RewriteRule is the pointcut, and the join points are the URLs that that pattern matches. The statement include($_ENV['REQUEST_URI']) in wrap.php plays the same role as proceed or invokeNext() in Java AOP: it specifies where in the advice to insert the code at the join point.

conceptJava exampleWeb example
advicetime = ...include("header.php");
pointcutcall(...)RewriteRule .*.html
join pointf()/index.html
holeproceedinclude

Limitations

RewriteRule, not surprisingly, doesn’t do everything an AOP framework does. One way it falls short is that it only implements advice, not aspects. (An aspect is a set of advices, designed to be applied in concert.) It only implements around advice — although given around, it’s trivial to implement before and after by placing the include at the beginning or end.

Perhaps most seriously, it’s not possible to apply multiple advices to the same file. (Or at least, I haven’t figured out how.) In the example above, I listed the rewrite rule as RewriteRule ^.*.html$ /wrap.php [QSA], and used $_ENV['REQUEST_URI'] in wrap.php to retrieve the original URL. Since my actual .htaccess contains other rewrite rules too, I actually use RewriteRule (.*.html)$ /wrap.php?file=$1 [QSA] to rewrite the URL, and $_GET['file'] to retrieve the original URL. Either of these methods only works for one rewrite per URL, though.

This limitation means it’s not actually possible to factor a number of crosscutting concerns out of a page — just one. Certain other concerns can be factored out because RewriteRule and RewriteCond can check request headers and test and set variables, but in general you’re stuck with one general-purpose solution (aspects), and a few special-purpose solutions for whatever mod_rewrite handles. Of course, you can always use traditional OO techniques in the server-side code that you do weave together, to handle everything else in a non-AOP manner.

Summary

There’s something of a dancing bear quality to this: .htaccess is enough like assembly language that you can implement an aproximation of something high-level in it! (I remember being thrilled by Z-80 assembly language as a big step up from Dartmouth Basic, because now I could use subroutines for the first time.)

What I find interesting is that this wasn’t the motivation at all. I didn’t decide “let’s port AOP to .htaccess, just to see if it can be done”. Instead, I implemented wrapper pages the laziest way I could think of (where “lazy” means I didn’t have to do anything repetitive, now or in the future), and the result turned out to be closely analogous to AOP.

The stages of web site generation techniques that I gave at the beginning of this post — write a program to generate pages statically, use somebody else’s static page generation program, generate the pages dynamically, and finally weave the code that does the generation together dynamically — recapitulates the history of programming techniques in general. First there were a few domain-specific special-purpose code-generators, then compilers for general-purpose static languages, then dynamic languages caught on, and finally — as the binding time for everything, including program construction, gets later — AOP.

Web programming really is web programming.

Instance-First Development 3

Posted by Oliver on March 28, 2004

LZX is a prototype-based language: any attribute that can be attached to a class definition, can be attached to an instance of that class instead. This is handy in UI programming, where there are a number of objects with one-off behaviors. It’s also handy in prototyping and incremental program development, where it creates the possibility for a novel kind of refactoring.

Continue reading…

A Taxonomy of Comments 5

Posted by Oliver on August 31, 2003

Christian Sepulveda writes about comments in source code:

Not all comments are bad. But they are generally deodorant; they cover up mistakes in the code. Each time a comment is written to explain what the code is doing, the code should be re-written to be more clean and self explanatory.

This statement is provocative and interesting, but wrong. There are more good uses for comments than bad ones. (The rest of Sepulveda’s posting is more nuanced, and much of what I write here expands on points he makes.)

Comments are an escape hatch for expressing everything about a program that the programming language can’t. Comments therefore don’t fall into a single natural category.

Rather than stating a single purpose or use for comments, one can start by stating the purpose of the non-comment portions of the source code. Comments are used for everything else, which is at least the following:

The Coding Compromise

Non-comment source code is a compromise among meeting the needs of two classes of consumers: compilers and runtimes on one hand; and human developers on the other1. The priority given to each of these consumers depends on the context of the program’s development and deployment. An example at the level of programming language selection is that I use C++ for performance-critical applications or deployment to resource-limited platforms, but for more readable programs I use Python. However, even within a specific context, the compromise is always present.

1 There are of course also tradeoffs within each of these audiences: for example, size versus speed for program execution; readability by domain experts versus programming language experts on the human side.

Comments don’t have to meet this compromise. A line of code may legitimately be biased towards program execution (the compiler audience), but the line of code plus its comment can meet the needs of the human reader as well.

As an example, let’s look at the inner loop of the scan convertor for a graphics library that I worked on. This loop is responsible for drawing spans within a polygon. A span is the portion of a scan line from the x position of an edge on the left side of the polygon’s interior to the x position of the next leftmost edge on its right. The definition of “interior” depends on the fill mode of the polygon. Since (for source size and code size reasons) a single function implements both fill modes, the way that this function computes the interior of the polygon is conditional on the fill mode.

If I’m implementing the computation for human readability, especially by someone who isn’t familiar with low-level graphics programming, the initialization code, and the code to handle an edge transition, might look like this:

bool inside = false;
int windingCount = 0;
if (windingType == kEvenOddFill) {
   inside= !inside;
} else {
  windingCount += edge->direction;
  inside = windingCount != 0;
}

(I I cared more about abstraction and flexibility, I might even turn windingType into an instance of a class with an update function:
windingType.updateState(edge);
inside = windingType.isInside();)

The same functionality coded for runtime efficiency might instead look like this:

int windingMask = (windingType == kEvenOddFill) ? 1 : -1;
bool inside=false;
int windingCount = 0;
inside = windingCount & windingMask;

The efficient code uses integer/bitfield and integer/boolean puns which wouldn’t even compile in a strictly typed language, but which take advantage of the implementation of values of these types in C++ to optimize the performance characteristics of the program. (It also uses a clever hack that I was proud of at the time.) These tricks turn the process of reading the source, however, into an exercise in reverse engineering.

Comments ameliorate this reverse engineering process, by speaking sotto voce to the human audience:

// windingMask tells which bits of the windingCount to test.
// For even-odd fill, test the low bit, to tell whether the number is odd.
// Otherwise, test all the bits, to tell whether it's non-zero.

The Limits of Expression

Another reason for a comment is to compensate for the limits of the programming language at hand. There is often no way to express the design of the program within the syntax of a particular programming language. Languages in mainstream use have become moderately good at abstracting over data structures; they’re less good at abstracting over types, control structures, definition patterns, or patterns of composition, and they generally lack means of describing design patterns or architecture2.

A comment can add information about a program’s design, such as that the program implements the Pipes-and-Filters architecture, that a class implements the Flyweight Design Pattern, that a declared float represents feet, or that a declared String may be null. There are languages which can express each of these facts directly, in which case the comment is superfluous, but chances are you aren’t using one of them.

2 Haskell is good at expressing abstractions over types; C++ is better than Java. Languages with either a lightweight syntax for closures (aka blocks), such as Smalltalk and Ruby, or with structural macros such as JSE, or with both such as Dylan and Scheme, can express abstractions over control structures. Languages with definition-level macros or modifiers such as Bigwig, Dylan, Lisp, or Elide can abstract over definition patterns. Architecture languages such as ArchJava are still in the research stage.

Levels of Abstraction

Source code is typically written only at one level of abstraction. It may combine different units of structure or composition, such as methods, classes, and packages, but the source code typically doesn’t contain both statements that express something in a high-level or coarse-grained way, and the same thing in a low-level or fine grained way. (An exception is invariants in Eiffel, which express the abstract what as well as the concrete how.) After all, this would be redundant, from the perspective of the compiler.

The human reader, on the other hand, would like to be able to understand the purpose of a code block, function, or package without reading its implementation. Different audiences care about different levels of detail, and at different times. An API user needs a description of a method’s external behavior; someone working on the implementation of a method needs a high-level understanding of its implementation or algorithms as a roadmap of the implementation, maybe an overview of what each block does, but a line-by-line understanding of a particular block within the method at only a particular time. Source code is either there or not, and often can’t be easily skimmed; comments let the reader turn the dial to positions betweeen no information and too much.

Program as Palimpsest

A comment is a note from the past to the future.

I write a comment wherever I’m afraid someone might change the program for the worse. One reason this might happen, discussed above, is because the program’s design isn’t evident in the source. Another is that the reason for an implementation decision comes from data that isn’t present in the design. Corner cases and performance metrics are two kinds of data that aren’t present in a program’s design. Changes made in response to these data frequently result in code that’s suboptimal from a readability perspective, and that in the absence of that data would appear to be redundant. In the absence of information to the contrary, this code should be optimized out, for performance and code size reasons as well as from a readability perspective. Comments are a protection against this form of regression.

For example, I’ve written code like this:

if (shape.bounds.contains(point) && shape.contains(point)) ...

First this code tests if the bounding box of the shape contains point; if it does, it tests if the shape itself contains point. The first test is redundant. It’s extra source text to read, extra code to maintain (if I rename “shape” or “point”, there’s one more subexpression to edit), extra code to deploy, and it breaks the Law of Demeter — it increases the coupling between this code and the implementation of the class of shape. As it stands, in my book, this is bad code.

Here’s a non-executable line that, prepended to the example above, changes it into correct code:
// On a 400MHz P5, testing shape.bounds speeds text tracking by 10%

Without this line, there’s nothing to indicate whether the code is the result of premature and possibly misguided optimization, or whether it actually provides some benefit to compensate for its readability and maintainability shortcomings. With it, the source code records its own history; if anyone decides to remove the hard-won optimization, it won’t be out of ignorance.

Joel Spolsky has written an essay about corner cases. I’ll just add that corner case code looks like optimization code: without a comment it’s hard to tell whether it’s contingency or clutter.

Time Bomb or Time Capsule

Comments are also a place to put wishlist items and to-dos. Some of these may be “mistakes in the code”, but an item can be a mistake in the code without being a mistake in the software development process. The point of software engineering is to make tradeoffs between implementation speed, implementation resources, deployment resources, and code quality. Sometimes putting all the emphasis on code quality or feature completeness is the wrong way to make the tradeoff — for example, when bytes count, when days count, when the code is throwaway and the corner condition will never be met , or when the enhancement isn’t yet useful.

When the developer knows that a corner case is present or that an enhancement is possible, comments add a choice between implementing it immediately on one hand, and ignoring it altogether on the other. The first alternative is expensive immediately, because of its affect on the schedule and its distraction from work implementing the more important use cases. The second is expensive later, because it loses information that’s available now and that may have to be recreated then.

A comment at the implementation site can document the cause of the error in implementation terms, or collect notes about how the missing condition could be handled. The comment captures knowledge from construction time, when it’s available, and saves it for maintenance or enhancement time, when it’s useful.

The Path to Deployment

Conventional programming languages define program execution, but don’t express information about activities that occur before the deployed program is executed. Other activities than execution require other artifacts. Development requires examples. Testing requires tests and contracts. Deployment requires packaging information.

These artifacts can be placed in separate files. Often they’re ephemeral, such as a test case that is typed into the command line and then discarded. But it’s convenient to keep these artifacts close to the program units that they describe, so that they can be updated and reused as the program evolves. Many of them are also useful to the human reader trying to understand the program: for example, the examples and tests that drive and verify development make useful API documentation too. Capturing these artifacts in comments is a way to do meet these needs.

(All of the other uses of comments are a special case of this point. Design notes and explanations support construction, maintenance, and white-box testing, which are all non-execution activities.)

The Third Consumer

I wrote at the beginning of this entry that there were two consumers for source text: humans, and the compiler. I lied. There’s a third class of consumers: comment processors, which treat comments as source code in an supplementary language — a language that fills gaps in the coverage of the source code’s primary language.

Tim Peters’s doctest treats comments as unit tests. iContract reads comments for pre- and post-conditions. XDoclet is an extensible processor, preconfigured to read packaging and deployment descriptions. These tools address the non-execution activities of software development.

SPARK is a generic parser that reads BNF productions from the comments of methods that their semantic actions. It patches the failure of Python to abstract over defining forms.

All of these functions of comments as metadata could be implemented by extending the language instead. (Contracts are from Eiffel, for example, which supports them in its core syntax.) However, the use of structured comments as metadata lets a third-party tool address a gap in the language in a way that leaves the source structure intact for compilers and other source processors (such as editors, source debuggers, and refactoring tools) that wouldn’t recognize an extended grammar. Just as comments are an escape hatch for human readers, they’re an escape hatch for development tools as well.

And these two kinds of comments — natural language and structured metadata — are related. They both add information that can’t be expressed in the primary programming language. And they’re both useful to humans too.