Use Controlled Variables

This tip builds upon a another tip, "Restrict Access to Variables Using Four Categories and the Magical Number". If you haven't already looked at that tip, you may wish to do so now. That tip suggested four ways to categorize your variables so you can keep intellectual control over them. This tip explains a fifth category.

What makes variables tricky is the same thing that gives them their power, they change values. After all, when a symbol X represents a constant, X is easier to keep track of than when X represents a variable.

Of course, we cannot make everything a constant. On the other hand, we can make variables less variable.

In fact, in a purely functional programming language, there is no assignment statement. Once a parameter X is bound to a value when a function F starts executing, X keeps that value. This is a lot like being a constant, but X can have different values each time F is executed.

The original functional programming language was LISP. However, LISP didn't stay purely functional. Today, most LISP programmers seem to rely heavily on SETQ which is LISP's form of the assignment statement. LISP's mutation is not surprising. Functional programming is anything but popular. I won't advocate it here.

Another approach is to permit assignments to X but only under controlled circumstances. Here is a classification scheme for variable-like symbols X :

unchanging
X remains unchanged during execution of the program. If you can change X by re-compiling or by re-executing with a different parameter, that's OK. The important thing is that X is fixed very, very early in a very, very simple way and then remains unchanged.

unrestricted
X is an ordinary programming language variable without extra restrictions.

controlled
A small set of subprograms is permitted to have statements that can alter X's value. Before one of these subprograms has executed, X is undefined. After any one of them has executed X is constrained to satisfy some condition.

The subprograms with statements that can alter X's value are called its controllers. X is not passed as a parameter to its controllers -- they access X directly. The constraint on X is called X's invariant,. The controllers must be written so that the invariant is satisfied whenever they are not active.

When you are dealing with a controlled variable, you know exactly which subprograms may alter it and you know what to expect from it when none of those subprograms is executing.

Example

Here's a quick example. Suppose you are writing a program which

This program will require some respresentation of an output line. I'll place it in a variable Line and control Line with three procedures: clear_line, add_word, and shift_space.

My idea is to invoke clear_line whenever I'm ready to start a new output line. Then I'll invoke add_word each time I have another word to add to the line -- add_work returns a boolean, true iff it has been able to add the word. When the line is full, I'll adjust the spacing with shift_space. My algorithm for doing so will right-justify the line and spread out the extra spacing between words.

This idea is not fully developed. I've yet to decide exactly how shift_space will work. Will it automagically fix up Line? Or, will it simply shift blank space from the right to some place in the middle determined by an argument? If the latter, the subprogram which runs shift_space will need to be able to obtain information about the spacing in Line . I don't feel a burning need to solve this problem yet. What I'm doing now is nailing down the places where Line changes and the properties it has.

The property I'm counting on for Line is that it is always the same size: so many pixels high and so many pixels wide. This property is my invariant: the height and width of Line measured in pixels does not change.

The invariant doesn't tell you what the individual controllers do. It sets ground rules that have a strong influence on the way the controllers must operate.

The controllers aren't necessarily the only subprograms that deal with Line -- just the only subprograms that can change Line's value.

When you make a variable controlled you create two comments of this form:

  

X,Y and Z are controlled by ....  

Invariants affecting X, Y and Z are ...  

These particular comments would document three variables that are controlled by the same subprograms. Any number of controlled variables can be documented at once if they involve the same subprograms and invariants. The invariant (or invariants) for the controlled variables (or variable) can involve other parts of the system as well. It simply describes properties you can count on when none of the controlling subprograms is executing.

Object Orientation

When I read object-oriented documentation, I am often frustrated by the lack of detail. Objects will be named and methods listed. There may be a vague sentence telling what each method is for and, then again, there may not. Without any detail, the documentation seems to tell me nothing.

On the other hand, we all know that complete documentation is a lot of work and as I mention in Don't Write Comments that Lie ... complete documentation that isn't kept up-to-date can be worse than useless.

