ID:1605373
 
Keywords: logic
BYOND Version:506
Operating System:Windows Vista Home Premium 64-bit
Web Browser:Chrome 35.0.1916.153
Applies to:DM Language
Status: Verified

A member of our crack team of bug testers has verified that this issue is reproducible, and has handed it off to the development team for investigation.
Descriptive Problem Summary:
The order of operations as it applies to logic operators and "in list" seems at best counter-intuitive. As a descriptive example, the line

if(1 in list(1) || 2 in list(2))

necessarily returns false in spite of both "in" clauses appearing correct.

Using named lists makes this problem clearer; see below
Code Snippet (if applicable) to Reproduce Problem:
    var/list/L1 = list(1)
var/list/L2 = list(2)
var/list/L3 = list(L1,L2)
world << "L1: list(1); L2: list(2); L3: list(L1,L2)"
world << "1) 1 in L1: [1 in L1]"
world << "2) 2 in L2: [2 in L2]"
world << "3) 1 in L1 || 2 in L2: [1 in L1 || 2 in L2]"
world << "4) L1 || 2: [L1 || 2]"
world << "5) L1 || 2 in L2: [L1 || 2 in L2]"
world << "6) L1 || 2 in L3: [L1 || 2 in L3]"
world << "7) L2 in L3 in L1: [L2 in L3 in L1]"
world << "8) 1 in L1 && 1: [1 in L1 && 1]"


It seems that the interpretation of line 3 (the original test case) is probably
1 in L1 || 2 in L2 equals
(1 in (L1 || 2)) in L2 equals
(1 in L1) in L2 equals
(0 or 1) in L2

Expected Results:
L1: list(1); L2: list(2); L3: list(L1,L2)
1) 1 in L1: 1
2) 2 in L2: 1
3) 1 in L1 || 2 in L2: 1
4) L1 || 2: L1 // proper shortcut returns value of first non-false parameter
5) L1 || 2 in L2: L1 // shortcut removes test operator
6) L1 || 2 in L3: L1 // shortcut removes test operator
7) L2 in L3 in L1: 1 // becomes (1 in L1), since 1 is used as 'true'
8) 1 in L1 && 1: 1 // test operator evaluates before logical operator

Actual Results:
L1: list(1); L2: list(2); L3: list(L1,L2)
1) 1 in L1: 1
2) 2 in L2: 1
3) 1 in L1 || 2 in L2: 0 // nominal test case
4) L1 || 2: L1 // correct, outputs /list
5) L1 || 2 in L2: 0 // return value of || taken as left hand input to test operator
6) L1 || 2 in L3: 1 // proof of line 5 comment
7) L2 in L3 in L1: 1 // correct
8) 1 in L1 && 1: 0 // return value of && taken as right hand input to test operator

Does the problem occur:
Yes, it does.

Workarounds:
Proper parenthesis.
Other test cases:

// L2 = list(2)
9) 1 || 2 in L2: Expected 1, got 0 (1 in L2)
10) 0 || 2 in L2: Expected 1, got 1 (2 in L2)

var/list/L4 = list(2,0)
11) 0 && 2 in L4: Expected 0, got 1 (0 in L4)
12) 1 && 2 in L4: Expected 1, got 1 (2 in L4)
13) null && 2 in L4: Expected 0, got 0 (null in L4)
One would expect that a complaint and a list of code failures caused by the bug would merit at least some comment. (Edit: note that it was temporarily labelled not a bug without comment)

The in operator does not follow the order of operations that other test operators follow (==,<, etc) in spite of falling into that category in every other way. Note that:
1 == 1 || 2 == 2

follows the order of operations that was described as expected behavior instead of becoming
1 == (1 || 2) == 2
(1 == 1) == 2
1 == 2
0.

The behavior listed here is *literally causing programs to fail*. Additional code is required because the syntax does not meet standards nor expectations.

Additionally, it should be noted that documenting order of operations is difficult. People do not know where to look it up, and writing a succinct summary is difficult. If your order of operations is nonstandard, people are not going to be able to look it up, and will assume standard order. The appropriate order of operations, I argue, would leave the in operator at the same priority as other test operators.

If the argument is that changing the order of operations would break existing functionality (and I don't know because there is in fact no argument presented), that would be essentially nonsensical. The order of operations as is presented has no value at all.

In order for the present order of operations to have any value, you would need to:
* Check the first non-null value in a hardcoded list of variables to see if it is in a list
x || y || z in list() // if x is null, use y, etc

which is so godawfully abhorrent code that it makes me shudder to think of it, or

* Check if 0/null is in a list only if some complicated logic expression is... no, you know what, I can't even defend using && here, or

* Use an alternate list if the first variable is null
x in list1 || list2
or,

* Creatively cause the in check to fail by using 0/null as the right hand parameter of in
x in list1 && null


I cannot see marking it as "Not a bug" as anything other than caustic to the functionality of existing and future programs.
Interesting find! I think we can probably fix this without breaking anything because it's hard to believe anyone would rely on this behavior. I would definitely consider this a bug because every other language I've seen that uses the "in" operator (eg, python) evaluates the operation as you'd expect.

(On a related note, we really should have 'or' and 'and' named operators to fit with the syntax)
Tom changed status to 'Verified'
Would it also make sense (or be possible) for the "in" operator to have higher priority over the ! operator? I'd say that usually if someone uses !thing in list it's to check "is the thing not in the list," not "is null in the list." It would save on parentheses, but the spacing does make it look odd.
I'd say it would make sense, yes.
The alternative would be that !thing in list can only translate into one of the following two cases:
0 in list
1 in list

It seems exceedingly rare that that would be what you would want.

From a devil's advocate point of view, though, !clauses are often hard to read if there aren't parentheses around them. "!word word word" doesn't seem like it equals "!(word word word)".

I suppose the only counter argument would be that there should be a "not in" operator so it reads x not in list instead of not x in list
In response to SuperSayu
I'd say that would make more sense, if "!in" could be added. The ! operator could just be given an alias to allow "object !in list" and "object not in list". Maybe.
I would like the addition of !in as well. It would clean up logic nicely, and make it feel more natural. When programming while being distracted or after no using DM for some time, I still fall into the trap of !thing in list without parenthesis.
If we could and an "and" and an "or" named operator, that'd be wonderful.
In response to Ernesto5432
Ernesto5432 wrote:
If we could and an "and" and an "or" named operator, that'd be wonderful.

Make a separate request for it?
In response to Ernesto5432
Make your own definitions for them?
#define and &&
#define or &&
If in is going to be prioritized, may I suggest adding to alongside (maybe a higher priority than in)?

I doubt there is much instances for the use of to except maybe in if() where one might do
if(i in 1 to 10)


Better to be safe then sorry >.>
I'd like to see in have a higher priority as well, and if not a !in would also suffice.

Although ultimately I rather write:
if(!atom in list)


Rather than:
if(!(atom in list))


Idunno, I don't really like how !in looks though. ./shrugs
if(atom !in list)


Also support for GhostAnime's suggestion of to taking a higher priority for to. Assuming that it doesn't break anything of course. That way statements like his above can be executed properly.
bump
I like Kaiochao's "!in". GhostAnime's "in 1 to 10" suggestions and am bumping as well, in order to show my support of this.
o/
In response to Inuyashaisbest
Inuyashaisbest wrote:
GhostAnime's "in 1 to 10" suggestions and am bumping as well, in order to show my support of this.
o/

That's not a suggestion. That's a feature.
In response to Super Saiyan X
So it is, I was always under the impressing it would only work in for() and switch cases. </3
Bump since there seems to be a spat of language revision going on
Page: 1 2