Tuesday 5 August 2008

Why Not Five-teen?

My four-year-old son has just learnt how to count. As a newbie, he has not memorised all the numbers. So sometimes, he improvises and derives the number words that he needed. For example, when he sees '15', he would read it as 'five-teen'. He is OK with the numbers that are 'regular', such as 14, 16, 17... But he could not readily read the irregular ones all the time, such as 11 and 12.

At this young age, he has not been 'tainted' by all the variations and irregularities of the English spelling and pronunciation rules. It is natural that he follows the 'logical' way to come up what he thinks and seemingly to be correct.

The English language is full of exceptions and variations in its rules of spelling, pronunciation and grammar. These inconsistencies make English not an easylanguage to learn comparing to many others.

When it comes to computing languages and APIs, consistency also determines whether the language or API is easy to learn and use (although now people are talking about more advanced topics, such as fluency). Let's take GUI frameworks as an example.

When building graphical user interfaces, we need to put some widgets/components onto the canvas or another widget. So there is a parent-child relationship: the parent is the container, the child is the object being put inside the container. There are two ways of saying this relationship:

  1. parent.addChild(child)
  2. child.setParent(parent) or specify the parent in the child's constructor

Most GUI frameworks use the first syntax - such as Swing, Windows Forms, WPF, ASP.NET, GWT, Echo2. For example, in Swing:

JButton button=new JButton("Clear");
button.setActionCommand(command);
button.setToolTipText(tooltip);
button.addActionListener(this);
...
JPanel buttonPanel=new JPanel(new FlowLayout());
buttonPanel.add(button);

In these GUI frameworks, the same widget cannot be added to more than one container. If you did, then it either complains at runtime or gives undesired odd results. But the API allows you to make these mistakes and your code compiles without error. So this is an inconsistency in those framework APIs.

To fix this problem, SWT/JFace takes the second approach by using the constructor:

Composite buttons=new Composite(this, SWT.NONE);
...
Button clearButton=new Button(buttons, SWT.PUSH);

This way, you cannot mistakenly set more than one parent for the widget.

There are other languages and frameworks that take a third approach by creating the child widget inside the code block of the parent. For example, in Wicket:

public abstract class BaseAddressForm extends Form {
...
  void initForm() {
  ...
    add(new Button("clear", new Model("Clear")) {
      @Override
      public void onSubmit() {
        log.info(getModelObject());
        resetModel();
      }
    });
  }
...
}
Similarly, in JavaFX script:
...
bottom: FlowPanel {
  content: [
    Button {
      text: "Clear"
      action: operation() {
        model.selectedAddressIndex=-1;
      }
    },
...

The above are exmaples within a single framework. When we have to deal with multiple frameworks the problem becomes more obvious. Normally, we don't expect different frameworks/languages to follow the same rules. However, when one framework is an add-on/extension to another, our expectation changes. For example, GWT-Ext is an add-on to GWT. However, its API naming conventions are not consistent with that of GWT: in GWT the method name getText() is used to get the text label of a widget (e.g. a label, button, etc.); but in GWT-Ext the method is called getValue(). Also, in GWT, the Command is widely used (in a similar way to the Action class in JFace). However, in GWT-Ext event listeners have to be explicitly created...

Don't get me wrong, I love using GWT and GWT-Ext. But these little inconsistencies hinder productivity when using this mix.

No comments: