Skip to content

[RFC] Approximately equals operator #18214

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed

Conversation

nielsdos
Copy link
Member

@nielsdos nielsdos commented Mar 31, 2025

Below is a copy to my email to internals: https://externals.io/message/126989


I'm excited to share what I've been working on!
I had an epiphany. I realized what we truly need to revolutionize PHP: a new operator.

Hear me out.
We live in an imperfect world, and we often approximate data, but neither == nor === are ideal comparison operators to deal with these kinds of data.

Introducing: the "approximately equal" (or "approx-equal") operator ~= (to immitate the maths symbol ≃).
This combines the power of type coercion with approximating equality.
Who cares if things are actually equal, close enough amirite?

First of all, if $a == $b holds, then $a ~= $b obviously.
The true power lies where the data is not exactly the same, but "close enough"!

Here are some examples:

We all had situations where we wanted to compare two floating point numbers and it turns out that due to the non-exact representation, seemingly-equal numbers don't match! Gone are those days because the ~= operator nicely rounds the numbers for you before comparing them.
This also means that the "Fundamental Theorem of Engineering" now holds!
i.e. 2.7 ~= 3 and 3.14 ~= 3. Of course also 2.7 ~= 3.14. But this is false obviously: 2 ~= 1.

Ever had trouble with users mistyping something? Say no more!
"This is a tpyo" ~= "This is a typo". It's typo-resistant!
However, if the strings are too different, then they're not approx-equal.
For example: "vanilla" ~= "strawberry" gives false.
How does this work?

  • The strings are equal if their levenshtein ratio is <= 50%, so it's adaptive to the length.
  • If the ratio is > 50%, then the shortest string comes first in the comparison, such that if we ever get a ~< operator, then "vanilla" ~< "strawberry".

There is of course a PoC implementation available at: [GitHub]
You can see more examples on GitHub in the tests, here is a copy:

// Number compares
var_dump(2 ~= 1); // false
var_dump(1.4 ~= 1); // true
var_dump(-1.4 ~= -1); // true
var_dump(-1.5 ~= -1.8); // true
var_dump(random_int(1, 1) ~= 1.1); // true

// Array compares (just compares the lengths)
var_dump([1, 2, 3] ~= [2, 3, 4]); // true
var_dump([1, 2, 3] ~= [2, 3, 4, 5]); // false

// String / string compares
var_dump("This is a tpyo" ~= "This is a typo"); // true: No more typos
var_dump("something" ~= "different"); // false: clearly completely different
var_dump("Wtf bro" ~= "Wtf sis"); // true: Abolish concept of gender

// String / different type compares
var_dump(-1.5 ~= "-1.a"); // true
var_dump(-1.5 ~= "-1.aaaaaaa"); // false
var_dump(NULL ~= "blablabla"); // false

Note that this does not support all possible Opcache optimizations yet, nor does it support the JIT yet.
However, there are no real blockers to add support for that.

I look forward to hearing you!

Have a nice first day of the month ;)

@linaori
Copy link

linaori commented Apr 1, 2025

You had me in the first half, not gonna lie

@imiroslavov
Copy link

Thanks for this draft. I didn't had a quality laugh for a long time.

var_dump(1.4 ~= 1); // true
var_dump(-1.4 ~= -1); // true
var_dump(-1.5 ~= -1.8); // true
var_dump(random_int(1, 1) ~= 1.1); // true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a test for the common floating point example to make sure it is correctly handled:

Suggested change
var_dump(random_int(1, 1) ~= 1.1); // true
var_dump(random_int(1, 1) ~= 1.1); // true
var_dump(0.1 + 0.2 ~= 0.3); // true

Comment on lines +27 to +28
// AST dump test
assert(function() { return 1 ~= 2; } && false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// AST dump test
assert(function() { return 1 ~= 2; } && false);
try {
// AST dump test
assert(function() { return 1 ~= 2; } && false);
} catch (AssertionError $e) {
echo $e->getMessage(), PHP_EOL;
}

The stacktrace is just noise.

@nielsdos
Copy link
Member Author

nielsdos commented Apr 1, 2025

See y'all next year when I finally get rid of type checking, TypeErrors are so yesterday!

@nielsdos nielsdos closed this Apr 1, 2025
@imiroslavov
Copy link

imiroslavov commented Apr 1, 2025

@nielsdos Shhhhh... I want UnexplainableInterface for the next year. :D Cheers!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants