# [RFC] Interval Comparison

- Thread: https://externals.io/message/96746
- Original archive: https://news-web.php.net/php.internals/96746
- Started by Fleshgrinder on 2016-11-06T20:29:13+00:00
- Messages: 17
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #96746 — Fleshgrinder — 2016-11-06T20:29:13+00:00

URL: https://externals.io/message/96746#96746

Validating whether a number is within a closed or open interval is
currently possible only via a construct like the following:

```
if (0 < $x && $x < 42) {
    echo 'x is in (0, 42)';
}

if (0 <= $x && $x <= 42) {
    echo 'x is in [0, 42]';
}
```

It is not very readable and repetitive. It would be interesting to
support a more mathematical notation for this:

```
if (0 < $x < 42) {
    echo 'x is in (0, 42)';
}

if (0 <= $x <= 42) {
    echo 'x is in [0, 42]';
}
```

I cannot judge how complicated it is to extend the Bison definitions to
allow this. However, if there is interest I'm sure there is a way. :)

-- 
Richard "Fleshgrinder" Fussenegger

---

## #96747 — David Rodrigues — 2016-11-06T20:59:26+00:00

URL: https://externals.io/message/96746#96747 · In reply to: #96746

I guess that the biggest problem is about the code parsing. Currently PHP
should supports what you wrote, but with another meaning. And personally I
think that this feature is not too common on programming language in
general, once that it is possible from first format (maybe more clear too).

Em 6 de nov de 2016 6:30 PM, "Fleshgrinder" <php@fleshgrinder.com> escreveu:

> Validating whether a number is within a closed or open interval is
> currently possible only via a construct like the following:
>
> ```
> if (0 < $x && $x < 42) {
>     echo 'x is in (0, 42)';
> }
>
> if (0 <= $x && $x <= 42) {
>     echo 'x is in [0, 42]';
> }
> ```
>
> It is not very readable and repetitive. It would be interesting to
> support a more mathematical notation for this:
>
> ```
> if (0 < $x < 42) {
>     echo 'x is in (0, 42)';
> }
>
> if (0 <= $x <= 42) {
>     echo 'x is in [0, 42]';
> }
> ```
>
> I cannot judge how complicated it is to extend the Bison definitions to
> allow this. However, if there is interest I'm sure there is a way. :)
>
> --
> Richard "Fleshgrinder" Fussenegger

---

## #96748 — David Walker — 2016-11-06T21:43:55+00:00

URL: https://externals.io/message/96746#96748 · In reply to: #96747

On Sun, Nov 6, 2016 at 1:59 PM David Rodrigues <david.proweb@gmail.com>
wrote:

> I guess that the biggest problem is about the code parsing. Currently PHP
> should supports what you wrote, but with another meaning. And personally I
> think that this feature is not too common on programming language in
> general, once that it is possible from first format (maybe more clear too).
>
> Em 6 de nov de 2016 6:30 PM, "Fleshgrinder" <php@fleshgrinder.com>
> escreveu:
>
> > […]
>
>
Richard,

I'd be a +1 on this.  I dislike having the double comparison all over
checking for bounds, and have for quite a while. Akin to what David brought
up, https://3v4l.org/7MvlB.  It seems the requested syntax is not valid in
any PHP version testable here.  I can't speak to the complexities of
implementing something like that, but would find it useful.

Of course, you'd have to worry about other conditions like
```
if (0 < $x > 20)
```
By worry about, I mean, should it be invalid syntax, or, should it be
optimized to be `20 < $x`?  I'd say you'd have to maintain the gt/lt for
chaining.

Then I'd wonder about similar syntax to check if 2 variables are both
greater than 0, but one greater/equal to the other:
```
if (0 < $x <= $y)
```

--
Dave

---

## #96751 — Andrea Faulds — 2016-11-07T01:17:45+00:00

URL: https://externals.io/message/96746#96751 · In reply to: #96746

Hi,

