As well as named messages, Grace also allows operator symbols for binary messages. This allow us to use conventional symbols like + and -, and also allows the programmer to define her own operator symbols from single characters or multiple character sequences. Since the advent of Unicode, there are lots of characters to choose from.
Note that we are using the term binary message in the same sense as Smalltalk: a message with a receiver and one argument. The evaluation order is the obvious one: parenthesized sub-expressions first; then messages are sent left to right.
In the absence of parenthesis, it would be nice to leave the order of evaluation of a # b # d # e to the implementation: maybe the machine can do a # b and d # e in parallel? In that case, wouldn’t it be nice to allow it to do so? The problem is that this makes sense only for associative operators, and we have no way of knowing what # actually does. What about a – b – c? We can’t very well leave it up to the implementation to choose between (a – b) – c and a – (b – c)!
Grace’s message evaluation rules for operators also follow Smalltalk: apart from the special syntax, they follow exactly the same evaluation rules as any other message. We hope that this single evaluation rule, straightfoward syntax, and lack of implicit calls or coercions, will ensure that Grace programs that use operators will remain comprehensible.
1 + 2
1 + (2 * 3)
0 – 1
“Hello” ++ ” ” ++ “World”
bezerk !@#$%^&* istan
But this is where things get more tricky. As mentioned above, Grace operators are evaluated left-to-right. In our current design, unlike Smalltalk, (but like Self) it is a syntax error for two different operator symbols to appear in an expression without parenthesis to indicate order or evaluation — all Grace operators have the same precedence. The same operator symbol can be sent more than once without parenthesis.
1 + 2 + 3 // evaluates to 6
1 + (2 * 3) // evaluates to 7
(1 + 2) * 3 // evaluates to 9
1 + 2 * 3 // syntax error in Grace; would evaluate to 9 in Smalltalk
Named message sends bind more tightly than operator message sends: The following examples show first the Grace expressions as they would be written, followed by the parse
1 + 2.i 1 + (2.i)
(a * a) + (b * b).sqrt (a * a) + ((b *b).sqrt)
((a * a) + (b * b)).sqrt ((a * a) + (b *b)).sqrt
a * a + b * b // syntax error
a + b + c (a + b) + c
a – b – c (a – b) – c
The One True Message Send has implications that carry over to operator messages also: an object can have only one method to respond to any given message, whether that message’s name is an operator symbol or a textual name. For example, Number can have only one definition for the “+” operator.
A second detail of this current design is that Grace has no unary operators. There can be negative numeric literals, like -2 or -345.34, but to negate an expression, programmers have to write either “e.negative” (using a named message) or “0 – e” using the binary “-” message.
We have more questions about the design for operators than some other parts of Grace’s design. The trade-offs seem more acute here than elsewhere for some reasons. This design has the key advantage that arbitrary operators can be defined, and the disadvantage that we do not support operator precedence.
Eric asks: whether it wouldn’t make more sense to require all operator sends to be parenthesized, rather than permitting multiple left associative calls. James thinks this worked OK in Self, and e.g. “a + b + c” isn’t generally a problem.
Kim says: the no-precedence design would be very confusing – we have to let people write normal arithmetic statements with the intuitive meanings.
James says: that there are advantages to requiring parentheses for novice programmers: they can be easily inserted by an IDE, many style guides recommend parens for complex expressions, and it’s worth it to permit arbitrary operators.
James thinks: if we do – after all – need operator precedence, the obvious design to fall back to is like Blue or C# – there are a fixed set of operators, with fixed precedence. The One True Message Send Rule will still apply; so operator methods would still be defined just like named methods, and operator message sends would behave just like named message sends.
We even have some draft BNF for expressions, ripped shamlessly from the Self manual in the first instance:
{ … } means zero or more occurrences of the symbols inside the braces.
[ … ] means zero or one occurrences of the symbols inside the braces.expression ::= constant | named-send | operator-send | ‘(‘ expression ‘)’
constant ::= “self” | numberLiteral | stringLiteral | blockLiteral | objectLiteral
named-send ::= named-message | receiver “.” named-message | “super” “.” named-message
named-message ::= identifier arglist {identifier arglist}
arglist ::= “(” [{expression”,” } expression] “)”] | blockLiteral
operator-send ::= receiver operator-message | “super” operator-message
operator-message ::= operator expression
operator ::= op-char {op-char}
receiver ::= expression
blockLiteral ::= “{” [ param-decl-list => ] code “}”
code ::= { statement “;” } [“return”] expression
Avod the operator precedence rathole. It is a black hole. The Self rule is already a compromise, and quite sufficient.
A late evening thought: the BNF above has changed from Self in that operator messages have to have both arguments explicit: you can’t have an explicit self receiver.
Doesn’t this mean that the expression syntax would in fact permit prefix unary operator messages like ! and – ? In Self, these would be implicit self messages?
The problem is the declaration syntax – we don’t want to override names, so C++ / Scala options that distinguish on the number of arguments are not available in Grace. Perhaps (as with assignments) we have a very special name so declaring unary-() gets you the unary minus operator?
Or is this yet more special casing for something that’s really not that important. For now, I think it’s nice to know we can do this if we need to, but we don’t have to commit to it yet.