Metaprogramming JavaScript, Part 2
Wednesday, May 2, 2007
In part 1 last month, I introduced a common UI scenario - dynamically showing and hiding form fields. The initial implementation for this functionality left much to be desired, though. The rules for which fields to show and hide were intertwined with the implementation of those rules, making it difficult to read and maintain. My goal in this part of the tutorial is to extract those rules into a JavaScript DSL (domain-specific language).
I should note that I regret using the term “metaprogramming” to describe what I’m doing here. Metaprogramming is becoming a bit of a buzzword that doesn’t have a clear definition. The most common definition I run across is dynamically generated code, or “code that writes code”. That might be technically correct - I have no idea - but that’s not the definition I subscribe to. I recently read The Pragmatic Programmer for the first time, and they describe metaprogramming as programming with metadata. Metadata is used here in the loosest sense, meaning any data about how your program is supposed to run. So the idea is basically to separate the details (or metadata) from the implementation. As I mentioned previously, I prefer to think of this as separating the “what” from the “how”. Okay, enough jargon. Let’s write.
The hardest part of writing anything is knowing where to begin. For writing a DSL, I recommend starting out by thinking about the vocabulary used to describe your domain. Sometimes this is different depending on who is describing it, be it a programmer, user, business analyst, etc. Before making that assumption, though, stop and think if it must be the case. Why do all of these groups of individuals use different vocabulary when talking about the same thing? Isn’t that just a recipe for miscommunication, where true meaning gets lost in translation?
Instead, make an effort to find a vocabulary that works for all parties involved, so no translation is necessary. Often, this common domain vocabulary can be found in your specs/requirements.
show us-state when country is “United States”
show province when country is “Canada”
show Brutus when state is “Ohio” or “Michigan”
The requirements from part 1 are exactly what I’m talking about. Our DSL should stick as close to this language as possible.
Now, we could use this language verbatim. It’s certainly not executable JavaScript code, but we could put it in a text file, read it in and parse it with JavaScript. While that’s certainly possible, I don’t find it desirable. The code will probably be overly complex, and since the DSL is just plain text, it gives the impression of being as forgiving as English. English is very forgiving of small grammatical variations, but since we’re going to be parsing it as data, it needs to adhere to a set of rigid rules.
I prefer to write DSL’s that are actually executable code. Since they are written in a programming language, it is clear right off the bat that there are strict rules to be followed. The bad side of this is that a non-programmer probably won’t be able to write with our DSL. In practice, though, I don’t find that to be a realistic goal. If a programmer can write it, but *anyone* can read it (and perhaps modify it), I’m happy.
So how do we take our requirements and make them executable code? This is probably the hardest part, and very subjective. Two recommendations for doing this in JavaScript: you can chain methods together to create a sentence-like construct, and be daring with method names. Here’s what I’m talking about:
show("us-state-field").when("country").is("United States");
show("province-field").when("country").is("Canada");
show("brutus").when("us-state").is("Ohio,Michigan");
On its own, a method name like “when” or “is” would be ridiculous. But within the context of a DSL where methods are chained together, it makes perfect sense.
This is obviously just the beginning of our form behavior DSL. We’ve defined what we want it to look like, but we haven’t touched the implementation. I’d like to keep my articles relatively brief, so I’ll dive into that in part 3.

Comments (1)