Fleshgrinder wrote:
> It is not very readable and repetitive. It would be interesting to
> support a more mathematical notation for this:
>
> ```
> if (0 < $x < 42) {
>      echo 'x is in (0, 42)';
> }
>
> if (0 <= $x <= 42) {
>      echo 'x is in [0, 42]';
> }
> ```
>
> I cannot judge how complicated it is to extend the Bison definitions to
> allow this. However, if there is interest I'm sure there is a way. :)
>

Python supports this, I believe it lets you use arbitrary chains of 
comparisons.

PHP is in the fortunate position where the associativity of the 
comparison operators is undefined. That is, `0 < $x < 42` is currently 
invalid syntax. Some other languages made the (in my view) mistake of 
defining a precedence here, so that it gets interpreted as `(0 < $x) < 
42`, which is (generally?) useless. PHP doesn't have this problem.

I've wanted to do this myself, but figuring out how to implement it was 
tricky, so I didn't spend much time on it. But it's certainly possible, 
and I'd appreciate this if it was added.

Thanks for bringing it up!

-- 
Andrea Faulds
https://ajf.me/

---

## #96763 — David Walker — 2016-11-07T21:51:18+00:00

URL: https://externals.io/message/96746#96763

> Hey guys! :)

The first one should definitely be an error since it makes no sense.


Sense be damed ;-) . I'd attribute it to an identity of sorts (if it was to
go all out with comparison chaining).  Yes it makes little sense, in
practice, but the truth of it would be the same.



> The following will also result in an error:
>
> ```
> if (0 < $x < $y < $z)
> ```
>
> We are only extending binary to ternary for <= and <. Of course it does
> not matter whether any part is constant or variable (hence you can
> exchange any and up to all variables in the following example with a
> constant number expression):
>
> ```
> $x <= $y <= $z;
> $x <= $y < $z;
> $x < $y <= $z;
> $x < $y < $z;
> ```
>

I realize that my comment, and question were going a bit off-topic with
going on about the chaining of comparisons, but I'm interested in it more
because I wonder which would be harder to implement.  It would seem to me
that it would be harder to implement a means in which you only allow one
additional layer of comparison, rather than abstract chaining of
comparisons (<, <=, >, >=).  As Andrea noted, python does allow for
arbitrary chains of comparison.  I'm not exactly sure how precedence would
work with comparisons, rather, as I see it the comparison would need to be
expanded such
```
if (0 < $a < $b > 10)
```
would be akin to
```
if (0 < $a && $a < $b && $b > 10)
```

I, of course, am only projecting a potential syntax, and I'm no yacc/ast
aficionado by any metric, but it'd seem to me that it'd be possible to
convert some stack of elements here into a recursive comparison.

--
Dave

---

## #96769 — lauri.kentta@gmail.com — 2016-11-08T12:25:13+00:00

URL: https://externals.io/message/96746#96769 · In reply to: #96763

> On Mon, Nov 7, 2016 at 1:38 PM Fleshgrinder <php@fleshgrinder.com> 
> wrote:
> > […]
> 
> I realize that my comment, and question were going a bit off-topic with
> going on about the chaining of comparisons, but I'm interested in it 
> more
> because I wonder which would be harder to implement.  It would seem to 
> me
> that it would be harder to implement a means in which you only allow 
> one
> additional layer of comparison, rather than abstract chaining of
> comparisons (<, <=, >, >=).

I'm pretty sure that a three-part comparison is easier to implement. It 
can be kind of hard-coded as a new type of syntax, somewhat like the 
ternary operator.

Personally I'd prefer the full Python-style chaining. It's a lot more 
useful, especially for cases like 0 < $a <= $b < $upper_limit.

One possible implementation idea (not knowing anything about Zend): 
Comparison (a<b) would return a pair of data, the result and the latter 
value. If this was the last comparison or the result was false, the 
value will be dropped and the comparison ends. Else the result (true) 
will be dropped and the comparison will go on.

-- 
Lauri Kenttä

---

## #96778 — Fleshgrinder — 2016-11-08T19:33:52+00:00

URL: https://externals.io/message/96746#96778 · In reply to: #96763

