Simple Rules
There are two rules that determine the order of op1 +++ op2 +++ op3 *** op4
expression evaluation:
Firstly since an operator starting with * takes precedence over an operator starting with + the expression is transformed into:
op1 +++ op2 +++ (op3 *** op4)
Secondly since there are multiple operators of the same precedence that appear side by side (op1 +++ op2 +++ ...) they are grouped left to right:
(op1 +++ op2) +++ (op3 *** op4)
Example
Consider the following expression:
op1 +++ op2 +++ op3 +++ op4 *** op5
Following the same two simple rules it will be evaluated as:
((op1 +++ op2) +++ op3) +++ (op4 *** op5)
Another Example
Alternatively let's apply the same two rules to op1 +++ op2 +++ op3 +++ op4 *** op5 *** op6
:
Operators starting with * have precedence over operators starting with +:
op1 +++ op2 +++ op3 +++ (op4 *** op5 *** op6)
Then group operators with the same precedence left to right:
((op1 +++ op2) +++ op3) +++ ((op4 *** op5) *** op6)
Mutable Objects: a Word of Caution
The grouping makes perfect mathematical sense provided the +++
and ***
methods do not have any side effects. Consider:
op1 +++ op2 +++ op1 *** op2
Intuitively the expression should return an object holding 5. However, because of the unfortunate side effects that +++
and ***
methods produce in the original code sample (both modify the value stored within the object) the expression will result in an object holding 12 instead of expected 5.
That's why it's best to rely exclusively on immutable objects when constructing such expressions:
case class Op ( x: Int) {
def +++(that: Op) = {
println(this.x + " +++ " + that.x)
Op(this.x+that.x)
}
def ***(that: Op) = {
println(this.x + " *** " + that.x)
Op(this.x * that.x)
}
}
The Op(1) +++ Op(2) +++ Op(1) *** Op(2)
expression will result in Op(5)
, as expected.