Anomaly in order of evaluation

This arithmetic quirk cost me some time.

It appears that :

a = 3 b = 2 c = 1
a * b + c = 9
(a * b) + c = 7
a = 0 b = 2 c = 1
a * b + c = 0
(a * b) + c = 1

i.e. evaluation is add before multiply rather than the conventional multiply before add.

Demonstrate as follows:

The classroom is a room. 
When play begins: 
	let a be 3;
	let b be 2;
	let c be 1;
	say " a = [a] b = [b] c = [c][line break]";
	say "a * b + c = [a * b + c][line break]";
	say "(a * b) + c = [ ( a * b ) + c][line break]";
	let a be 0;
	let b be 2;
	let c be 1;
	say " a = [a] b = [b] c = [c][line break]";
	say "a * b + c = [a * b + c][line break]";
	say "(a * b) + c = [ ( a * b ) + c][line break]".

It took me longer to realise because when a is 1, both methods result in the same answer.

This has come up several times in the bug tracker (#776, #1005, #1179). Graham says:

“Equation” refers to specifically defined equations (§15.18. in the manual.) The operators outside equations are shorthands for words “times” and “plus” so they’re counted as “arithmetic in words”. I don’t know why they can’t follow usual mathematical rules too, but apparently this is not going to change. Always use parentheses when the order of operations matters.

My advice is to watch out for parentheses even if you’re using an equation; I have an abandoned work that contains an equation written:

R = (sin(2b))/((sin(b+c))+(sin(b-c))) 

which has more parentheses than it seems to need because if you write this:

R = sin(2b)/(sin(b+c)+(sin(b-c)) 

then Inform thinks that you’re trying to take the sine of (2b)/(sin(b+c)+(sin(b-c)).

Admittedly using trigonometry is a pretty obscure case, which is not unrelated to why this work is abandoned.

Point taken. Brackets shall be used without question at all times.

Hmmm, if it’s come up several times, I wonder whether this means that the design is a bit TOO unforgiving. What benefit is there in making it behave ONE way if you’re writing it as an equation and ANOTHER way if you’re spelling it out in words?

(It’s like the old joke, the storekeeper who says, exasperated, “You’re the 10th customer to ask me that today! For the last time, there’s NO DEMAND for that product!”)

It wouldn’t be that Graham has deliberately made it work different in normal code, it would just be using the normal phrase resolution process. It would be much easier to switch to a different mode in equations because they have special syntax, whereas switching a maths mode on an off in normal code would be more complicated.

I think it would probably be good to add a note to section 15.5 of the docs warning that the arithmetic outside of equations will not follow the conventional order of operations and that brackets are strongly recommended any time you’re using more than just addition or multiplication (but also if you’re using both together!)

Enthusiastic second for the idea of explaining this in the documentation. Also it seems like his line has shifted a bit; on one of the bug reports he says:

and on a later one

which is not quite the same, as the first one would suggest that “4 - 2 * 6” would evaluate mathematically when used in-line.

In fact… the symbols do evaluate differently from the words, but not in the ways one would expect. Consider:

When play begins: say "6 - 4 / 2 = [6 - 4 / 2]. 6 minus 4 divided by 2 is [6 minus 4 divided by 2]. 3 * 2 + 1 = [3 * 2 + 1]. 3 times 2 plus 1 is [3 times 2 plus 1]. 4 - 2 * 6 = [4 - 2 * 6]. 4 minus 2 times 6 is [4 minus 2 times 6]." Lab is a room.

So the first pair is behaving as Graham said on the first bug report–when symbols are used, the / is evaluated before the -, but when words are used, the operations are evaluated from left to right.

For the second pair, when symbols are used, the + is evaluated before the * even though the traditional mathematical order of operations is the other way around and even though the * is first–that is, the result of using symbols is neither linguistically nor mathematically correct, in Graham’s terms. (And the result of using words is both.)

For the third pair, when symbols are used, the - is evaluated before the *, which obeys English rules (operations evaluated in order) but not mathematical ones. When words are used, the times is evaluated before the minus, which obeys mathematical rules but not English rules. This is the reverse of what Graham’s first note suggests.

In none of the cases are the operations consistently evaluated in order, which is what Graham’s second note suggests (about linguistic rather than mathematical rules applying outside of equations).

So I’m completely at sea both about what the desired behavior is and about what the actual behavior is. Some clarification in the documentation would definitely be helpful, and it does seem to me that this can’t all be working as intended.

In the meantime, it’s definitely prudent to put parentheses in when in doubt.

I took Graham’s “it follows no special conventions” to mean that the behavior is essentially undefined (because “evaluated from left to right” would be a convention.)

Note that the English rule is that it depends on context, c.f. “We shared a bottle of wine and each of us had the soup, so that’s $15 plus $5 times four” and “Each of us had a glass of wine and the soup, so that’s $4 plus $5 times four.” Although of course the compiler can’t interpret the context so it’s not a very good argument for it to not be consistent.

It seems there is never a case for doing sums with more than two factors and one sign without parentheses. Why not simply state that in the docs?

Hmm, I took “no special conventions” to mean “the same convention as everything else.” Though I’m not sure what those conventions are, so I guess that means “essentially undefined.”

Anyway, this definitely could use some explanation in the documentation. Peter, maybe this is one for the roundup? (I’d go through official channels but Graham has made it pretty clear that he doesn’t want this filed as a bug, and I don’t think I have any votes left on Uservoice.)

The usual convention isn’t clearly defined. For example:

To decide which number is echo (N - a number):
	say N;
	decide on N.
	
To test (N - a number) with (M - a number) and (O - a number):
	do nothing. 

When play begins:
	test echo 1 with echo 2 and echo 3;
	say line break;
	if echo 4 is echo 5 or echo 6 is echo 7 or echo 8 is echo 9:
		do nothing.

The first test prints “321” (evaluated from right to left) and the second test prints “547698” (logical operations evaluated from left to right, equivalence tests from right to left). This isn’t conclusive evidence because it might evaluate phrases in different order than the whole expression, but for practical purposes it’s pretty much the same thing.

The logical operators order is conventional: they must be evaluated left to right to guarantee the parameters with side effects are short-cutted appropriated.

The phrase parameters order does not seem so conventional - I’m pretty sure Inform 6 evaluates function arguments left to right. Right to left is sure to trip people up whenever they have side effects (like removing items from a list.) I don’t know if multi-parameter phrases are consistently evaluated right to left or not.

But we know that it doesn’t consistently parse ambiguous phrase nestings, as shown by the differences between using - and minus. If the order was intuitive but consistent that would be one thing, but this lack of consistency is pretty problematic.

I’d say a documentation change is essential. A compiler warning could help too perhaps.

Seems that way! Added to my list!