> Sense be damed ;-) . I'd attribute it to an identity of sorts (if it was
> to go all out with comparison chaining).  Yes it makes little sense, in
> practice, but the truth of it would be the same.
> 
> I realize that my comment, and question were going a bit off-topic with
> going on about the chaining of comparisons, but I'm interested in it
> more because I wonder which would be harder to implement.  It would seem
> to me that it would be harder to implement a means in which you only
> allow one additional layer of comparison, rather than abstract chaining
> of comparisons (<, <=, >, >=).  As Andrea noted, python does allow for
> arbitrary chains of comparison.  I'm not exactly sure how precedence
> would work with comparisons, rather, as I see it the comparison would
> need to be expanded such
> ```
> if (0 < $a < $b > 10)
> ```
> would be akin to
> ```
> if (0 < $a && $a < $b && $b > 10)
> ```
> 
> I, of course, am only projecting a potential syntax, and I'm no yacc/ast
> aficionado by any metric, but it'd seem to me that it'd be possible to
> convert some stack of elements here into a recursive comparison.
> 

This requires associativity, as Python has it.

https://docs.python.org/3/reference/expressions.html#comparisons

The problem, as explained in the Python reference, is that in `x < y <
z` the variables `x` and `z` are never compared against each other.
This, however, works great almost always and nothing is wrong in doing
so in my opinion.

That change would actually be brutally easy since we only need to change
the `%nonassoc` to `%left` and we are done.

https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L73

-- 
Richard "Fleshgrinder" Fussenegger

---

## #96779 — David Walker — 2016-11-08T21:57:10+00:00

URL: https://externals.io/message/96746#96779 · In reply to: #96778

> This requires associativity, as Python has it.
>
> https://docs.python.org/3/reference/expressions.html#comparisons
>
> The problem, as explained in the Python reference, is that in `x < y <
> z` the variables `x` and `z` are never compared against each other.
> This, however, works great almost always and nothing is wrong in doing
> so in my opinion.
>
> That change would actually be brutally easy since we only need to change
> the `%nonassoc` to `%left` and we are done.
>
> https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L73


I don't think that alone allows the chaining of comparisons.  I'd have to
look closer, but it'd seem to me that zend_ast_create_binary_op
(ZEND_AST_BINARY_OP) evaluation might need to be amended as well.  Seems it
eventually calls a `op(zval*,zval*,zval*) so the evaluated end of: one op
would be a true/false, when comparing against another boolean op.

Regardless of implementation, the core question you raised was should
something like it exist, I'm still a strong yes on that front.  I'd just
promote introducing the left precedence and allowing the arbitrary chains
of comparison, rather than just a single interval comparison.

cheers
--
Dave

---

## #96794 — Fleshgrinder — 2016-11-09T19:47:59+00:00

URL: https://externals.io/message/96746#96794 · In reply to: #96779

> I don't think that alone allows the chaining of comparisons.  I'd have to
> look closer, but it'd seem to me that zend_ast_create_binary_op
> (ZEND_AST_BINARY_OP) evaluation might need to be amended as well.  Seems it
> eventually calls a `op(zval*,zval*,zval*) so the evaluated end of: one op
> would be a true/false, when comparing against another boolean op.
> 
> Regardless of implementation, the core question you raised was should
> something like it exist, I'm still a strong yes on that front.  I'd just
> promote introducing the left precedence and allowing the arbitrary chains
> of comparison, rather than just a single interval comparison.
> 
> cheers
> --
> Dave
> 

Yeah maybe not that easy but also not too complicated if we add
associativity to the mix. I am open for both variations and could write
up an RFC with a vote for both if desired.

Not sure if I find the time to look into actual implementation but I
most certainly can try.

-- 
Richard "Fleshgrinder" Fussenegger

---

## #96799 — Andrea Faulds — 2016-11-09T21:45:33+00:00

URL: https://externals.io/message/96746#96799 · In reply to: #96778

Hi,

Fleshgrinder wrote:
> That change would actually be brutally easy since we only need to change
> the `%nonassoc` to `%left` and we are done.

