[Next] [Previous] [Up] [Top] [Contents] [Index]

2.6 Precedence Declarations

2.6.1 Previous Approaches

Most languages restrict infix operators to a fixed set, with a fixed set of precedences and associativities. This is not appropriate for Cecil, since we'd like the set of infix messages to be user-extensible.

Smalltalk defines all infix operators to be of equal precedence and left-associative. While simple, this rule differs from standard mathematical rules, sometimes leading to hard-to-find bugs. For example, in Smalltalk, the expression 3 + 4 * 5 returns 35, not 23.

Self attempts to rectify this problem with Smalltalk by specifying the relative precedence of infix operators to be undefined, requiring programmers to explicitly parenthesize their code. This avoids problems with Smalltalk's approach, but leads to many unsightly parentheses. For example, the parentheses in the following Self code are all required:

(x <= y) && (y <= (z + 1))

Self makes an exception for the case where the same binary operator is used in series, treating that case as left-associative. For example, the expression

x + y + z

parses as expected in Self. Even so, the expression

x ** y ** z

would parse "backwards" in Self, if ** were defined. (Self uses power: for exponentiation, perhaps to avoid problems like this.) Also, expressions like

x + y - z

are illegal in Self, requiring explicit parenthesization.

Standard ML [Milner et al. 90] allows any operator to be declared prefix (called "nonfix" in SML) or infix, and infix operators can be declared left- or right-associative. Infix declarations also specify a precedence level, which is an integer from 0 (loosest binding) to 9 (tightest binding), with 0 being the default. For example, the following SML declarations are standard:

infix 7 *, /, div, mod;
infix 6 +, -;
infix 4 = <> < > <= >=;
infix 3 :=;
nonfix ~;

SML also provides special syntax to use an infix operator as a prefix operator, and vice versa.

A fixity declaration can appear wherever any other declaration can appear, and affect any parsing of expressions while the fixity declaration is in scope. Fixity declarations can be spread throughout a program, and multiple declarations can add independent operators to the same precedence level. Fixity declarations in one scope override any fixity declarations of the same operator from enclosing scopes.

One disadvantage of SML's approach is that is supports only 10 levels of precedence. It is not possible to add a new operator that is higher precedence than some operator already defined at level 9, nor is it possible to squeeze a new operator in between operators at adjacent levels. Finally, all operators at one level bind tighter than all operators at lower levels, even if the programmer might have preferred that expressions mixing operators from completely different applications be explicitly parenthesized, for readability.