I've got a Django review in the pipe, and it's generally positive. It's still cooking both so I can gather more experience, and while some fact-checking occurs.
But there's something still missing that I can't put my finger on. I think cutting away even more of the general cruft of making web apps is bringing it out. I've been programming on the web for nearly ten years now, and something's not right. Despite the fact that I have gotten generally good at programming, and I keep refactoring and refactoring, I keep writing the same web page over and over again: I've got a tree of heterogeneous objects that I need to render, which itself a view of a graph-like structure, I allow the user to manipulate it somehow, and I have to propagate those changes back to the database. And for some damn reason, no matter what I do, there's always something super-special about this form that requires special treatment and requires further extension of the frameworks or libraries I'm using.
(Note this programming post meanders a lot; part of the point is that I don't entirely know where I'm going with this.)
The problem I see with most introduction to closures is they choose examples that can be done easily with a standard for loop, or some other standard construct, leading a student to ask (with great justification!) what the point is and why (s)he should bother. In my opinion, the best place to start understanding them practically is to look at their ability to decouple logic, because there they have benefits that are difficult or even impossible to replicate without them.
So despite the huge amount of verbiage on the net about closures, I thought I'd take another crack at explaining it with examples that actually do something that is much harder to do without closures.
Every program of any complexity needs to be configured. The traditional answer is to put this in some sort of text file, or something else that is fundamentally static data that must be read in and parsed. (That is, for the purposes of this discussion the Windows Registry is a static text file.)
If you are programming in a language that can take a program in at run-time, like Python, Ruby, or Perl, and you are targeting an advanced audience, the configuration file should actually be a program. In the degenerate case, the programming language can easily look like a configuration file, but then, if you need the full power of programming, it's sitting right there.
Update Dec. 24th: Similar thoughts w.r.t. build tools. I think declaration-based systems tend towards "demoware"; any arbitrary task can be made to look good in the demo of a declarative system, but it seems inevitable that before you can even blink, you need a scripting language. And ultimately, as with ant vs. rake, it's simpler just to pull in the full scripting language that to fall prey to Greenspun's Tenth Rule in your "simpler" declarative language.
I've released NonMockObjects 0.2.0.
This release adds easy support for obtaining many variants of an object, which allows you to easily test that all combinations of some parameters have a certain invariant.
For instance, my Entry objects for this weblog can optionally contain a title, a link for the title, and a summary. The only constraint on that set is that a link affects a title, so you really need a title for the link to make sense, although it is permissible to have a link without a title in the database. By setting up my Entry creation function to indicate that each of those can either be None or contain a certain concrete string, I can obtain all 8 combinations of an entry with:
for entry in data.variations_entry():
And then perform some tests, such as verifying that no code crashes and that the RSS renders all combinations correctly.
You'd be surprised what such combinatoric testing can reveal. I think such combinatoric testing is a powerful argument in favor of automated testing; you already can't possibly run manual tests as often as you run automated tests, but testing umpteen combinations of parameters really highlights the difference. Manually testing what can rapidly become hundreds of combinations of parameters can be impossible, even once at the end of a project, and you certainly aren't going to do it repeatedly, let alone every time the code changes.
Obviously you have to be careful not to go overboard and accidentally specify a few million combinations, but so far I have not found that to be a problem in practice. If you have an object that truly has a million distinct behaviors, you've probably got a design problem.
The unit testing on this weblog's code still isn't quite up to par (I just now noticed I have no category index pages, whoops!), but it'd be even worse without the support of NonMockObjects.
If there's going to be a 0.2.1, it'll probably add the ability to request the exact set of parameters that was used to create the object along with the object, that is, code that looks like the following:
for object, creation_parameters in data.variations_entry():
where creation_parameters is a dict containing the concrete parameters used to create the object this time through the loop.
I'm still trying to work out whether this constitutes an antipattern or not. The argument against it is that you really ought to be able to easily re-discover the parameters by examining the returned object, and if you can't, it might be a sign that your creation functions are too complicated. The argument in favor of it is "complexity happens", and might still be easier to work with the parameters directly. So far I've always been able to examine the returned object.
For full documentation on the process of creating and using variations, see the Variations header in the API documentation.
I'm a little late to this party; only today and to a lesser degree yesterday is my typing speed high enough that I feel I can "afford" a weblog post... and even this otherwise pointless paragraph :-) (My previous post on switching to Dvorak was justified as a typing exercise and easily took over two hours to type. This one did too in rough draft but you see how much better I'm doing :-) )
|<- Future Posts|