December 23, 2012
Declarative programming, at least in the form I described last week and the week before, is by its very nature ambiguous. A computer can't really know for sure what I mean when I say "make this circle occupy 25% of the screen" because I haven't said anything about which 25% or what happens when I rotate the display or what if I have two screens or no screens?
Resolving these inherent ambiguities falls on the original assumptions made by the designer of the declarative framework. And declarative frameworks are made up of imperative code1, so that's what ultimately tells the computer how to draw my circle.
Take a look at my imaginary CSS example from last week. It might seem pretty definitive at first, but it leans on several implicit assumptions I made. You might share these assumptions, but the computer has some options for rendering the page2. For example, I neglected to specify that the left and right columns must be exactly the same width.
Let's say that I meant for the outer columns to have the same width, but my rendering framework renders the left column using only as much width as the contents require, and the right column using all the remaining width on the page. What now? How do I tweak my declaration to act in the way I actually want rather than the way the computer assumes I want? How do I tell CSS, even my awesome imaginary CSS, that the remaining width of the page should be split evenly between two separate columns?
I've seen a lot of ugly hacks in my day, but I think the ugliest hacks of all are the ones that try to coax non-default behaviour out of a declarative framework. This is the realm of negative margins in CSS, comically convoluted XAML control template specifications, key path sequences in Cocoa key-value coding, hand-optimized SQL querying in Rails, and so on.
Trying to make a declarative framework do what you want rather than what it expects is one of the most painful things I've ever experienced while sitting in front of a keyboard. And you can't just override the default behaviour unless the original framework designer gave you a specific mechanism to do just that.
So, do we just eschew declarative programming entirely and stick to layering progressively more abstract layers of imperative code on top of each other?
Maybe for now.
But declarative programming speaks to a bigger vision: an unrealized dream of what programming could be someday. Programming where we just sketch out a rough outline and let the machine fill in the details. Programming at a level of intentions and desired outcomes rather than menial shifting and sifting of data structures and algorithms.
10 MAKE THE WORLD EXACTLY HOW I ENVISION IT OUGHT TO BE 20 GOTO 10
Imperative control loop for a really great declarative program
In a way, declarative programming shares a lot with that other unrealized dream, artificial intelligence. Maybe they're actually two aspects of the same thing. And as with artificial intelligence, just because we're not there yet doesn't mean we should stop trying.
1. At the lowest level, all code executed by a computer is imperative. Everything eventually gets compiled to very specific, well-defined instructions along the lines of "move this bit of memory here" and "add 1 to the value in that bit of memory over there."
2. There are more ambiguities than this. I didn't explitly state that the columns should appear beside each other, either, so the computer could legitimately stack the divs vertically without violating my constraints.