1. 16
  1.  

  2. 6

    I love these discussions, because I feel there is no good answer here, and it really depends where you stand to think about that. Thanks for sharing!

    1. 4

      I worked on a similar thing quite recently, and a lot of thinking went into how to avoid evaluation some fragments twice.

      It didn’t even come to my mind that some may consider evaluating things twice to be not anything but a bug.

      I guess it’s the power of status quo, which is why it’s always helpful to ask “if it didn’t work that way, would we make it work that way” before going on pointless tangents (like this debate).

      1. 3

        I can see the logic here:

        1. x < f() < z desugars to x < f() && f() < z.
        2. If f() is evaluated twice, that’s simple but unintuitive.
        3. If f() is evaluated once, we have to enforce that somehow.
        4. If we miss a case, now it’s complex and unintuitive, because now it behaves as you expect it to expect for that specific special case, when it’s evaluated twice.
        5. Maybe it’s safer to just make it always evaluate twice so people always know what they’re getting into.
        1. 6

          I think there are a few problems with that:

          x < f() < z desugars to x < f() && f() < z

          • If we need to look at the desugaring to understand what it does, maybe we shouldn’t have that syntax sugar in the first place?
          • Why make the language worse with additional syntax if you could just write the “desugared” syntax down and be done with it?

          If f() is evaluated once, we have to enforce that somehow.

          I’m not sure what you mean with that. If the function invocation is written down once it needs to be evaluated once. You simply implement that. It’s pretty much straight-forward, as f() is guaranteed to be stand-alone, not something like this.

          Maybe it’s safer to just make it always evaluate twice so people always know what they’re getting into.

          Then go ahead and also re-evaluate switch-case for every match-arm, right?

           // how often foo() is evaluated is determined the number of cases below
          switch foo() {
            case x
            case y
            ...
          }
          
          1. 1

            I based that chain of thinking off your claim that

            I worked on a similar thing quite recently, and a lot of thinking went into how to avoid evaluation some fragments twice.

            If you have to worry about evaluating some fragments twice, then the reasoning makes sense. If that isn’t a problem, then it doesn’t.

            (I’m not saying I agree with the chain, I’m saying that I can see how a person would think of double-evaluation as the ‘right behavior’.)

          2. 4

            x < f() < z desugars to x < f() && f() < z.

            Actually, according to the documentation, that would desugar to something like tmp = f(); x < tmp && tmp < z (pseudocode). The expression is only evaluated once, but the value can be fetched twice, which causes the problem with things like tied scalars but not ordinary subroutines.

        2. 6

          It is obvious that such chained comparisons should not exist at all. Just recently, this was on Hackernews:

          https://stackoverflow.com/questions/17202207/why-does-true-false-is-false-evaluate-to-false

          The minor convenience this feature brings is a thousand times outweighed by all the subtleties and gotchas.

          1. 3

            For someone with only a passing exposure to Perl, is there a go-to resource on contemporary Perl? Even a basic introduction or tutorial would work.

            1. 4

              man perlbook

                1. 3

                  For this particular case, the tie functionality is the root of the issue. Read more about it here: https://perldoc.perl.org/perltie.html

                  IMO tie is nice to have in certain circumstances - one can transparently use a hash (dict) that actually corresponds to a database or a file on disk - but there’s infinite scope for footguns.

                  1. 1

                    tie isn’t at the root of anything. tie is just a relatively-friendly way of creating a magical variable, being used here to provide an understandable test-case for discussion. If you were working with $! or a slot in %ENV or any shared variable under threads, to name a couple examples, the question of whether the value was fetched once or twice could be relevant to you. Here the discussion uses tie to create a variable that consistently tweaks the issue we want to talk about, instead of “sometimes, maybe, under some circumstances”. It focuses the discussion. You could even say that it’s a nice thing that the language is powerful enough to create such a test-case… many would leave it in the realm of the un-testable.

                  2. 2

                    It’s not an intro but the O’Reilly book Perl Cookbook is an enjoyable and informative read. It’s dated though.