johanafm wrote:

A binary operator never involves more than two operands.

And associativity is never involved if there is only one operation. Associativity applies to operators, not operands (look it up). That is the point we're disagreeing on.

traq wrote:

objectively, it's not a "bug" because the results are predictable and reliable (once you work out the nuances)

Not really reliable; being undocumented and unsupported (explicitly), it's theoretically possible that a future version may do things differently. Then everything that previously depended on it would break.

traq wrote:

(maybe even according to the laws of mathematics, I'm not sure)

In normal algebraic situations (and in purely functional languages), once a variable is bound to a particular entity it stays bound to that entity – no changing it part way through; so no incrementing.

    laserlight;11036585 wrote:

    In johanafm's example of $a = $z + $y, precedence indicates that the grouping is equivalent to: $a = ($z + $y). If you want to talk about the "leftmost operator", that would actually be the = operator, not the + operator.

    thank you.

    Weedpacket;11036591 wrote:

    Not really reliable; being undocumented and unsupported (explicitly), it's theoretically possible that a future version may do things differently. Then everything that previously depended on it would break.

    Fair enough. I had missed that it actually was "undefined" behavior.

    Weedpacket;11036591 wrote:

    In normal algebraic situations (and in purely functional languages), once a variable is bound to a particular entity it stays bound to that entity – no changing it part way through; so no incrementing.

    So, since we're talking about something that doesn't happen at all in algebra (but obviously does in C), what rules should apply? (Aside from not trying to do "tricky programming"?)

      traq wrote:

      So, since we're talking about something that doesn't happen at all in algebra (but obviously does in C), what rules should apply? (Aside from not trying to do "tricky programming"?)

      The principle of least surprise means that the rules that should apply would be what a typical user of the language would expect. Often, this means drawing on mathematics, hence things like making precedence of arithmetic operators follow the BODMAS convention that is commonly taught to elementary maths students in various countries. But as this very thread demonstrates, what different people expect to be the order of evaluation of a complex expression can be reasonably different, and there is no clear common background to draw on, besides the programming language itself.

      So, the programming language designer could decide that compilers/interpreters "cannot pick and choose when to do what or when to apply what rules" (to use johanafm's words), and hence specify the order of evaluation in all cases. Or, like C and C++, they can leave the order of evaluation unspecified for the benefit of optimisation, and even allow for undefined behaviour. (In the case of PHP, I now think the behaviour is unspecified rather than undefined... the difference being that unspecified behaviour means that any one of the possible orders of evaluation could be chosen, whereas undefined means that any behaviour is possible, including simply resulting in a fatal error.)

        First off, regarding associativity, I cede the point. The three of you (weed, traq and laserlight) all agree, and so does the internet. I've probably always assumed it was about operands because that removes all ambiguity in evaluation order between operands and by extension to operators.

        traq;11036551 wrote:

        $x * 2. Regardless of which order the operands are evaluated in, you add one and subtract one: a no-op

        This is not true though.
        x-- + ++x:
        RHS before LHS:
        - RHS: x becomes (x+1), LHS: x is now (x+1) afterwards decrease by 1 to (x): result: x => x, statement: (x+1) + (x + 1)
        LHS before RHS:
        - LHS: use x in the statement, dec x to (x-1), RHS: inc (x-1) to x, use x: x => x, statement: x + x
        The result differs by 2.

        I do agree that the only thing that makes things right or wrong is their definition. But as weedpacket states, without a clear definition, you have potential for behaviour that will change in future versions. At best it is "only" lacking in documentation or a documentation error. Still, I find both the javascript way of handling things and the C++ way of handling things straightforward, simple and easily predictable. I like both, even though they are different. Do note that neither C++ (and I'd guess C) nor javascript agree on all points, and that both disagree with php on some points.

        C++ evaluates all operators of top precedence before operators of lower precedence. For the sake of simplicity, I will refer to this as hard precedence. Pre / post operators affect their variables before or after the statement.
        Javascript evaluates operands from left to right while resolving precedence as needed. Pre / post operators affect their values after the expression (i.e. the post or pre operator and its variable) is evaluated.

        Back to php:
        While it could be "right" to
        - Always evaluate operands left to right
        - Except if the left operand is a variable and the right operand is an expression, then reverse order
        assuming this was defined and documented.

        PHP has behaviour which sometimes means left before right, sometimes right before left, which I personally believe is overly complicated, weird etc. They may obviously define whatever behaviour they wish. But either way, I will still claim their current behaviour is a bug, and I'm rather certain it all comes from them NOT having defined (and documented) exactly what is expected while evaluating expressions (or more noteably, their operands). Straight from php.net on operator precedence

        Parentheses may be used to force precedence

        Yet, they do not always, namely in the exact same situation that has been the show case from the start

        $x = 5;
        $y = ($x) + ++$x;
        

        If parenthese is indeed used to force precedence, ($x) should be evaluated before anything else. Still, it is not. At the same time, non-parenthesized expressions that should be evaluated before addition, but would always be evaluated later than any parenthesized expression do change the outcome even though they are identity operators under given circumstances:
        - integer cast for integers is an identity operation but changes behaviour: (int) 5 === 5
        - multiplication by 1 is an identity operation for all integers: 1 5 = 5 1 = 5

        $x = 5;
        $y = 1*$x + ++$x;
        $x = 5;
        $y = (int) $x + ++$x;
        

        This last block of code would both evaluate the left operand of + before the right operand. Assuming this behaviour is defined and documented, it is "right" (although I will continue to argue that it would make sense to change it into something that is inline with maths in general and either javascript or C). Mainly for the fact that it gives you one single edge case where RHS is evaluated before LHS, while LHS is always otherwise the norm. But in combination with the lack of respect of forcing evaluation precedence through the use of parenthesis, I will claim it is a bug. The block above with ($x) would evaluated the right operand of + before the left, even though () is supposed to force precdence.

        And once again, they could of couse define this behaviour to be "correct", which would make it "right". If they ever do, and put it in the docs, I will simply keep my thoughts to myself on the matter.

        Not sure what post this came from, but at some point Traq wrote

        Traq wrote:

        // first, operator precedence.

        // note that I was wrong when I assumed ++ had a higher precedence than +:
        // in fact, ++ is not an arithmetic operator at all (++/-- have their own operator type).

        That you may consider it other than an arithmetic operator changes nothing. It still has higher precedence than addition. It is listed on the page of operator precedence at php.net alongside casts and error supression. The only thing that may confuse things is that "operator precdence" isn't enough to determine in which order operators are evaluated.

        2 + 3 + 4 * 5
        

        May be evaluated as
        2 + 3: 5
        4 * 4: 20
        5 + 20: 25
        or as
        4 * 5: 20
        2 + 3: 5
        5 + 20: 25

          laserlight;11036559 wrote:

          You may be surprised to learn that in C and C++, there is explicitly no such rule: when considering an expression like (i + ++i), the behaviour is undefined

          Have you read that in the specs? Or are you talking specifically about operand evaluation order? Because C doesn't need to deal with operand evaluation order due to how they treat operator precedence. From what I've seen (using gcc -lstd=c++), C++ uses "hard precedence" in conjunction with inc/dec before/after the statement (as oppsed to the expression) which means there is no need to specify operand evaluation order.

          int c =2, d = 4;
          int y;
          
          y = c-- + ++c + d + c++;
          

          Will then, through hard precedence lead to all inc/dec operators being evaluted first. Their respective order of evaluation is irrelevant.

          c--: decrement c by one after the statement.
          c++: increment c by one after the statement
          c++ increment c by one after the statement

          Total: c += 1 before the statement, which means c = 3;
          y = 3 + 3 + 4 + 3;

          y = 13

          Decremnt c by one and increment by one: 0 total; c remains 3

            Well, first off, I think laserlight is right that all these differing expectations we've been discussing are all reasonable, from one angle or the other. As for ++/-- not be arithmetic operators, I had thought they were; but they have different pages in the manual (so I'm assuming PHP treats them differently).

            It seems, I think, that ++/-- behave a lot more like functions that operate on numbers by reference than simple math operators. As weedpacket pointed out, there's no such thing as references in algebra.

            It's because of this "by reference" issue that I would expect your example to be a no-op: what happens to one happens to the other.

              johanafm wrote:

              If parenthese is indeed used to force precedence, ($x) should be evaluated before anything else. Still, it is not.

              Your reasoning is flawed because precedence is for grouping, not order of evaluation.

              johanafm wrote:

              This last block of code would both evaluate the left operand of + before the right operand. Assuming this behaviour is defined and documented, it is "right" (although I will continue to argue that it would make sense to change it into something that is inline with maths in general and either javascript or C). Mainly for the fact that it gives you one single edge case where RHS is evaluated before LHS, while LHS is always otherwise the norm. But in combination with the lack of respect of forcing evaluation precedence through the use of parenthesis, I will claim it is a bug. The block above with ($x) would evaluated the right operand of + before the left, even though () is supposed to force precdence.

              And once again, they could of couse define this behaviour to be "correct", which would make it "right". If they ever do, and put it in the docs, I will simply keep my thoughts to myself on the matter.

              I already quoted the PHP manual to you in post #28:

              PHP manual wrote:

              Operator precedence and associativity only determine how expressions are grouped, they do not specify an order of evaluation. PHP does not (in the general case) specify in which order an expression is evaluated and code that assumes a specific order of evaluation should be avoided, because the behavior can change between versions of PHP or depending on the surrounding code.

              Therefore, your claim that "this last block of code would both evaluate the left operand of + before the right operand" is merely an observation of a particular run of a particular version of the PHP interpreter (if you did indeed make such an observation rather than guess).

              johanafm wrote:

              Have you read that in the specs?

              Yes.

              C99 Clause 6.5 Paragraph 2 wrote:

              Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be read only to determine the value to be stored.

              C99 Clause 6.5 Paragraph 2 Note 70 wrote:

              This paragraph renders undefined statement expressions such as

              i = ++i + 1;
              a[i++] = i;

              while allowing

              i = i + 1;
              a[i] = i;

              C++11 introduces the concept of "sequenced before" and "sequenced after" to replace sequence points to cater to the introduction of standard support for threading, so the language has become even more technical and hard to understand, so I shall quote from C++03 instead since we are more concerned about PHP here (the basic idea remains):

              C++03 Clause 5 Paragraph 4 wrote:

              Except where noted, the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified.53) Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored. The requirements of this paragraph shall be met for each allowable ordering of the subexpressions of a fullexpression; otherwise the behavior is undefined. [Example:

              i = v[i++]; // the behavior is unspecified
              i = 7, i++, i++; // i becomes 9
              i = ++i + 1; // the behavior is unspecified
              i = i + 1; // the value of i is incremented

              β€”end example]

              johanafm wrote:

              Or are you talking specifically about operand evaluation order? Because C doesn't need to deal with operand evaluation order due to how they treat operator precedence. From what I've seen (using gcc -lstd=c++), C++ uses "hard precedence" in conjunction with inc/dec before/after the statement (as oppsed to the expression) which means there is no need to specify operand evaluation order.

              int c =2, d = 4;
              int y;
              
              y = c-- + ++c + d + c++;

              Will then, through hard precedence lead to all inc/dec operators being evaluted first. Their respective order of evaluation is irrelevant.

              Try compiling with -Wall and watch gcc warn you about undefined behaviour πŸ˜‰

              EDIT:
              As such, you are mistaken when you claim that "C++ evaluates all operators of top precedence before operators of lower precedence". A C++ compiler author is free to write his/her C++ compiler to do this (though it isn't always possible, e.g., in x[++i], the subscript operator [] has a higher precedence than prefix operator ++, but it is necessary to evaluate ++i first), but that is not generally true about C++.

                Well I'll be damned. A whole bunch of warnings...

                laserlight;11036621 wrote:

                (if you did indeed make such an observation rather than guess).

                No, everything regarding php is through observation.

                Thanks for the clarifications. But then, why the hell is evaluation order not specified? Are there any actual upsides to leaving behaviour undefined? Explicitly so certainly makes more sense than implicitly. But still...

                  johanafm wrote:

                  But then, why the hell is evaluation order not specified?

                  It allows compiler authors to write their compilers to be more aggressive in applying optimisations, as far as I know. Besides, even in languages where the order of evaluation of such potentially confusing expressions is well defined, it is still not advisable to write such expressions, so...

                  Oh, and in case you missed my edit of my previous post:
                  You are mistaken when you claim that "C++ evaluates all operators of top precedence before operators of lower precedence". A C++ compiler author is free to write his/her C++ compiler to do this (though it isn't always possible, e.g., in x[++i], the subscript operator [] has a higher precedence than prefix operator ++, but it is necessary to evaluate ++i first), but that is not generally true about C++.

                    I did miss it. Thanks for the repost and insights! πŸ™‚

                      @, @, @, @, @, @ & @ laserlight, Thank you very much for this insightful and enlightening discussion. My deep appreciation to johanafm for his strength and courage and and raising some interesting points with beautiful examples such as $y = 0 + $x + ++$x;. The explanations from Weedpacket are indeed eye-openers for me. Traq has followed up the whole discussion and raised many points and questions to keep the discussion interesting and alive. Most of all, I thank laserlight. I think, you have indebted all of us here through your clarifications based on your knowledge and deep insight into the world of different programming languages. And my special thanks to dalecosp for bringing me in touch with the video clips of Douglas Crockford. Thank you very much and a Big Merry Christmas to you all!!!

                        Indeed. A big thanks to everyone. I had no idea about all of this until you raised the question. I've been most intrigued by it and have had lots of fun. I've been lots of wrong too, so I really appreciate everyone trying to enlighten me. Merry christmas πŸ™‚

                          I have really enjoyed reading all these explanations. I have learned quite a bit from this, but most importantly I learned that trying to be too clever can bite you in the ass. You guys here are so very knowledgable and I have learned a lot from you guys over the years. I really appreciate the discussions like this where I get to learn some of the more intricate workings of the language. I feel like this isn't the type of information gained through the normal act of learning to program and a specific language. You guys have provided a lot of insight and I have to say, I very much felt as johanafm did at the beginning of the thread and the banter has enlightened many of us it seems. Merry Christmas to you all, and thanks for always advancing my knowledge. πŸ™‚

                            I used to chuckle (privately) at a couple guys I worked with who felt you should never use the ternary operator; that saving a few keystrokes and putting a simple if/else block into a little one-liner wasn't worth the tiny bit of time you save and the pseudo-elegance of having everything on one line. This discussion is helping me come around to their viewpoint. πŸ™‚

                              Yes, thanks everyone; this was a very informative conversation.

                                Write a Reply...