Monday, March 31, 2008

Operators

It is fairly easy to design a flexible language. You can create any object, module, class, or function you like in most programming languages. JavaScript 1.x only has objects and functions, but can mimic the other constructs easily enough. Methods in Java are fairly limited compared to other languages, but you can emulate them easily enough with functors. Objects don't exist as a core port of all languages, but in most you can do things in an object-oriented fashion -- it just is a matter of how much you have to fight with the language to do so.

The point is, all of these constructs are open. I can create as many classes, objects, and functions as I like.

This is not true of operators. Operators don't fit smoothly in with the rest of the language's design. Because of their unique behavior, they must be fixed at the moment that a program is parsed. This means that they are almost always closed.

There are some interesting exceptions, however. Many languages allow operator overloading. This allows you to change the behavior of an operator and, more importanly, it allows you to apply operators to new classes. (If Java had this feature, you could add two BigIntegers with '+').

This is only a partial solution, however. While it does let you expand the usage of an operator, you still cannot create a new one.

The Lisp family takes a novel approach to this issue by simply not having any. As a plus, the core of the language(s) remains small, clean, and elegant. But there is a trade-off. Now, instead of being able to type:

42 + 3/4;

You have:

(+ 42 (/ 3 4))

Lisp advocates will be quick to argue that Lisp's form is unambiguous, and often more concise than the infix notation that we are all familiar with. This is why Lisp people never get invited to any parties.

Scala, however, has cleverly done away operators and yet still maintained the **appearance** of them. It does this by making both parenthesis and the dot optional. As a result, this works just fine:

scala> 42 + 3/4.0
res2: Double = 42.75

Despite the appearance, there are no operators. This is essentially the same statement:

scala> 42.+(3./(4.0))
res4: Double = 42.75

The genius of this is that operators essentially become an open class. However, what happens to the rules of precedence? That is another major complication of operators. If we reverse the order, we will most likely get 3./((4.0).+(42)), right?

scala> 3/4.0 + 42
res3: Double = 42.75

Hmm. So what is happening here? Well, the answer is that Scala looks at the first character of a method. If it starts with '+', it is lower in precedence than a method starting with '*'. With some clever tricks, Scala has operators that are both open and intuitive.

So is Scala perfect? No. While it has essentially made operators an open class, it still has fixed rules for the precedence. As a result, some things are missing in Scala.

scala> var i = 4
i: Int = 4

scala> i++
:5: error: value ++ is not a member of Int
val res6 = i++
^
The reason for this is that the results would not be what you would expect. Consider this pseudocode statement:

x = 5
y = 7
x = x * y++

At the end of this code, you would expect x=35 and y=8. But in Scala (if the ++ method existed) it would translate to:

var x = 5
var y = 7
x = (x.*(y)).++

While Scala has made an impressive step, the problem is still not totally solved.