This site will look much better in a browser that supports web standards, but is accessible to any browser or Internet device.

Anomaly ~ G. Wade Johnson Anomaly Home G. Wade Home

June 25, 2006

The Importance of the Correct Notation

Several of the past few essays have focused on the notation aspects of programming languages. The major benefit of a notation is making what you want to say concise without losing it's power in the process. To use an example from an earlier essay, supporting the use of normal algebraic notation for working with matrices allows the programmer to work at a high level, without spending too much time on the details of the operations. Unfortunately, this conciseness of expression is both an advantage and a disadvantage of any particular notation. If the reader is not familiar with the notation, they can easily be confused by what is happening. As I said earlier, a powerful notation is useful for the expert, but harder for the novice.

Another interesting issue with notations is that the best notations are limited in scope. Trying to use a notation outside its scope leads to more confusion. A good bad example for me is the use of + by some languages to concatenate two strings. This notation seems relatively intuitive for most programmers and works rather well for many languages. Unfortunately, in some scripting languages, this notation causes a problem. I first ran into this in ECMAscript. When adding a number to a string that contains a number, what should be the outcome?

var str = "10";
alert( str + 0 );

Should the output be "100" or "10"? Since the + notation is used in two overlapping contexts, you can see a real cause for confusion. This problem would have been solved by using a different notation for concatenation. Then, this notation would interpret the string as the number 10 and add 0 to it.

Obviously, using the same notation for adding two integers and two whole numbers simplifies the use of the language. But, often using a different notation for different concepts makes the language clearer. By the same token, having different sets of notations for operations that are different is very handy. If the operation isn't in some way the same the notation should look different.

In the early '90s, C++ was becoming popular in the PC world. Borland C++ was a popular compiler in this market. Borland produced a set of container classes that used many features of C++ that were considered advanced at the time. Unfortunately, like most of us, the compiler writers did not have much experience with these features and they made some really bad notational mistakes. One of them was to use operator overloading to add items to their containers.

This turned out to be confusing because not everyone agreed what was meant by adding an item to the container. Did it add at the front, the back, or somewhere else? When using + to add to the container, the operator modified it's left hand argument. This is not how + works for integers. In a later version of the compiler, Borland did a very brave thing by completely changing the interface based on these experiences. They realized that they were using the wrong notation, and broke a significant amount of code by changing the interface to one that was more effective.

This is a good example of a bad notation making a feature harder to use than necessary. (I know some people will suggest that this proves operator overloading is a bad thing. I'll come back to that subject in another essay.) By changing the notation to be more precise, Borland made the next version of their containers easier to use correctly.

The important point here is that notations are like interfaces. Good ones make saying what you want easier and more correct. Bad ones are easy to misuse. More importantly, in both interfaces and notations, what is good or bad is determined partially by the context in which it will be used. In the + example above, the problem with using + for string concatenation was that ECMAscript also converts strings to numbers and back automatically. This generates an ambiguity in the notation. This ambiguity is caused by the context of the operation. In the containers example, replacing + with insert and append (I don't remember the exact method names, but they were something like this), allowed more precision at the cost of somewhat more verbosity.

The important point is to use the right notation for the job.

Posted by GWade at 05:03 PM. Email comments

June 17, 2006

Notation vs. Paradigm

Recently, I've been taken with the idea of programming languages as notation. When most people look at a programming language, they see syntax and possibly an underlying paradigm. I see the same, I just find the idea that each language also provides a unique notation for expressing ideas quite appealing.

Lately, I've been thinking about how notation relates to programming paradigms. In order to explore this topic, I need to begin with some definitions. Most programmers are familiar with one or two programming paradigms: the object paradigm, the functional paradigm, the generic programming paradigm, and so on. But, if asked, most could not explain what a paradigm is. The third definition of paradigm on Dictionary.com seems to fit our needs the best. This way of viewing reality definition definitely fits the way programmers apply programming paradigms.

The term notation is not used as much in our industry, so I would suggest the second set of definitions from Dictionary.com as being closest to what I mean: a technical system of symbols used to represent special things.

The key difference between these two is that a paradigm defines how you think about problems and solutions, and a notation defines how you write about problems and solutions. This leads to the obvious (to a programmer) question, are these two concepts completely independent. It's probably safe to say that the answer is no. (The dot notation used by many languages to make method calls is not very useful in a non-object oriented language.)

The next obvious question is how separate are they. Most of us a familiar with at least a couple of different object-oriented languages, since that is pretty much the dominant programming paradigm right now. Many of the differences between these languages are not about paradigm, but about notation. We often argue about:

  • "." vs. "->"
  • pointers vs. references
  • null vs. 0
  • garbage collection vs. RAII
  • messages vs. methods
  • interfaces vs. abstract base classes
  • inheritance-based polymorphism vs. duck typing
  • static typing vs. dynamic typing

The interesting thing is that in most cases, replacing one of these with the other would not change your ability to program with the language. None of them have anything to do with the paradigm. (Although I've heard people in some groups claim that a language can't be object oriented without: garbage collection, exceptions, message passing, or strong typing.) In reality, you can do object design and programming in assembly language if you choose. The problem is that the notation of assembly language is not well suited to the paradigm.

The key to a good notation is that it simplifies writing about your problems and solutions. As such, it may be worth suggesting that different notations may be useful for different problem domains and kinds of solutions. I also think that some people are more comfortable with some kinds of notation than others. Although we try not to admit it, often the choice of notation is more a point of personal preference (or habit) than of technical merit.

This is not to say that all notations are equal, some notations are more powerful or clearer than others. Even in the procedural paradigm, C is usually considered clearer than assembly. A really good example of the differences in notation is the implementation of a 2D matrix. In a language that supports matrices natively, you can add two matrices like this:

C = A + B

and multiply two matrices like this:

D = A * B

In some OO languages, you can build a matrix class and (with operator overloading) get an equivalent notation.

C = A + B
D = A * B

If the language doesn't support operator overloading, you might end up with a notation that looks like this:

C = A.add( B )
D = A.multiply( B )

Without some kind of support for user-defined types, the notation gets increasingly messy.

Now, if you do a lot of work with 2D matrices, the first notation is going to be important to you. It requires less typing, it is clear, and it's mostly what we all learned in school. If you haven't had need of 2D matrices since your last math class, the difference in notation will not be important.

This leads back to an important point. Notation determines how you write your solutions. It is possible to write object-oriented code in C. Unfortunately, the notation available in C does not support OO comfortably. The development of C++ (and Objective C and Java) made OO more popular by supplying the notation needed to support this paradigm.

The competing concepts of expressiveness and clarity are part of what defines a notation. Any time you are confronted with a new notation, the clarity point is driven home. A new notation is more difficult for you to use than one you know. On the other hand, as you become more comfortable with a notation, the expressiveness concept becomes more important. A good notation must be clear enough that new people can learn to use it, yet expressive enough to solve real problems.

Different languages make these tradeoffs in different ways. Just as importantly, different people have different tolerances for where they will accept the tradeoff. Some people prefer expressiveness in a language. They take the view that they will only be novices for a short while compared to the time they use the notation. These programmers are willing to trade a steeper learning curve (less clarity) for the power a more expressive notation gives. At the other extreme are people who prefer clarity over expressiveness. These people assume that most people will not be able to handle a very expressive notation. To this group, a simpler, clearer notation is more important than expressiveness because more people can understand it more readily.

Notice that this concept is independent of paradigm. Each notation in each paradigm makes these (and other) tradeoffs differently, based on the experience and biases of that language's creators. So, the next time you find yourself in a fight over which language is more object-oriented, you might want to consider if you are arguing paradigm or notation. It may not stop the holy war, but this viewpoint might at least confuse your adversaries.(<grin/>).

Posted by GWade at 02:40 PM. Email comments

June 11, 2006

Geek Gauntlets revisited

I recently finished reading Eric Sink on the Business of Software. I had read many of the essays before and, in general, I like what Sink has to say. I'll freely admit that he knows a lot more about the business of software than I probably ever will, and I'll normally take his opinion over my own on any subject in that area...However, I found that one of his essays (Geek Gauntlets) made some arguments that I didn't agree with. As I thought about it, I realized that the essay makes the same mistake he was accusing programmers of.

I would suggest you read his essay to be certain you understand his point rather than just taking my interpretation. Sink makes some good points and explains some realities of business very well in the process. The point where I believe he goes wrong are two examples of programmer bias. Let's start with the first:

Commercial Version Control

According to Sink, a programmer at Gnomedex once asked him why anyone would buy his commercial version control system when CVS is available for free. In the essay, Sink dismisses this person's question as a case of bias and states that we need to learn to see things through the eyes of the customer. He assumes that technical people just don't understand why a customer would ignore a good technical answer that is freely available. I find this reaction frustrating because I can see asking the same question. Not because I have anything against the SourceGear Vault software, but because I want to understand what the customer wants.

In all honesty, I don't completely understand the customer's decision to go with the commercial tool. But, I do understand some of it. The problem that the customer is trying to solve is only partially a technical problem (version control), it is also partially a business problem. Some of these requirements are important to know:

  • software must interface well with their current development environment
  • possibly large amount of revision history in a format compatible with Vault, but incompatible with CVS
  • knowledge and abilities of the customer's staff
  • corporate standards
  • features may be provided by Vault, and missing from CVS
  • customer support, including hand-holding
  • a corporate face on the product

Some of these reasons are technical, the CVS development team might want to consider making changes to support these features if the need is great enough. But most of them are non-technical issues that cannot be solved by any technical means. In many cases, the business reasons trump the technical reasons. Just stating that companies obviously make money in this field, therefore the question is ridiculous, does not help this programmer or a wider audience understand these issues. I would be interested in knowing which of these issues (and others) really seem to influence customer decision in his experience.

In many ways, those of us who choose technology for our livelihood find it hard to understand these kinds of issues. But, those in business are also often blind to technical issues. I wasn't there, so I don't know if the programmer's intent was obvious in his tone and Sink was justified in this viewpoint. But, I did find his (reported) reaction somewhat less than helpful.

Passport

In his next example, he describes a case where the company was considering the possibility of adding support for Microsoft's Passport into their software. One of their developers made the comment that "Passport makes my skin crawl". Sink argues that the developer's reaction was biased and unhelpful. He goes on to point out that if the customers want it, we need to lay aside our bias and implement the customer's requirements.

I remember looking at Passport some time ago, and what I saw made me agree with that developer. (I have not looked at it recently, so I don't know if those opinions are still valid.) I have also been in situations where the customer wanted something that I felt (or knew) was a bad idea. Following Sink's advice seems to imply that I should have just shut up and done what the customer wanted.

In one case that I can think of, I was working with a company that provided financial information from a number of sources on websites that we built for large customers. At one point, one of the customers demanded that I remove the copyright notice that identified one graph as coming from a third party. The customer really wanted this, but I was pretty sure that we couldn't do that. (I didn't know all of the details of the contract allowing us to display the data.) I refused and passed the information on to our project people.

After quite a bit of discussion and checking, it was determined that if I had done what the customer wanted, I would have placed my company, not the customer, in legal problems. Although this was not a technical issue, I applied my knowledge to overrule a customer's desires.

Although this does not seem to relate to the example in the essay, consider this. Let's say for the sake of argument that the developer had been correct and that Passport was a really bad idea. Let's also consider the possibility that he was overruled and that the Passport compatibility was added to the software anyway. Now imagine what would happen if a flaw in the Passport login system had compromised the customer's server allowing their repository to be accessed or damaged. What if the compromise had allowed unauthorized access to the corporate network. Who will be blamed? Do you think it will be the customers who asked for this functionality? Do you think it would be Microsoft? Or, do you think that Sink's company would take the hit?

Part of what the developer was doing was identifying a risk. He may not have done that in an business-like manner, but it was a risk to be identified. As such, it was entirely appropriate to be discussed in a meeting to determine if the technology should be considered. This risk should have been researched and determination made on whether it was a problem or not.

Conclusion

It might not seem like it, but I do agree with much of what Sink says in his essay. However, I think it might be worth considering that knee-jerk business reactions are no better than knee-jerk technical reactions. Sometimes we have to do things that we don't like to make the customer happy. Other times, we need to be able to use our technical knowledge to prevent customer requests from leading to later disasters.

In several of his other essays, Sink states that the technical knowledge is critical to running a technical company. Many of his decisions are based on both technical and business knowledge. The examples in this essay suggest that business understanding and customer desires must always overrule technical issues. I am simply arguing that the real answer is more complex than that. Any decision is a set of tradeoffs. Although we (as technical types) tend to favor the technical issues, we need to be aware that business issues are sometimes more important. Just as importantly, real technical issues sometimes cannot be ignored just because the customer wants it.

Posted by GWade at 02:07 PM. Email comments