Controlled variable documentation is a way around this dilemma. With object-oriented design, it is your classes (or objects if you haven't any classes) that need documenting. Objects are the "variables" being controlled. Your documentation identifies which methods can change an object and some properties which the object can be trusted to have when none of those methods is executing. This documentation is only difficult to write if you haven't thought much of anything out. If you are the kind of programmer who programs first and thinks second, then start the documentation when the class or object is being used for the first time.

Advantages

As my digression on object orientation shows, controlled variables need not be global. They can be regional as well. (Variables which make up an object's attributes are an example of regional variables.)

Here's a list of advantages of controlled variables.

  1. Controlled variable documentation is much easier to keep up to date than traditional complete documentation.
  2. Controlled variable documentation is much easier to read than complete documentation. The documentation acts as a guide to the code not a parallel, and possibly incompatible, representation of the same thing.
  3. Controlling X forces you to handle X with more consistency than you might otherwise do.
  4. Controlling X organizes the way you debug errors involving X .
  5. Controlled variable documention is consistent with all programming languages: object-oriented, scripting, assembler, whatever.
  6. Controlled variable documentation is consistent with all design technique -- even that of "build it first".
  7. One reason for poor documentation, is the difficulty programmers have in finding a golden mean between saying nothing and saying too much. Controlled variable documentation provides a standard that busy programmers can follow.

Back to the Example

To show how flexible the concept of controlled variable is: suppose that the line formatting example does not have fixed size lines. Let line size be altered with font size and margin settings. To handle these complications, Line will be controlled by five procedures: set_line_font, set_line_margins, clear_line, add_word, and shift_space. All of these work with parameters Line_Height and Line_Width. The invariant is that Line_Height has the value set by the most recent set_line_font and Line_Width has the value set by the most recent set_line_font or set_line_margins.

Experiment

Here's a thought experiment to show you the value of formally controlling your variables. Look back through your work of the past couple months for a bug that involved a variable X having an unexpected value. This might have happened because of pointer or parameter errors. If so, find a different example.

In fact, you are looking for an example of error that occurred because you had one idea about X when you wrote one part of the program but a quite different idea when you wrote another part of the program. Such errors are not uncommon and debugging them can waste a lot of time.

Now, suppose X had been controlled. That means: as you created it, you wrote a comment listing exactly those subprograms that could change X and also a comment explaining the ground rules under which those subprograms could change X . As you wrote the code you made sure these comments were obeyed. If they could not be obeyed, you changed the comments and, then, rechecked all relevant code to see if the new comments were obeyed.

Would you have made your mistake?

I cannot answer that question. You will have to. If the answer is no, then you are starting to see the value of controlled variables. If the answer is yes, your programming error probably had to do with one subprogram not doing what was expected and not with a confusion about the general purpose of X. This is because the invariants you would have written about X describe its general properties, not the behavior of specific controllers.

My experience has been that I can understand and debug an individual subprogram more easily than I can understand and debug a set of subprograms. Moreover, when I have to deal with a set of subprograms, it is easier if I have written down some common characteristics they are supposed to have. In other words, documenting invariants early makes things easier for me without forcing me to write a lot of comments.

Controlling Variables Without Language Support

It can be difficult to control your variables without language support. For my own use, I have created a front-end to the noweb literate programming system that helps. Read the tip about literate programming and then, if you are still interested, check this reference to a subsection on export control in my documentation on the HL markup language.

Copyright and Permissions

Copyright, 1995,1996,1998 by J Adrian Zimmer

This tip is distributed to individuals free of charge from the Software Build and Fix web site. All other distribution (including but not limited to internal distribution within an organization and mirroring of any kind) is forbidden without written consent of the copyright holder.

Return to the top of this document.

Context  Some Tips for Programmers    Author J Adrian Zimmer  
Dated: September 1, 1995; Revised: Oct 07 1998