Not quite. We'd still need to parse and compile these expressions 
correctly. If we just add associativity, then we end up with Java's 
behaviour.

-- 
Andrea Faulds
https://ajf.me/

---

## #96835 — David Walker — 2016-11-11T17:03:45+00:00

URL: https://externals.io/message/96746#96835 · In reply to: #96794

> 
> > […]
> it
> > […]
>
> Yeah maybe not that easy but also not too complicated if we add
> associativity to the mix. I am open for both variations and could write
> up an RFC with a vote for both if desired.
>
> Not sure if I find the time to look into actual implementation but I
> most certainly can try.
>

I took a quick stab at implementing, and had something working for constant
expressions, but handling something akin to:

$a = 2;
if (1 < $a++ < 3) {
...
}

Is a bit awkward in our expansions of : if (1 < $a++ && $a++ < 3).  Seems
as if when processing the chain here, you'd need to see if the left node
has a child, and somehow ensure you get the evaluated value somehow, to
override the "left" node.  So logically expansion of the above would be  if
(1 < $a++ && 3 < 3).  I think the same would have too somehow handle
(either by syntax error or something) that if a non-numeric value creeps
into a binary-op-compare we error like:  if (1 < (2==3) < 3).

Just some food for thought
--
Dave

---

## #96836 — Fleshgrinder — 2016-11-11T18:25:01+00:00

URL: https://externals.io/message/96746#96836 · In reply to: #96835

> I took a quick stab at implementing, and had something working for constant
> expressions, but handling something akin to:
> 
> $a = 2;
> if (1 < $a++ < 3) {
> ...
> }
> 
> Is a bit awkward in our expansions of : if (1 < $a++ && $a++ < 3).  Seems
> as if when processing the chain here, you'd need to see if the left node
> has a child, and somehow ensure you get the evaluated value somehow, to
> override the "left" node.  So logically expansion of the above would be  if
> (1 < $a++ && 3 < 3).  I think the same would have too somehow handle
> (either by syntax error or something) that if a non-numeric value creeps
> into a binary-op-compare we error like:  if (1 < (2==3) < 3).
> 
> Just some food for thought
> --
> Dave
> 

Nice :)

Personally I would make all of these situations parse errors. These are
hard to read and understand code constructs, why would we want to
support them in the first place?

-- 
Richard "Fleshgrinder" Fussenegger

---

## #96840 — lauri.kentta@gmail.com — 2016-11-12T10:08:08+00:00

URL: https://externals.io/message/96746#96840 · In reply to: #96835

> 
> I took a quick stab at implementing, and had something working for
> constant expressions, but handling something akin to:
> 
> $a = 2;
> if (1 < $a++ < 3) {
> ...
> }
> 
> Is a bit awkward in our expansions of : if (1 < $a++ && $a++ < 3).
> Seems as if when processing the chain here, you'd need to see if the
> left node has a child, and somehow ensure you get the evaluated value
> somehow, to override the "left" node.  So logically expansion of the
> above would be  if (1 < $a++ && 3 < 3).  I think the same would have
> too somehow handle (either by syntax error or something) that if a
> non-numeric value creeps into a binary-op-compare we error like:  if
> (1 < (2==3) < 3).
> 
> Just some food for thought
> --
> Dave

I don't see how you would ”logically” get 3 < 3. The very point of 
chaining is that each expression is evaluated only once, so the 
expression will have the same value in both of the comparisons. So if 
the first part is 1<2, then the other must be 2<3 (and not 3<3).

An expression like a < b < c < d can be currently implemented with 
temporary variables like this:
a < ($tmp1 = b) && $tmp1 < ($tmp2 = c) && $tmp2 < d

-- 
Lauri Kenttä

---

## #96857 — David Walker — 2016-11-12T16:21:32+00:00

URL: https://externals.io/message/96746#96857 · In reply to: #96840

