2013-03-17
The power of suggestion that documentation has
DWikiText (the wikitext dialect of Wandering Thoughts) has a couple of different ways of writing links, less by design and more because of evolution over time. DWikiText started out with one form, later added a second that I felt was better, and then of course could never remove the first one because there were plenty of existing entries using it. When the second form was added I updated the DWikiText help to include a mention of it. More to the point I more or less tacked it on the end of the help text in appropriate spots. I didn't revise the help text or the examples it uses to make the second form the prominent form; instead, the first form kept on being the first one mentioned and the one used for examples.
(The two forms are [[text text text|URL]], the first and more awkward one, and the simpler one, [[text text text URL]]. In addition to being simpler to write I think that the second one plain looks better, although it doesn't clearly mark out that the last word is special.)
I switched to using the second form pretty much the moment it was available, but what I've recently noticed is that a lot of other people using DWikiText (either in comments here or on their own DWiki) are using the older, more awkward first form. While I can't know exactly why they're doing that, I suspect that it's because my help text lists the old form first. I'm guessing that people scan the help text for 'how to make a link', scan that section to find the first way that works for them, and then use it; they don't bother to read further and really, why should they? They've got the answer that they came for.
What this says to me is that documentation has much stronger and more subtle powers of suggestion than I was tacitly assuming. It's not enough to document everything; you (ie, I) have to structure things so that readers are led to the best or preferred options first. This makes total sense and I'm sure I've had related thoughts before (probably about other people's documentation), but I haven't looked at my own stuff through this lens before. I think I have some revising to do on the DWiki help documentation and I'm going to have to remember this for any future documentation I write in general.
(To be clear I think this is perfectly sensible behavior on the part of the people reading the documentation. People generally do not read our documentation as if it was a fascinating book that they want to fully consume. Unless you force them or write really stunning prose, why shouldn't they spend as little time as possible by skimming and focusing on just what they want to know? And forcing people to read all your deathless prose is not going to be highly appreciated by your audience, to put it one way.)
Argument validation using functions
There's a pattern (or perhaps an anti-pattern) that I keep inventing in my programs. I start out with a bunch of commands (or macros or template text renderers or the like) that can take arguments (registered somehow), and I have all of the functions do their own argument count validation. But this is repetitive, so I start having the central dispatching code do some checks on the argument count. But there are always special cases (one command might take exactly N arguments, another takes M to N, another takes at least N but maybe more, and so on), so pretty soon I start trying to encode all of this in increasing baroque special meanings for various sorts of argument counts ('if it's negative, it means...').
In thinking about this recently (as part of some DWiki changes I'm thinking about) I've realized another approach, hopefully a better one. Instead of trying yet another crazy encoding scheme, I can use functions to validate the argument count. Instead of registering the argument count, register a function that validates the argument count. These functions (or callable objects) will of course be created by argument count validation factories, so I will write code like:
register("fred", fredfunc, noMoreThan(3)) register("brad", bradfunc, betweenCnt(2, 4)) register("barney", barnfunc, anyOf(0, 1, 3, 5))
The great attraction of this approach to me is that it completely decentralizes the encoding scheme for argument validation (and thus the complexity of argument validation entirely). The central dispatch function simply calls the validation function and doesn't care any further; all of the huge variety of possible arguments necessary is delegated to the code that creates any particular validation function. I can have any sort of validation ranging from very generic to completely custom, whatever makes the most sense, and none of the complexity of that shows up outside of code that actually uses it.
This is also completely expandable. New forms of argument validation just need new functions, they don't need any changes in the central dispatch system to understand and handle yet another special case. This is an attractive property for me since I never know just what sort of arguments I'm going to need until I actually write a particular command (or whatever) handler.
Obviously, this can be extended to also validate various properties of the arguments (for example, you might know that the first argument of a particular command has to be a file). When you reach this sort of extended argument validation I start to think that you want something like an ArgValidator class which you instantiate and then start adding restrictions to (otherwise you have a rapidly exploding number of combinations of various options; basically you want some way of easily composing separate restrictions together instead of having to hard code them).