Operators, fixity, and annotations

Operators are functions with symbolic names. Fixity declarations tell the parser how an infix expression groups; types and annotations then tell the checker what it means.

Declaring an operator

function (+++)(left:Int, right:Int) : Int = left + right;
infixl 6 (+++);

Call it in either form:

1 +++ 2
(+++)(1, 2)

The parenthesized name identifies the function itself.

Associativity and precedence

infixl 6 (+);
infixr 5 (++);
infix 4 (==);

Precedence ranges from 0 to 9; higher numbers bind more tightly. Function application and projection bind more tightly than infix operators.

Published modules should declare non-standard fixity. The interactive recovery default for an unknown operator is left-associative precedence 9, but portable source should not rely on that fallback.

Parentheses communicate intent

2 + 3 * 4
(2 + 3) * 4

Fixity makes the first expression conventional; parentheses make the second grouping explicit. Add parentheses whenever mixed custom operators would make a reader reconstruct a precedence table.

Type-expression precedence

From loosest to tightest:

  1. forall and exists;
  2. ->, right associative;
  3. +;
  4. *;
  5. type application, projection, and parentheses.

Thus A -> B -> C is A -> (B -> C), and type application binds before the arrow.

Expression annotations

value answer = (40 : Int) + 2;

expression : Type checks an expression against an expected type. It helps choose overloaded literals, constructors, existential witnesses, or a higher-order argument.

An annotation does not convert the value. These are distinct operations:

Prefix operators

Prefix use binds below application/projection and above infix operations. Numeric negation is the common example. Parenthesize when a prefix operator and application could be read in more than one way.

Stable numerical meaning

Operator notation does not authorize approximate rewrites. In particular, floating division must preserve the selected numerical contract, and fused multiply-add contraction is controlled artifact-wide by --fp-contract, not by operator spelling.

Common mistakes

Do not depend on the recovery fixity for a published custom operator. Remember that an annotation checks, as uses a representation mapping, and downcast checks a class relationship—these forms are not interchangeable.

Recap

Operators are ordinary functions plus declared grouping information. Parentheses remain the clearest answer when either expression or type precedence would make the intended tree uncertain.

Normative details: Language Reference §7.7, §8.4, and §4.8.