> 
> > […]
>
> I don't see how you would ”logically” get 3 < 3. The very point of
> chaining is that each expression is evaluated only once, so the
> expression will have the same value in both of the comparisons. So if
> the first part is 1<2, then the other must be 2<3 (and not 3<3).
>
> An expression like a < b < c < d can be currently implemented with
> temporary variables like this:
> a < ($tmp1 = b) && $tmp1 < ($tmp2 = c) && $tmp2 < d
>

That's was the intent in the question I tried to raise with the
post-increment, as you write `$tmp1 = b`, to my example `$tmp1 = $a++`:

Should
$a = 1;
var_dump(1 < $a++ < 3);

(expanded into numbers) be evaluated as:
1 < 2 && 2 < 3 - True
or
1 < 2 && 3 < 3 - False

I could see the case for either, although, I would probably prefer the
latter, as that's what's apparent today, example with

```
$a = 0;
var_dump($a < 1);
var_dump(++$a < 1);
```

Would print out true, false.

Sorry I wasn't overly clear in my statement.
--
Dave

---

## #96858 — Kris Craig — 2016-11-12T16:27:43+00:00

URL: https://externals.io/message/96746#96858 · In reply to: #96857

>
> On Sat, Nov 12, 2016 at 3:08 AM Lauri Kenttä <lauri.kentta@gmail.com>
wrote:
>
> > […]
>
> That's was the intent in the question I tried to raise with the
> post-increment, as you write `$tmp1 = b`, to my example `$tmp1 = $a++`:
>
> Should
> $a = 1;
> var_dump(1 < $a++ < 3);
>
> (expanded into numbers) be evaluated as:
> 1 < 2 && 2 < 3 - True
> or
> 1 < 2 && 3 < 3 - False
>
> I could see the case for either, although, I would probably prefer the
> latter, as that's what's apparent today, example with
>
> ```
> $a = 0;
> var_dump($a < 1);
> var_dump(++$a < 1);
> ```
>
> Would print out true, false.
>
> Sorry I wasn't overly clear in my statement.
> --
> Dave

+1 on the usefulness of this idea.  I've always wondered why more parsers
don't already support this.

--Kris

---

## #96859 — Christoph M. Becker — 2016-11-12T16:47:59+00:00

URL: https://externals.io/message/96746#96859 · In reply to: #96857

> Should
> $a = 1;
> var_dump(1 < $a++ < 3);
> 
> (expanded into numbers) be evaluated as:
> 1 < 2 && 2 < 3 - True
> or
> 1 < 2 && 3 < 3 - False

In my opinion, that should evaluate to

  1 < 1 // false

because we have a post-increment operator, and it seems to me that the
expansion defined for Python[1] makes most sense:

> Comparisons can be chained arbitrarily, e.g., x < y <= z is 
> equivalent to x < y and y <= z, except that y is evaluated only once
> (but in both cases z is not evaluated at all when x < y is found to
> be false).

[1] <https://docs.python.org/2/reference/expressions.html#not-in>

-- 
Christoph M. Becker

---

## #96860 — David Walker — 2016-11-12T16:56:13+00:00

URL: https://externals.io/message/96746#96860 · In reply to: #96859

On Sat, Nov 12, 2016 at 9:47 AM Christoph M. Becker <cmbecker69@gmx.de>
wrote:

> 
>
> > […]
>
> In my opinion, that should evaluate to
>
>   1 < 1 // false
>
> because we have a post-increment operator, and it seems to me that the
> expansion defined for Python[1] makes most sense:
>

Double my oops, I meant a pre-increment.


>
> > […]
>
> [1] <https://docs.python.org/2/reference/expressions.html#not-in>
>

I concur with this, that each term should only be evaluated once, in more
what Lauri defined:
a < ($tmp1 = b) && $tmp1 < ($tmp2 = c) && $tmp2 < d

Which would ensure that each position is evaluated once, in a left->right
fashion.  And I doubly concur that evaluation is short circuited (per my
wrong example, you corrected):

$a = 0
1 < $a++ < $a++ < 3   ~~ 1 < 1 // false. $a would be 1 after this line.
--
Dave
